feat: UtilityProcess API (#34980)
* chore: initial scaffolding * chore: implement interface and docs * chore: address code style review * fix: cleanup of utility process on shutdown * chore: simplify NodeBindings::CreateEnvironment * chore: rename disableLibraryValidation => allowLoadingUnsignedLibraries * chore: implement process.parentPort * chore(posix): implement stdio pipe interface * chore(win): implement stdio interface * chore: reenable SetNodeOptions for utility process * chore: add specs * chore: fix lint * fix: update kill API * fix: update process.parentPort API * fix: exit event * docs: update exit event * fix: tests on linux * chore: expand on some comments * fix: shutdown of pipe reader Avoid logging since it is always the case that reader end of pipe will terminate after the child process. * fix: remove exit code check for crash spec * fix: rm PR_SET_NO_NEW_PRIVS for unsandbox utility process * chore: fix incorrect rebase * fix: address review feedback * chore: rename utility_process -> utility * chore: update docs * chore: cleanup c++ implemantation * fix: leak in NodeServiceHost impl * chore: minor cleanup * chore: cleanup JS implementation * chore: flip default stdio to inherit * fix: some api improvements * Support cwd option * Remove path restriction for modulePath * Rewire impl for env support * fix: add tests for cwd and env option * chore: alt impl for reading stdio handles * chore: support message queuing * chore: fix lint * chore: new UtilityProcess => utilityProcess.fork * fix: support for uncaught exception exits * chore: remove process.execArgv as default * fix: windows build * fix: style changes * fix: docs and style changes * chore: update patches * spec: disable flaky test on win32 arm CI Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
This commit is contained in:
parent
44c40efecf
commit
da0fd286b4
59 changed files with 2700 additions and 54 deletions
|
@ -70,6 +70,7 @@
|
|||
V(electron_browser_system_preferences) \
|
||||
V(electron_browser_base_window) \
|
||||
V(electron_browser_tray) \
|
||||
V(electron_browser_utility_process) \
|
||||
V(electron_browser_view) \
|
||||
V(electron_browser_web_contents) \
|
||||
V(electron_browser_web_contents_view) \
|
||||
|
@ -87,7 +88,8 @@
|
|||
V(electron_renderer_context_bridge) \
|
||||
V(electron_renderer_crash_reporter) \
|
||||
V(electron_renderer_ipc) \
|
||||
V(electron_renderer_web_frame)
|
||||
V(electron_renderer_web_frame) \
|
||||
V(electron_utility_parent_port)
|
||||
|
||||
#define ELECTRON_VIEWS_MODULES(V) V(electron_browser_image_view)
|
||||
|
||||
|
@ -390,7 +392,11 @@ void NodeBindings::Initialize() {
|
|||
std::vector<std::string> argv = {"electron"};
|
||||
std::vector<std::string> exec_argv;
|
||||
std::vector<std::string> errors;
|
||||
uint64_t process_flags = node::ProcessFlags::kEnableStdioInheritance;
|
||||
uint64_t process_flags = node::ProcessFlags::kNoFlags;
|
||||
// We do not want the child processes spawned from the utility process
|
||||
// to inherit the custom stdio handles created for the parent.
|
||||
if (browser_env_ != BrowserEnvironment::kUtility)
|
||||
process_flags |= node::ProcessFlags::kEnableStdioInheritance;
|
||||
if (!fuses::IsNodeOptionsEnabled())
|
||||
process_flags |= node::ProcessFlags::kDisableNodeOptionsEnv;
|
||||
|
||||
|
@ -417,16 +423,9 @@ void NodeBindings::Initialize() {
|
|||
|
||||
node::Environment* NodeBindings::CreateEnvironment(
|
||||
v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
auto& atom_args = ElectronCommandLine::argv();
|
||||
std::vector<std::string> args(atom_args.size());
|
||||
std::transform(atom_args.cbegin(), atom_args.cend(), args.begin(),
|
||||
[](auto& a) { return base::WideToUTF8(a); });
|
||||
#else
|
||||
auto args = ElectronCommandLine::argv();
|
||||
#endif
|
||||
|
||||
node::MultiIsolatePlatform* platform,
|
||||
std::vector<std::string> args,
|
||||
std::vector<std::string> exec_args) {
|
||||
// Feed node the path to initialization script.
|
||||
std::string process_type;
|
||||
switch (browser_env_) {
|
||||
|
@ -439,14 +438,20 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
case BrowserEnvironment::kWorker:
|
||||
process_type = "worker";
|
||||
break;
|
||||
case BrowserEnvironment::kUtility:
|
||||
process_type = "utility";
|
||||
break;
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
gin_helper::Dictionary global(isolate, context->Global());
|
||||
// Do not set DOM globals for renderer process.
|
||||
// We must set this before the node bootstrapper which is run inside
|
||||
// CreateEnvironment
|
||||
if (browser_env_ != BrowserEnvironment::kBrowser)
|
||||
// Avoids overriding globals like setImmediate, clearImmediate
|
||||
// queueMicrotask etc during the bootstrap phase of Node.js
|
||||
// for processes that already have these defined by DOM.
|
||||
// Check //third_party/electron_node/lib/internal/bootstrap/node.js
|
||||
// for the list of overrides on globalThis.
|
||||
if (browser_env_ == BrowserEnvironment::kRenderer ||
|
||||
browser_env_ == BrowserEnvironment::kWorker)
|
||||
global.Set("_noBrowserGlobals", true);
|
||||
|
||||
if (browser_env_ == BrowserEnvironment::kBrowser) {
|
||||
|
@ -464,7 +469,6 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
: search_paths));
|
||||
}
|
||||
|
||||
std::vector<std::string> exec_args;
|
||||
base::FilePath resources_path = GetResourcesPath();
|
||||
std::string init_script = "electron/js2c/" + process_type + "_init";
|
||||
|
||||
|
@ -478,7 +482,8 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
node::EnvironmentFlags::kHideConsoleWindows |
|
||||
node::EnvironmentFlags::kNoGlobalSearchPaths;
|
||||
|
||||
if (browser_env_ != BrowserEnvironment::kBrowser) {
|
||||
if (browser_env_ == BrowserEnvironment::kRenderer ||
|
||||
browser_env_ == BrowserEnvironment::kWorker) {
|
||||
// Only one ESM loader can be registered per isolate -
|
||||
// in renderer processes this should be blink. We need to tell Node.js
|
||||
// not to register its handler (overriding blinks) in non-browser processes.
|
||||
|
@ -514,7 +519,8 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
|
||||
// Clean up the global _noBrowserGlobals that we unironically injected into
|
||||
// the global scope
|
||||
if (browser_env_ != BrowserEnvironment::kBrowser) {
|
||||
if (browser_env_ == BrowserEnvironment::kRenderer ||
|
||||
browser_env_ == BrowserEnvironment::kWorker) {
|
||||
// We need to bootstrap the env in non-browser processes so that
|
||||
// _noBrowserGlobals is read correctly before we remove it
|
||||
global.Delete("_noBrowserGlobals");
|
||||
|
@ -528,15 +534,21 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
|
||||
// We don't want to abort either in the renderer or browser processes.
|
||||
// We already listen for uncaught exceptions and handle them there.
|
||||
is.should_abort_on_uncaught_exception_callback = [](v8::Isolate*) {
|
||||
return false;
|
||||
};
|
||||
// For utility process we expect the process to behave as standard
|
||||
// Node.js runtime and abort the process with appropriate exit
|
||||
// code depending on a handler being set for `uncaughtException` event.
|
||||
if (browser_env_ != BrowserEnvironment::kUtility) {
|
||||
is.should_abort_on_uncaught_exception_callback = [](v8::Isolate*) {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// Use a custom callback here to allow us to leverage Blink's logic in the
|
||||
// renderer process.
|
||||
is.allow_wasm_code_generation_callback = AllowWasmCodeGenerationCallback;
|
||||
|
||||
if (browser_env_ == BrowserEnvironment::kBrowser) {
|
||||
if (browser_env_ == BrowserEnvironment::kBrowser ||
|
||||
browser_env_ == BrowserEnvironment::kUtility) {
|
||||
// Node.js requires that microtask checkpoints be explicitly invoked.
|
||||
is.policy = v8::MicrotasksPolicy::kExplicit;
|
||||
} else {
|
||||
|
@ -585,6 +597,20 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
return env;
|
||||
}
|
||||
|
||||
node::Environment* NodeBindings::CreateEnvironment(
|
||||
v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
auto& electron_args = ElectronCommandLine::argv();
|
||||
std::vector<std::string> args(electron_args.size());
|
||||
std::transform(electron_args.cbegin(), electron_args.cend(), args.begin(),
|
||||
[](auto& a) { return base::WideToUTF8(a); });
|
||||
#else
|
||||
auto args = ElectronCommandLine::argv();
|
||||
#endif
|
||||
return CreateEnvironment(context, platform, args, {});
|
||||
}
|
||||
|
||||
void NodeBindings::LoadEnvironment(node::Environment* env) {
|
||||
node::LoadEnvironment(env, node::StartExecutionCallback{});
|
||||
gin_helper::EmitEvent(env->isolate(), env->process_object(), "loaded");
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#ifndef ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
|
||||
#define ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
@ -74,7 +76,7 @@ class UvHandle {
|
|||
|
||||
class NodeBindings {
|
||||
public:
|
||||
enum class BrowserEnvironment { kBrowser, kRenderer, kWorker };
|
||||
enum class BrowserEnvironment { kBrowser, kRenderer, kUtility, kWorker };
|
||||
|
||||
static NodeBindings* Create(BrowserEnvironment browser_env);
|
||||
static void RegisterBuiltinModules();
|
||||
|
@ -86,6 +88,10 @@ class NodeBindings {
|
|||
void Initialize();
|
||||
|
||||
// Create the environment and load node.js.
|
||||
node::Environment* CreateEnvironment(v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform,
|
||||
std::vector<std::string> args,
|
||||
std::vector<std::string> exec_args);
|
||||
node::Environment* CreateEnvironment(v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue