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:
Robo 2022-10-20 14:49:49 +09:00 committed by GitHub
parent 44c40efecf
commit da0fd286b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 2700 additions and 54 deletions

View file

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

View file

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