chore: remove node patches by using the preload feature (#41115)

* chore: remove node patches by using the preload feature

* chore: fix node tests caused by filename change
This commit is contained in:
Cheng Zhao
2024-01-26 17:28:11 +09:00
committed by GitHub
parent f9ac250f6d
commit 2d559ca855
22 changed files with 445 additions and 158 deletions

View File

@@ -164,15 +164,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" ]
@@ -218,6 +209,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" ]
@@ -229,9 +229,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",
@@ -239,9 +239,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",

View File

@@ -1,5 +0,0 @@
module.exports = require('./webpack.config.base')({
target: 'asar',
alwaysHasNode: true,
targetDeletesNodeGlobals: true
});

View File

@@ -0,0 +1,4 @@
module.exports = require('./webpack.config.base')({
target: 'node',
alwaysHasNode: true
});

View File

@@ -331,10 +331,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",

View File

@@ -1,3 +0,0 @@
import { wrapFsWithAsar } from './fs-wrapper';
wrapFsWithAsar(require('fs'));

31
lib/node/init.ts Normal file
View File

@@ -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);
};

View File

@@ -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
@@ -50,3 +48,4 @@ lib_fix_broadcastchannel_initialization_location.patch
fix_handle_possible_disabled_sharedarraybuffer.patch
win_process_avoid_assert_after_spawning_store_app_4152.patch
fix_avx_detection.patch
src_preload_function_for_environment.patch

View File

