diff --git a/BUILD.gn b/BUILD.gn index 3e4ab112506..efbd0f585b5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -166,15 +166,6 @@ npm_action("build_electron_definitions") { outputs = [ "$target_gen_dir/tsc/typings/electron.d.ts" ] } -webpack_build("electron_asar_bundle") { - deps = [ ":build_electron_definitions" ] - - inputs = auto_filenames.asar_bundle_deps - - config_file = "//electron/build/webpack/webpack.config.asar.js" - out_file = "$target_gen_dir/js2c/asar_bundle.js" -} - webpack_build("electron_browser_bundle") { deps = [ ":build_electron_definitions" ] @@ -220,6 +211,15 @@ webpack_build("electron_isolated_renderer_bundle") { out_file = "$target_gen_dir/js2c/isolated_bundle.js" } +webpack_build("electron_node_bundle") { + deps = [ ":build_electron_definitions" ] + + inputs = auto_filenames.node_bundle_deps + + config_file = "//electron/build/webpack/webpack.config.node.js" + out_file = "$target_gen_dir/js2c/node_init.js" +} + webpack_build("electron_utility_bundle") { deps = [ ":build_electron_definitions" ] @@ -231,9 +231,9 @@ webpack_build("electron_utility_bundle") { action("electron_js2c") { deps = [ - ":electron_asar_bundle", ":electron_browser_bundle", ":electron_isolated_renderer_bundle", + ":electron_node_bundle", ":electron_renderer_bundle", ":electron_sandboxed_renderer_bundle", ":electron_utility_bundle", @@ -242,9 +242,9 @@ action("electron_js2c") { ] sources = [ - "$target_gen_dir/js2c/asar_bundle.js", "$target_gen_dir/js2c/browser_init.js", "$target_gen_dir/js2c/isolated_bundle.js", + "$target_gen_dir/js2c/node_init.js", "$target_gen_dir/js2c/renderer_init.js", "$target_gen_dir/js2c/sandbox_bundle.js", "$target_gen_dir/js2c/utility_init.js", diff --git a/build/webpack/webpack.config.asar.js b/build/webpack/webpack.config.asar.js deleted file mode 100644 index 83443f467ca..00000000000 --- a/build/webpack/webpack.config.asar.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = require('./webpack.config.base')({ - target: 'asar', - alwaysHasNode: true, - targetDeletesNodeGlobals: true -}); diff --git a/build/webpack/webpack.config.node.js b/build/webpack/webpack.config.node.js new file mode 100644 index 00000000000..875ec4bacb2 --- /dev/null +++ b/build/webpack/webpack.config.node.js @@ -0,0 +1,4 @@ +module.exports = require('./webpack.config.base')({ + target: 'node', + alwaysHasNode: true +}); diff --git a/filenames.auto.gni b/filenames.auto.gni index b972dc9f4e3..bbf71f31cc0 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -336,10 +336,9 @@ auto_filenames = { "typings/internal-electron.d.ts", ] - asar_bundle_deps = [ - "lib/asar/fs-wrapper.ts", - "lib/asar/init.ts", - "lib/common/webpack-provider.ts", + node_bundle_deps = [ + "lib/node/asar-fs-wrapper.ts", + "lib/node/init.ts", "package.json", "tsconfig.electron.json", "tsconfig.json", diff --git a/lib/asar/init.ts b/lib/asar/init.ts deleted file mode 100644 index 71ad738e9f9..00000000000 --- a/lib/asar/init.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { wrapFsWithAsar } from './fs-wrapper'; - -wrapFsWithAsar(require('fs')); diff --git a/lib/asar/fs-wrapper.ts b/lib/node/asar-fs-wrapper.ts similarity index 100% rename from lib/asar/fs-wrapper.ts rename to lib/node/asar-fs-wrapper.ts diff --git a/lib/node/init.ts b/lib/node/init.ts new file mode 100644 index 00000000000..19bd28eccfa --- /dev/null +++ b/lib/node/init.ts @@ -0,0 +1,31 @@ +// Initialize ASAR support in fs module. +import { wrapFsWithAsar } from './asar-fs-wrapper'; +wrapFsWithAsar(require('fs')); + +// Hook child_process.fork. +const cp = require('child_process'); +const originalFork = cp.fork; +cp.fork = (modulePath: string, args: any, options: any) => { + // Parse optional args. + if (args == null) { + args = []; + } else if (typeof args === 'object' && !Array.isArray(args)) { + options = args; + args = []; + } + // Fallback to original fork to report arg type errors. + if (typeof modulePath !== 'string' || !Array.isArray(args) || + (typeof options !== 'object' && typeof options !== 'undefined')) { + return originalFork(modulePath, args, options); + } + // When forking a child script, we setup a special environment to make + // the electron binary run like upstream Node.js. + options = options ?? {}; + options.env = Object.create(options.env || process.env); + options.env.ELECTRON_RUN_AS_NODE = 1; + // On mac the child script runs in helper executable. + if (!options.execPath && process.platform === 'darwin') { + options.execPath = process.helperExecPath; + } + return originalFork(modulePath, args, options); +}; diff --git a/patches/node/.patches b/patches/node/.patches index 48c817bb392..7709224dce2 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -1,5 +1,3 @@ -refactor_alter_child_process_fork_to_use_execute_script_with.patch -feat_initialize_asar_support.patch expose_get_builtin_module_function.patch build_add_gn_build_files.patch fix_add_default_values_for_variables_in_common_gypi.patch @@ -46,3 +44,4 @@ build_do_not_rely_on_gn_helpers_in_gn_build.patch test_make_test-node-output-v8-warning_generic.patch test_match_wpt_streams_transferable_transform-stream-members_any_js.patch build_ensure_v8_pointer_compression_sandbox_is_enabled_on_64bit.patch +src_preload_function_for_environment.patch diff --git a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch index da850edb32d..4a342ab026d 100644 --- a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch +++ b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch @@ -26,10 +26,10 @@ index 1f3b719048f2477de183e2856b9b8eee8502f708..21116088c101f4679b5a5f41762ce710 try { resolvedArgv = Module._resolveFilename(process.argv[1], null, false); diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js -index fc8f61ee6d30cf18951ec7a5eb5f09a9583a85ae..61858f6bdcdbc231d7e2327e42732ad928d47ac7 100644 +index 9142fed75e9050fcc17c01208e82f1bc57923fcd..157a85623c7eb5338baa77aba5dc448a4614ded0 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js -@@ -238,12 +238,14 @@ function patchProcessObject(expandArgv1) { +@@ -232,12 +232,14 @@ function patchProcessObject(expandArgv1) { if (expandArgv1 && process.argv[1] && !StringPrototypeStartsWith(process.argv[1], '-')) { // Expand process.argv[1] into a full path. diff --git a/patches/node/enable_crashpad_linux_node_processes.patch b/patches/node/enable_crashpad_linux_node_processes.patch index 16276e457b4..45a332c46f9 100644 --- a/patches/node/enable_crashpad_linux_node_processes.patch +++ b/patches/node/enable_crashpad_linux_node_processes.patch @@ -8,7 +8,7 @@ to child processes spawned with `ELECTRON_RUN_AS_NODE` which is used by the crashpad client to connect with the handler process. diff --git a/lib/child_process.js b/lib/child_process.js -index 9dd33ecbac3a5d516f9bff76fdbe1a8aece531f2..0464ecc7b53389cdff97a7fe4cb01582e8b72dbb 100644 +index 449013906e93e59568a90264d5372a3962db6cb0..168163001f13b641bc284fd01a71f075e1ada94f 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -61,6 +61,7 @@ let debug = require('internal/util/debuglog').debuglog( @@ -19,7 +19,7 @@ index 9dd33ecbac3a5d516f9bff76fdbe1a8aece531f2..0464ecc7b53389cdff97a7fe4cb01582 const { AbortError, -@@ -162,7 +163,6 @@ function fork(modulePath, args = [], options) { +@@ -154,7 +155,6 @@ function fork(modulePath, args = [], options) { ArrayPrototypeSplice(execArgv, index - 1, 2); } } @@ -27,7 +27,7 @@ index 9dd33ecbac3a5d516f9bff76fdbe1a8aece531f2..0464ecc7b53389cdff97a7fe4cb01582 args = [...execArgv, modulePath, ...args]; if (typeof options.stdio === 'string') { -@@ -625,6 +625,21 @@ function normalizeSpawnArguments(file, args, options) { +@@ -617,6 +617,21 @@ function normalizeSpawnArguments(file, args, options) { 'options.windowsVerbatimArguments'); } diff --git a/patches/node/feat_initialize_asar_support.patch b/patches/node/feat_initialize_asar_support.patch deleted file mode 100644 index 33ae71f3611..00000000000 --- a/patches/node/feat_initialize_asar_support.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shelley Vohr -Date: Thu, 13 Sep 2018 08:56:07 -0700 -Subject: feat: initialize asar support - -This patch initializes asar support in Node.js. - -diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js -index 9142fed75e9050fcc17c01208e82f1bc57923fcd..fc8f61ee6d30cf18951ec7a5eb5f09a9583a85ae 100644 ---- a/lib/internal/process/pre_execution.js -+++ b/lib/internal/process/pre_execution.js -@@ -87,6 +87,7 @@ function prepareShadowRealmExecution() { - }); - } - -+let processLinkedBinding = process._linkedBinding; - function prepareExecution(options) { - const { expandArgv1, initializeModules, isMainThread } = options; - -@@ -193,12 +194,17 @@ function setupUserModules(forceDefaultLoader = false) { - } - // Need to be done after --require setup. - initializeFrozenIntrinsics(); -+ setupAsarSupport(); - } - - function refreshRuntimeOptions() { - refreshOptions(); - } - -+function setupAsarSupport() { -+ processLinkedBinding('electron_common_asar').initAsarSupport(require); -+} -+ - /** - * Patch the process object with legacy properties and normalizations. - * Replace `process.argv[0]` with `process.execPath`, preserving the original `argv[0]` value as `process.argv0`. diff --git a/patches/node/refactor_alter_child_process_fork_to_use_execute_script_with.patch b/patches/node/refactor_alter_child_process_fork_to_use_execute_script_with.patch deleted file mode 100644 index ea4b6034079..00000000000 --- a/patches/node/refactor_alter_child_process_fork_to_use_execute_script_with.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shelley Vohr -Date: Mon, 30 Jul 2018 10:30:35 -0700 -Subject: refactor: alter child_process.fork to use execute script with - Electron - -When forking a child script, we setup a special environment to make the Electron binary run like the upstream node. On Mac, we use the helper app as node binary. - -diff --git a/lib/child_process.js b/lib/child_process.js -index 449013906e93e59568a90264d5372a3962db6cb0..9dd33ecbac3a5d516f9bff76fdbe1a8aece531f2 100644 ---- a/lib/child_process.js -+++ b/lib/child_process.js -@@ -139,6 +139,14 @@ function fork(modulePath, args = [], options) { - validateObject(options, 'options'); - } - options = { __proto__: null, ...options, shell: false }; -+ // When forking a child script, we setup a special environment to make -+ // the electron binary run like upstream Node.js -+ options.env = Object.create(options.env || process.env) -+ options.env.ELECTRON_RUN_AS_NODE = 1; -+ -+ if (!options.execPath && process.type && process.platform == 'darwin') { -+ options.execPath = process.helperExecPath; -+ } - options.execPath = options.execPath || process.execPath; - validateArgumentNullCheck(options.execPath, 'options.execPath'); - diff --git a/patches/node/src_preload_function_for_environment.patch b/patches/node/src_preload_function_for_environment.patch new file mode 100644 index 00000000000..5679e1572cc --- /dev/null +++ b/patches/node/src_preload_function_for_environment.patch @@ -0,0 +1,299 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Mon, 22 Jan 2024 13:45:55 +0900 +Subject: src: preload function for Environment + +https://github.com/nodejs/node/pull/51539 + +This PR adds a |preload| arg to the node::CreateEnvironment to allow +embedders to set a preload function for the environment, which will run +after the environment is loaded and before the main script runs. + +This is similiar to the --require CLI option, but runs a C++ function, +and can only be set by embedders. + +The preload function can be used by embedders to inject scripts before +running the main script, for example: +1. In Electron it is used to initialize the ASAR virtual filesystem, + inject custom process properties, etc. +2. In VS Code it can be used to reset the module search paths for + extensions. + +diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js +index 157a85623c7eb5338baa77aba5dc448a4614ded0..701f91f1c9603c1da03e0fe6d20c8627c8d644fb 100644 +--- a/lib/internal/process/pre_execution.js ++++ b/lib/internal/process/pre_execution.js +@@ -185,6 +185,9 @@ function setupUserModules(forceDefaultLoader = false) { + initializeESMLoader(forceDefaultLoader); + const CJSLoader = require('internal/modules/cjs/loader'); + assert(!CJSLoader.hasLoadedAnyUserCJSModule); ++ if (getEmbedderOptions().hasEmbedderPreload) { ++ runEmbedderPreload(); ++ } + // Do not enable preload modules if custom loaders are disabled. + // For example, loader workers are responsible for doing this themselves. + // And preload modules are not supported in ShadowRealm as well. +@@ -742,6 +745,10 @@ function initializeFrozenIntrinsics() { + } + } + ++function runEmbedderPreload() { ++ internalBinding('mksnapshot').runEmbedderPreload(process, require); ++} ++ + function loadPreloadModules() { + // For user code, we preload modules if `-r` is passed + const preloadModules = getOptionValue('--require'); +diff --git a/src/api/environment.cc b/src/api/environment.cc +index 9045de3b17c93c4864a1bb1024b08f7d1ffa83be..7c580e1ce1af66e010083240aaf8b0037dd41f2e 100644 +--- a/src/api/environment.cc ++++ b/src/api/environment.cc +@@ -442,7 +442,8 @@ Environment* CreateEnvironment( + const std::vector& exec_args, + EnvironmentFlags::Flags flags, + ThreadId thread_id, +- std::unique_ptr inspector_parent_handle) { ++ std::unique_ptr inspector_parent_handle, ++ EmbedderPreloadCallback preload) { + Isolate* isolate = isolate_data->isolate(); + + Isolate::Scope isolate_scope(isolate); +@@ -463,7 +464,8 @@ Environment* CreateEnvironment( + exec_args, + env_snapshot_info, + flags, +- thread_id); ++ thread_id, ++ std::move(preload)); + CHECK_NOT_NULL(env); + + if (use_snapshot) { +diff --git a/src/env-inl.h b/src/env-inl.h +index 564de2990c09a54693686666f9ad66398ff76ab5..b10bc2396539b011dec6f09719251bfc842072af 100644 +--- a/src/env-inl.h ++++ b/src/env-inl.h +@@ -438,6 +438,10 @@ inline void Environment::set_embedder_entry_point(StartExecutionCallback&& fn) { + embedder_entry_point_ = std::move(fn); + } + ++inline const EmbedderPreloadCallback& Environment::embedder_preload() const { ++ return embedder_preload_; ++} ++ + inline double Environment::new_async_id() { + async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter] += 1; + return async_hooks()->async_id_fields()[AsyncHooks::kAsyncIdCounter]; +diff --git a/src/env.cc b/src/env.cc +index ba575a04340b91709fb6c8710ab160a4ca1f8b77..76db0ac4ef72b902a7567a96cfd751ff879117b7 100644 +--- a/src/env.cc ++++ b/src/env.cc +@@ -767,7 +767,8 @@ Environment::Environment(IsolateData* isolate_data, + const std::vector& exec_args, + const EnvSerializeInfo* env_info, + EnvironmentFlags::Flags flags, +- ThreadId thread_id) ++ ThreadId thread_id, ++ EmbedderPreloadCallback preload) + : isolate_(isolate), + isolate_data_(isolate_data), + async_hooks_(isolate, MAYBE_FIELD_PTR(env_info, async_hooks)), +@@ -793,7 +794,8 @@ Environment::Environment(IsolateData* isolate_data, + flags_(flags), + thread_id_(thread_id.id == static_cast(-1) + ? AllocateEnvironmentThreadId().id +- : thread_id.id) { ++ : thread_id.id), ++ embedder_preload_(std::move(preload)) { + constexpr bool is_shared_ro_heap = + #ifdef NODE_V8_SHARED_RO_HEAP + true; +diff --git a/src/env.h b/src/env.h +index 448075e354c760a2dbd1dd763f40b7a645730250..a5aad9596953536b0a1f741dfbc4f21f6a961404 100644 +--- a/src/env.h ++++ b/src/env.h +@@ -635,7 +635,8 @@ class Environment : public MemoryRetainer { + const std::vector& exec_args, + const EnvSerializeInfo* env_info, + EnvironmentFlags::Flags flags, +- ThreadId thread_id); ++ ThreadId thread_id, ++ EmbedderPreloadCallback preload); + void InitializeMainContext(v8::Local context, + const EnvSerializeInfo* env_info); + ~Environment() override; +@@ -986,6 +987,8 @@ class Environment : public MemoryRetainer { + inline const StartExecutionCallback& embedder_entry_point() const; + inline void set_embedder_entry_point(StartExecutionCallback&& fn); + ++ inline const EmbedderPreloadCallback& embedder_preload() const; ++ + inline void set_process_exit_handler( + std::function&& handler); + +@@ -1186,6 +1189,7 @@ class Environment : public MemoryRetainer { + + builtins::BuiltinLoader builtin_loader_; + StartExecutionCallback embedder_entry_point_; ++ EmbedderPreloadCallback embedder_preload_; + + // Used by allocate_managed_buffer() and release_managed_buffer() to keep + // track of the BackingStore for a given pointer. +diff --git a/src/node.h b/src/node.h +index 36da93a7b41ea450a5f288ec17b61adae46ae178..09e044e86bab2cef42c86dbfc9bbcc743daf564d 100644 +--- a/src/node.h ++++ b/src/node.h +@@ -678,11 +678,23 @@ struct InspectorParentHandle { + virtual ~InspectorParentHandle() = default; + }; + ++using EmbedderPreloadCallback = ++ std::function process, ++ v8::Local require)>; ++ + // TODO(addaleax): Maybe move per-Environment options parsing here. + // Returns nullptr when the Environment cannot be created e.g. there are + // pending JavaScript exceptions. + // `context` may be empty if an `EmbedderSnapshotData` instance was provided + // to `NewIsolate()` and `CreateIsolateData()`. ++// ++// The |preload| function will run before executing the entry point, which ++// is usually used by embedders to inject scripts. The function is executed ++// with preload(process, require), and the passed require function has access ++// to internal Node.js modules. The |preload| function is inherited by worker ++// threads and thus will run in work threads, so make sure the function is ++// thread-safe. + NODE_EXTERN Environment* CreateEnvironment( + IsolateData* isolate_data, + v8::Local context, +@@ -690,7 +702,8 @@ NODE_EXTERN Environment* CreateEnvironment( + const std::vector& exec_args, + EnvironmentFlags::Flags flags = EnvironmentFlags::kDefaultFlags, + ThreadId thread_id = {} /* allocates a thread id automatically */, +- std::unique_ptr inspector_parent_handle = {}); ++ std::unique_ptr inspector_parent_handle = {}, ++ EmbedderPreloadCallback preload = nullptr); + + // Returns a handle that can be passed to `LoadEnvironment()`, making the + // child Environment accessible to the inspector as if it were a Node.js Worker. +diff --git a/src/node_options.cc b/src/node_options.cc +index 48ce3f3b68a94fc35e5ce93a385ddbebb03741b9..39d34e18e483882a71145110962109711a1566e2 100644 +--- a/src/node_options.cc ++++ b/src/node_options.cc +@@ -1290,6 +1290,12 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { + .IsNothing()) + return; + ++ if (ret->Set(context, ++ FIXED_ONE_BYTE_STRING(env->isolate(), "hasEmbedderPreload"), ++ Boolean::New(isolate, env->embedder_preload() != nullptr)) ++ .IsNothing()) ++ return; ++ + args.GetReturnValue().Set(ret); + } + +diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc +index 562a47ddcc9c8e61590b7b09d84dc08ab4b3653d..325bebc1df9ad2e8b0bad468951cf1563ecefc14 100644 +--- a/src/node_snapshotable.cc ++++ b/src/node_snapshotable.cc +@@ -1369,6 +1369,13 @@ static void RunEmbedderEntryPoint(const FunctionCallbackInfo& args) { + } + } + ++static void RunEmbedderPreload(const FunctionCallbackInfo& args) { ++ Environment* env = Environment::GetCurrent(args); ++ CHECK(env->embedder_preload()); ++ CHECK_EQ(args.Length(), 2); ++ env->embedder_preload()(env, args[0], args[1]); ++} ++ + void CompileSerializeMain(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsString()); + Local filename = args[0].As(); +@@ -1493,6 +1500,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data, + Local target) { + Isolate* isolate = isolate_data->isolate(); + SetMethod(isolate, target, "runEmbedderEntryPoint", RunEmbedderEntryPoint); ++ SetMethod(isolate, target, "runEmbedderPreload", RunEmbedderPreload); + SetMethod(isolate, target, "compileSerializeMain", CompileSerializeMain); + SetMethod(isolate, target, "setSerializeCallback", SetSerializeCallback); + SetMethod(isolate, target, "setDeserializeCallback", SetDeserializeCallback); +@@ -1506,6 +1514,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data, + + void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(RunEmbedderEntryPoint); ++ registry->Register(RunEmbedderPreload); + registry->Register(CompileSerializeMain); + registry->Register(SetSerializeCallback); + registry->Register(SetDeserializeCallback); +diff --git a/src/node_worker.cc b/src/node_worker.cc +index 900674bbe4c90e9aeb2013c06c9979864b06dcd5..2a22d986585e93ea00c6dcdca1f7b783ef0723f8 100644 +--- a/src/node_worker.cc ++++ b/src/node_worker.cc +@@ -63,6 +63,7 @@ Worker::Worker(Environment* env, + thread_id_(AllocateEnvironmentThreadId()), + name_(name), + env_vars_(env_vars), ++ embedder_preload_(env->embedder_preload()), + snapshot_data_(snapshot_data) { + Debug(this, "Creating new worker instance with thread id %llu", + thread_id_.id); +@@ -360,7 +361,8 @@ void Worker::Run() { + std::move(exec_argv_), + static_cast(environment_flags_), + thread_id_, +- std::move(inspector_parent_handle_))); ++ std::move(inspector_parent_handle_), ++ std::move(embedder_preload_))); + if (is_stopped()) return; + CHECK_NOT_NULL(env_); + env_->set_env_vars(std::move(env_vars_)); +diff --git a/src/node_worker.h b/src/node_worker.h +index 531e2b5287010f9206ab4fd7f4dd0f3dec9fe55c..07fd7b460654e169e8b6822474dc3cc70fcec4c0 100644 +--- a/src/node_worker.h ++++ b/src/node_worker.h +@@ -114,6 +114,7 @@ class Worker : public AsyncWrap { + + std::unique_ptr child_port_data_; + std::shared_ptr env_vars_; ++ EmbedderPreloadCallback embedder_preload_; + + // A raw flag that is used by creator and worker threads to + // sync up on pre-mature termination of worker - while in the +diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc +index 2e747c7be58922897abd0424b797f3f12a89ada1..658f8df4b01d60759e858cf5283b9be9467dd142 100644 +--- a/test/cctest/test_environment.cc ++++ b/test/cctest/test_environment.cc +@@ -773,3 +773,31 @@ TEST_F(EnvironmentTest, RequestInterruptAtExit) { + + context->Exit(); + } ++ ++TEST_F(EnvironmentTest, EmbedderPreload) { ++ v8::HandleScope handle_scope(isolate_); ++ v8::Local context = node::NewContext(isolate_); ++ v8::Context::Scope context_scope(context); ++ ++ node::EmbedderPreloadCallback preload = [](node::Environment* env, ++ v8::Local process, ++ v8::Local require) { ++ CHECK(process->IsObject()); ++ CHECK(require->IsFunction()); ++ process.As()->Set( ++ env->context(), ++ v8::String::NewFromUtf8Literal(env->isolate(), "prop"), ++ v8::String::NewFromUtf8Literal(env->isolate(), "preload")).Check(); ++ }; ++ ++ std::unique_ptr env( ++ node::CreateEnvironment(isolate_data_, context, {}, {}, ++ node::EnvironmentFlags::kDefaultFlags, {}, {}, ++ preload), ++ node::FreeEnvironment); ++ ++ v8::Local main_ret = ++ node::LoadEnvironment(env.get(), "return process.prop;").ToLocalChecked(); ++ node::Utf8Value main_ret_str(isolate_, main_ret); ++ EXPECT_EQ(std::string(*main_ret_str), "preload"); ++} diff --git a/script/gen-filenames.ts b/script/gen-filenames.ts index b281ea78ef9..cb2d0e65110 100644 --- a/script/gen-filenames.ts +++ b/script/gen-filenames.ts @@ -38,8 +38,8 @@ const main = async () => { config: 'webpack.config.worker.js' }, { - name: 'asar_bundle_deps', - config: 'webpack.config.asar.js' + name: 'node_bundle_deps', + config: 'webpack.config.node.js' }, { name: 'utility_bundle_deps', diff --git a/shell/app/node_main.cc b/shell/app/node_main.cc index e375cb6cb12..20b70806500 100644 --- a/shell/app/node_main.cc +++ b/shell/app/node_main.cc @@ -21,7 +21,6 @@ #include "base/task/single_thread_task_runner.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "content/public/common/content_switches.h" -#include "electron/electron_version.h" #include "electron/fuses.h" #include "gin/array_buffer.h" #include "gin/public/isolate_holder.h" @@ -260,7 +259,8 @@ int NodeMain(int argc, char* argv[]) { env = node::CreateEnvironment( isolate_data, isolate->GetCurrentContext(), result->args(), result->exec_args(), - static_cast(env_flags)); + static_cast(env_flags), {}, {}, + &OnNodePreload); CHECK_NE(nullptr, env); node::SetIsolateUpForNode(isolate); @@ -282,11 +282,6 @@ int NodeMain(int argc, char* argv[]) { #endif process.Set("crashReporter", reporter); - - gin_helper::Dictionary versions; - if (process.Get("versions", &versions)) { - versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING); - } } v8::HandleScope scope(isolate); diff --git a/shell/common/api/electron_api_asar.cc b/shell/common/api/electron_api_asar.cc index 4c8ff7ee471..58db75a327b 100644 --- a/shell/common/api/electron_api_asar.cc +++ b/shell/common/api/electron_api_asar.cc @@ -10,7 +10,6 @@ #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/node_includes.h" -#include "shell/common/node_util.h" namespace { @@ -191,19 +190,6 @@ class Archive : public node::ObjectWrap { std::shared_ptr archive_; }; -static void InitAsarSupport(const v8::FunctionCallbackInfo& args) { - auto* isolate = args.GetIsolate(); - auto require = args[0]; - - // Evaluate asar_bundle.js. - std::vector> asar_bundle_params = { - node::FIXED_ONE_BYTE_STRING(isolate, "require")}; - std::vector> asar_bundle_args = {require}; - electron::util::CompileAndCall(isolate->GetCurrentContext(), - "electron/js2c/asar_bundle", - &asar_bundle_params, &asar_bundle_args); -} - static void SplitPath(const v8::FunctionCallbackInfo& args) { auto* isolate = args.GetIsolate(); @@ -239,7 +225,6 @@ void Initialize(v8::Local exports, exports->Set(context, node::FIXED_ONE_BYTE_STRING(isolate, "Archive"), cons) .Check(); NODE_SET_METHOD(exports, "splitPath", &SplitPath); - NODE_SET_METHOD(exports, "initAsarSupport", &InitAsarSupport); } } // namespace diff --git a/shell/common/api/electron_bindings.cc b/shell/common/api/electron_bindings.cc index b89a583f7b1..596a593b2a6 100644 --- a/shell/common/api/electron_bindings.cc +++ b/shell/common/api/electron_bindings.cc @@ -15,8 +15,6 @@ #include "base/process/process_handle.h" #include "base/process/process_metrics_iocounters.h" #include "base/system/sys_info.h" -#include "chrome/common/chrome_version.h" -#include "electron/electron_version.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" #include "shell/browser/browser.h" @@ -84,12 +82,6 @@ void ElectronBindings::BindTo(v8::Isolate* isolate, dict.SetMethod("activateUvLoop", base::BindRepeating(&ElectronBindings::ActivateUVLoop, base::Unretained(this))); - - gin_helper::Dictionary versions; - if (dict.Get("versions", &versions)) { - versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING); - versions.SetReadOnly("chrome", CHROME_VERSION_STRING); - } } void ElectronBindings::EnvironmentDestroyed(node::Environment* env) { diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 9fc4166bd00..22d08a96297 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -20,9 +20,11 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" #include "base/trace_event/trace_event.h" +#include "chrome/common/chrome_version.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_paths.h" #include "electron/buildflags/buildflags.h" +#include "electron/electron_version.h" #include "electron/fuses.h" #include "shell/browser/api/electron_api_app.h" #include "shell/common/api/electron_bindings.h" @@ -34,6 +36,7 @@ #include "shell/common/gin_helper/event_emitter_caller.h" #include "shell/common/gin_helper/microtasks_scope.h" #include "shell/common/mac/main_application_bundle.h" +#include "shell/common/node_util.h" #include "shell/common/world_ids.h" #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/renderer/bindings/core/v8/v8_initializer.h" // nogncheck @@ -610,7 +613,6 @@ std::shared_ptr NodeBindings::CreateEnvironment( electron::fuses::IsOnlyLoadAppFromAsarEnabled())); } - base::FilePath resources_path = GetResourcesPath(); std::string init_script = "electron/js2c/" + process_type + "_init"; args.insert(args.begin() + 1, init_script); @@ -649,7 +651,8 @@ std::shared_ptr NodeBindings::CreateEnvironment( v8::TryCatch try_catch(isolate); env = node::CreateEnvironment( static_cast(isolate_data), context, args, exec_args, - static_cast(env_flags)); + static_cast(env_flags), {}, {}, + &OnNodePreload); if (try_catch.HasCaught()) { std::string err_msg = @@ -735,11 +738,6 @@ std::shared_ptr NodeBindings::CreateEnvironment( gin_helper::Dictionary process(context->GetIsolate(), env->process_object()); process.SetReadOnly("type", process_type); - process.Set("resourcesPath", resources_path); - // The path to helper app. - base::FilePath helper_exec_path; - base::PathService::Get(content::CHILD_PROCESS_EXE, &helper_exec_path); - process.Set("helperExecPath", helper_exec_path); if (browser_env_ == BrowserEnvironment::kBrowser || browser_env_ == BrowserEnvironment::kRenderer) { @@ -931,4 +929,29 @@ void NodeBindings::EmbedThreadRunner(void* arg) { } } +void OnNodePreload(node::Environment* env, + v8::Local process, + v8::Local require) { + // Set custom process properties. + gin_helper::Dictionary dict(env->isolate(), process.As()); + dict.SetReadOnly("resourcesPath", GetResourcesPath()); + base::FilePath helper_exec_path; // path to the helper app. + base::PathService::Get(content::CHILD_PROCESS_EXE, &helper_exec_path); + dict.SetReadOnly("helperExecPath", helper_exec_path); + gin_helper::Dictionary versions; + if (dict.Get("versions", &versions)) { + versions.SetReadOnly(ELECTRON_PROJECT_NAME, ELECTRON_VERSION_STRING); + versions.SetReadOnly("chrome", CHROME_VERSION_STRING); + } + + // Execute lib/node/init.ts. + std::vector> bundle_params = { + node::FIXED_ONE_BYTE_STRING(env->isolate(), "process"), + node::FIXED_ONE_BYTE_STRING(env->isolate(), "require"), + }; + std::vector> bundle_args = {process, require}; + electron::util::CompileAndCall(env->context(), "electron/js2c/node_init", + &bundle_params, &bundle_args); +} + } // namespace electron diff --git a/shell/common/node_bindings.h b/shell/common/node_bindings.h index 596eee17d3b..fce3f39eadf 100644 --- a/shell/common/node_bindings.h +++ b/shell/common/node_bindings.h @@ -219,6 +219,12 @@ class NodeBindings { base::WeakPtrFactory weak_factory_{this}; }; +// A thread-safe function responsible for loading preload script which runs for +// all node environments (including child processes and workers). +void OnNodePreload(node::Environment* env, + v8::Local process, + v8::Local require); + } // namespace electron #endif // ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_ diff --git a/shell/common/node_util.cc b/shell/common/node_util.cc index 2dcaea14cdf..344f07f37c0 100644 --- a/shell/common/node_util.cc +++ b/shell/common/node_util.cc @@ -5,6 +5,7 @@ #include "shell/common/node_util.h" #include "base/logging.h" +#include "gin/converter.h" #include "shell/common/node_includes.h" namespace electron::util { @@ -31,7 +32,14 @@ v8::MaybeLocal CompileAndCall( // This will only be caught when something has gone terrible wrong as all // electron scripts are wrapped in a try {} catch {} by webpack if (try_catch.HasCaught()) { - LOG(ERROR) << "Failed to CompileAndCall electron script: " << id; + std::string msg = "no error message"; + if (!try_catch.Message().IsEmpty()) { + gin::ConvertFromV8(isolate, try_catch.Message()->Get(), &msg); + } else if (try_catch.HasTerminated()) { + msg = "script execution has been terminated"; + } + LOG(ERROR) << "Failed to CompileAndCall electron script (" << id + << "): " << msg; } return ret; } diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 04a6ad6b7da..66ba2999b48 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -90,7 +90,6 @@ declare namespace NodeJS { asarPath: string; filePath: string; }; - initAsarSupport(require: NodeJS.Require): void; } interface NetBinding {