@@ -26,10 +26,10 @@ index 001343b74ce236f89dca030c0fc9dd0299df0b39..6f277daf4ce846f093f634c473daec07
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 2e35cbba2ac02494c44821af395fe0195b1ab6b5..f5b6fecf6843a6204ec532a77f39a36d33bf061d 100644
index 250a43c5455b4f9ff72dd3a34d5b0aa270f43cc6..31f5d1528c840a2926b59b2b1c82ff265588a37a 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -149,11 +149,13 @@ function patchProcessObject(expandArgv1) {
@@ -144,11 +144,13 @@ function patchProcessObject(expandArgv1) {
if (expandArgv1 && process.argv[1] &&
!StringPrototypeStartsWith(process.argv[1], '-')) {
// Expand process.argv[1] into a full path.

View File

@@ -11,7 +11,7 @@ trying to see whether or not the lines are greyed out. One possibility
would be to upstream a changed test that doesn't hardcode line numbers.
diff --git a/test/fixtures/errors/force_colors.snapshot b/test/fixtures/errors/force_colors.snapshot
index 0334a0b4faa3633aa8617b9538873e7f3540513b..fa9989f55980aeddd3fa944318488c0289e3ab6e 100644
index 0334a0b4faa3633aa8617b9538873e7f3540513b..0d503d60931278ff9a7fb41177687688119390a3 100644
--- a/test/fixtures/errors/force_colors.snapshot
+++ b/test/fixtures/errors/force_colors.snapshot
@@ -4,11 +4,12 @@ throw new Error('Should include grayed stack trace')
@@ -27,7 +27,7 @@ index 0334a0b4faa3633aa8617b9538873e7f3540513b..fa9989f55980aeddd3fa944318488c02
+ at Object..js (node:internal*modules*cjs*loader:1326:10)
+ at Module.load (node:internal*modules*cjs*loader:1126:32)
+ at node:internal*modules*cjs*loader:967:12
+ at Function._load (node:electron*js2c*asar_bundle:743:32)
+ at Function._load (node:electron*js2c*node_init:741:32)
+ at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:96:12)
 at node:internal*main*run_main_module:23:47

View File

@@ -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 da553f6556a06d57d7490d74a3b4dd8f0132600c..168a6c72520d6540f0dbf84a62bc5e2b2f3ae022 100644
index 59c37b97672d39a9da89ca2b78aa28a77ca78699..319d853a35d9191c64a4f21132306a774acd84e6 100644
--- a/lib/child_process.js
+++ b/lib/child_process.js
@@ -60,6 +60,7 @@ let debug = require('internal/util/debuglog').debuglog(
@@ -19,7 +19,7 @@ index da553f6556a06d57d7490d74a3b4dd8f0132600c..168a6c72520d6540f0dbf84a62bc5e2b
const {
AbortError,
@@ -163,7 +164,6 @@ function fork(modulePath, args = [], options) {
@@ -152,7 +153,6 @@ function fork(modulePath, args = [], options) {
ArrayPrototypeSplice(execArgv, index - 1, 2);
}
}
@@ -27,7 +27,7 @@ index da553f6556a06d57d7490d74a3b4dd8f0132600c..168a6c72520d6540f0dbf84a62bc5e2b
args = [...execArgv, modulePath, ...args];
if (typeof options.stdio === 'string') {
@@ -625,6 +625,21 @@ function normalizeSpawnArguments(file, args, options) {
@@ -614,6 +614,21 @@ function normalizeSpawnArguments(file, args, options) {
'options.windowsVerbatimArguments');
}

View File

@@ -1,54 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
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/policy.js b/lib/internal/process/policy.js
index 8e07cb92118c84b2bc1156532cae8b033b9a48f4..2073a024ad548fe80e84cafee012b003454d6c93 100644
--- a/lib/internal/process/policy.js
+++ b/lib/internal/process/policy.js
@@ -39,9 +39,6 @@ module.exports = ObjectFreeze({
process.binding = function binding(_module) {
throw new ERR_ACCESS_DENIED('process.binding');
};
- process._linkedBinding = function _linkedBinding(_module) {
- throw new ERR_ACCESS_DENIED('process._linkedBinding');
- };
},
get manifest() {
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index 250a43c5455b4f9ff72dd3a34d5b0aa270f43cc6..2e35cbba2ac02494c44821af395fe0195b1ab6b5 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -117,12 +117,17 @@ function setupUserModules() {
loadPreloadModules();
// Need to be done after --require setup.
initializeFrozenIntrinsics();
+ setupAsarSupport();
}
function refreshRuntimeOptions() {
refreshOptions();
}
+function setupAsarSupport() {
+ process._linkedBinding('electron_common_asar').initAsarSupport(require);
+}
+
function patchProcessObject(expandArgv1) {
const binding = internalBinding('process_methods');
binding.patchProcessObject(process);
diff --git a/test/fixtures/policy/process-binding/app.js b/test/fixtures/policy/process-binding/app.js
index 16e26d12a160286b1b6aaeb64b15668b05b9865b..a287d0a2363acbf24077eec040116f96ef18a7b3 100644
--- a/test/fixtures/policy/process-binding/app.js
+++ b/test/fixtures/policy/process-binding/app.js
@@ -5,6 +5,3 @@ const assert = require('assert');
assert.throws(() => { process.binding(); }, {
code: 'ERR_ACCESS_DENIED'
});
-assert.throws(() => { process._linkedBinding(); }, {
- code: 'ERR_ACCESS_DENIED'
-});

View File

@@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
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 59c37b97672d39a9da89ca2b78aa28a77ca78699..da553f6556a06d57d7490d74a3b4dd8f0132600c 100644
--- a/lib/child_process.js
+++ b/lib/child_process.js
@@ -137,7 +137,18 @@ function fork(modulePath, args = [], options) {
validateObject(options, 'options');
}
options = { ...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');
// Prepare arguments for fork:

View File

@@ -0,0 +1,339 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
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 31f5d1528c840a2926b59b2b1c82ff265588a37a..1e37bd59f53d3a194b2492c83f8e7299c42c828c 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -114,6 +114,9 @@ function setupUserModules() {
initializeESMLoader();
const CJSLoader = require('internal/modules/cjs/loader');
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
+ if (getEmbedderOptions().hasEmbedderPreload) {
+ runEmbedderPreload();
+ }
loadPreloadModules();
// Need to be done after --require setup.
initializeFrozenIntrinsics();
@@ -588,6 +591,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 c4caef25af670658965fc740ce03c2d2c4ed3e66..19443a9672441da5b98921eab9385083a72e3b7e 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -404,14 +404,16 @@ Environment* CreateEnvironment(
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags,
ThreadId thread_id,
- std::unique_ptr<InspectorParentHandle> inspector_parent_handle) {
+ std::unique_ptr<InspectorParentHandle> inspector_parent_handle,
+ EmbedderPreloadCallback preload) {
Isolate* isolate = context->GetIsolate();
HandleScope handle_scope(isolate);
Context::Scope context_scope(context);
// TODO(addaleax): This is a much better place for parsing per-Environment
// options than the global parse call.
Environment* env = new Environment(
- isolate_data, context, args, exec_args, nullptr, flags, thread_id);
+ isolate_data, context, args, exec_args, nullptr, flags, thread_id,
+ std::move(preload));
#if HAVE_INSPECTOR
if (env->should_create_inspector()) {
diff --git a/src/env-inl.h b/src/env-inl.h
index debd982c75805c51ea7d01229b9d635550060503..6af9217acb6f22c89bc92708aa9ab3d021c5e5bf 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -388,6 +388,10 @@ inline std::vector<double>* Environment::destroy_async_id_list() {
return &destroy_async_id_list_;
}
+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 6e8b314680c9175d8d513cc72382012ae5e70b26..e416bca327f181884ff8dad2b3a82ad826c0f3b5 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -645,7 +645,8 @@ Environment::Environment(IsolateData* isolate_data,
const std::vector<std::string>& 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)),
@@ -668,7 +669,8 @@ Environment::Environment(IsolateData* isolate_data,
flags_(flags),
thread_id_(thread_id.id == static_cast<uint64_t>(-1)
? AllocateEnvironmentThreadId().id
- : thread_id.id) {
+ : thread_id.id),
+ embedder_preload_(std::move(preload)) {
// We'll be creating new objects so make sure we've entered the context.
HandleScope handle_scope(isolate);
@@ -738,14 +740,16 @@ Environment::Environment(IsolateData* isolate_data,
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id)
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload)
: Environment(isolate_data,
context->GetIsolate(),
args,
exec_args,
env_info,
flags,
- thread_id) {
+ thread_id,
+ std::move(preload)) {
InitializeMainContext(context, env_info);
}
diff --git a/src/env.h b/src/env.h
index c914b621f50bcd6bce2617fef9e48737235aa516..d2e7f8534498ca171986cf77ef19d2fc9b950a5b 100644
--- a/src/env.h
+++ b/src/env.h
@@ -579,7 +579,8 @@ class Environment : public MemoryRetainer {
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id);
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload);
void InitializeMainContext(v8::Local<v8::Context> context,
const EnvSerializeInfo* env_info);
// Create an Environment and initialize the provided principal context for it.
@@ -589,7 +590,8 @@ class Environment : public MemoryRetainer {
const std::vector<std::string>& exec_args,
const EnvSerializeInfo* env_info,
EnvironmentFlags::Flags flags,
- ThreadId thread_id);
+ ThreadId thread_id,
+ EmbedderPreloadCallback preload);
~Environment() override;
void InitializeLibuv();
@@ -933,6 +935,8 @@ class Environment : public MemoryRetainer {
#endif // HAVE_INSPECTOR
+ inline const EmbedderPreloadCallback& embedder_preload() const;
+
inline void set_process_exit_handler(
std::function<void(Environment*, int)>&& handler);
@@ -1101,6 +1105,7 @@ class Environment : public MemoryRetainer {
DefaultProcessExitHandler };
std::unique_ptr<Realm> principal_realm_ = nullptr;
+ 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 26368061a909e6abc62a4cf261a5dbbd79404f1a..bb4065e33164c3ea762a27b71606ab4ed7b1b336 100644
--- a/src/node.h
+++ b/src/node.h
@@ -593,9 +593,21 @@ struct InspectorParentHandle {
virtual ~InspectorParentHandle();
};
+using EmbedderPreloadCallback =
+ std::function<void(Environment* env,
+ v8::Local<v8::Value> process,
+ v8::Local<v8::Value> 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.
+//
+// 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<v8::Context> context,
@@ -603,7 +615,8 @@ NODE_EXTERN Environment* CreateEnvironment(
const std::vector<std::string>& exec_args,
EnvironmentFlags::Flags flags = EnvironmentFlags::kDefaultFlags,
ThreadId thread_id = {} /* allocates a thread id automatically */,
- std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
+ std::unique_ptr<InspectorParentHandle> 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_main_instance.cc b/src/node_main_instance.cc
index a8661c3c2263fc62e55659310b8da12fc414361e..849442aa8c923808420cbc888befea7d3f1f4c1b 100644
--- a/src/node_main_instance.cc
+++ b/src/node_main_instance.cc
@@ -157,7 +157,8 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) {
exec_args_,
&(snapshot_data_->env_info),
EnvironmentFlags::kDefaultFlags,
- {}));
+ {},
+ nullptr));
context = Context::FromSnapshot(isolate_,
SnapshotData::kNodeMainContextIndex,
{DeserializeNodeInternalFields, env.get()})
diff --git a/src/node_options.cc b/src/node_options.cc
index 7ad8d80faee840e4dd224d946871b2ff08b0c23c..25842fd531fc7e1485bcd75f1f92aa9bc0640862 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -1220,6 +1220,12 @@ void GetEmbedderOptions(const FunctionCallbackInfo<Value>& args) {
Boolean::New(isolate, env->no_global_search_paths()))
.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 bfa048a4a8aa183e747dec84b11b1c1d847db2dd..b8337e68cb946366b2dff78bbd12ce5473ee166c 100644
--- a/src/node_snapshotable.cc
+++ b/src/node_snapshotable.cc
@@ -1462,6 +1462,13 @@ void SerializeSnapshotableObjects(Realm* realm,
namespace mksnapshot {
+static void RunEmbedderPreload(const FunctionCallbackInfo<Value>& 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<Value>& args) {
CHECK(args[0]->IsString());
Local<String> filename = args[0].As<String>();
@@ -1515,6 +1522,7 @@ void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
+ SetMethod(context, target, "runEmbedderPreload", RunEmbedderPreload);
SetMethod(context, target, "compileSerializeMain", CompileSerializeMain);
SetMethod(context, target, "setSerializeCallback", SetSerializeCallback);
SetMethod(context, target, "setDeserializeCallback", SetDeserializeCallback);
@@ -1525,6 +1533,7 @@ void Initialize(Local<Object> target,
}
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
+ 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 6a49144ec4f2059fe75983609b0768e4c2b1817d..dc2eb247b011f9cb1945c173c49e029f068ef103 100644
--- a/src/node_worker.cc
+++ b/src/node_worker.cc
@@ -60,6 +60,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);
@@ -333,7 +334,8 @@ void Worker::Run() {
std::move(exec_argv_),
static_cast<EnvironmentFlags::Flags>(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 a77c416735a79feb3f54e40d72a98c8903a20ccd..deab68576f6330f8bcfb4703fd05dbb9c515e473 100644
--- a/src/node_worker.h
+++ b/src/node_worker.h
@@ -113,6 +113,7 @@ class Worker : public AsyncWrap {
std::unique_ptr<MessagePortData> child_port_data_;
std::shared_ptr<KVStore> 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 547c8ddbffe243113bfe47a51072bb8f1541b94f..19ef2c2a083f908267e6a9365e77b20d46a3feec 100644
--- a/test/cctest/test_environment.cc
+++ b/test/cctest/test_environment.cc
@@ -749,3 +749,31 @@ TEST_F(EnvironmentTest, RequestInterruptAtExit) {
node::FreeIsolateData(isolate_data);
context->Exit();
}
+
+TEST_F(EnvironmentTest, EmbedderPreload) {
+ v8::HandleScope handle_scope(isolate_);
+ v8::Local<v8::Context> context = node::NewContext(isolate_);
+ v8::Context::Scope context_scope(context);
+
+ node::EmbedderPreloadCallback preload = [](node::Environment* env,
+ v8::Local<v8::Value> process,
+ v8::Local<v8::Value> require) {
+ CHECK(process->IsObject());
+ CHECK(require->IsFunction());
+ process.As<v8::Object>()->Set(
+ env->context(),
+ v8::String::NewFromUtf8Literal(env->isolate(), "prop"),
+ v8::String::NewFromUtf8Literal(env->isolate(), "preload")).Check();
+ };
+
+ std::unique_ptr<node::Environment, decltype(&node::FreeEnvironment)> env(
+ node::CreateEnvironment(isolate_data_, context, {}, {},
+ node::EnvironmentFlags::kDefaultFlags, {}, {},
+ preload),
+ node::FreeEnvironment);
+
+ v8::Local<v8::Value> 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");
+}

View File

@@ -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',

View File

@@ -20,7 +20,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<node::EnvironmentFlags::Flags>(env_flags));
static_cast<node::EnvironmentFlags::Flags>(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);

View File

@@ -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<asar::Archive> archive_;
};
static void InitAsarSupport(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto* isolate = args.GetIsolate();
auto require = args[0];
// Evaluate asar_bundle.js.
std::vector<v8::Local<v8::String>> asar_bundle_params = {
node::FIXED_ONE_BYTE_STRING(isolate, "require")};
std::vector<v8::Local<v8::Value>> asar_bundle_args = {require};
electron::util::CompileAndCall(
isolate->GetCurrentContext(), "electron/js2c/asar_bundle",
&asar_bundle_params, &asar_bundle_args, nullptr);
}
static void SplitPath(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto* isolate = args.GetIsolate();
@@ -239,7 +225,6 @@ void Initialize(v8::Local<v8::Object> 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

View File

@@ -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) {

View File

@@ -19,9 +19,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"
@@ -32,6 +34,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 "third_party/blink/renderer/bindings/core/v8/v8_initializer.h" // nogncheck
#include "third_party/electron_node/src/debug_utils.h"
@@ -537,7 +540,6 @@ std::shared_ptr<node::Environment> 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);
@@ -576,7 +578,8 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
v8::TryCatch try_catch(isolate);
env = node::CreateEnvironment(
static_cast<node::IsolateData*>(isolate_data), context, args, exec_args,
static_cast<node::EnvironmentFlags::Flags>(flags));
static_cast<node::EnvironmentFlags::Flags>(flags), {}, {},
&OnNodePreload);
if (try_catch.HasCaught()) {
std::string err_msg =
@@ -658,11 +661,6 @@ std::shared_ptr<node::Environment> 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);
auto env_deleter = [isolate, isolate_data,
context = v8::Global<v8::Context>{isolate, context}](
@@ -811,4 +809,29 @@ void NodeBindings::EmbedThreadRunner(void* arg) {
}
}
void OnNodePreload(node::Environment* env,
v8::Local<v8::Value> process,
v8::Local<v8::Value> require) {
// Set custom process properties.
gin_helper::Dictionary dict(env->isolate(), process.As<v8::Object>());
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<v8::Local<v8::String>> bundle_params = {
node::FIXED_ONE_BYTE_STRING(env->isolate(), "process"),
node::FIXED_ONE_BYTE_STRING(env->isolate(), "require"),
};
std::vector<v8::Local<v8::Value>> bundle_args = {process, require};
electron::util::CompileAndCall(env->context(), "electron/js2c/node_init",
&bundle_params, &bundle_args, env);
}
} // namespace electron

View File

@@ -201,6 +201,12 @@ class NodeBindings {
base::WeakPtrFactory<NodeBindings> 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<v8::Value> process,
v8::Local<v8::Value> require);
} // namespace electron
#endif // ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_

View File

@@ -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 {
@@ -29,7 +30,14 @@ v8::MaybeLocal<v8::Value> 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;
}

View File

@@ -83,7 +83,6 @@ declare namespace NodeJS {
asarPath: string;
filePath: string;
};
initAsarSupport(require: NodeJS.Require): void;
}
interface NetBinding {