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
				
			
		
							
								
								
									
										12
									
								
								BUILD.gn
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								BUILD.gn
									
										
									
									
									
								
							| 
						 | 
					@ -202,6 +202,15 @@ webpack_build("electron_isolated_renderer_bundle") {
 | 
				
			||||||
  out_file = "$target_gen_dir/js2c/isolated_bundle.js"
 | 
					  out_file = "$target_gen_dir/js2c/isolated_bundle.js"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					webpack_build("electron_utility_bundle") {
 | 
				
			||||||
 | 
					  deps = [ ":build_electron_definitions" ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inputs = auto_filenames.utility_bundle_deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  config_file = "//electron/build/webpack/webpack.config.utility.js"
 | 
				
			||||||
 | 
					  out_file = "$target_gen_dir/js2c/utility_init.js"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
action("electron_js2c") {
 | 
					action("electron_js2c") {
 | 
				
			||||||
  deps = [
 | 
					  deps = [
 | 
				
			||||||
    ":electron_asar_bundle",
 | 
					    ":electron_asar_bundle",
 | 
				
			||||||
| 
						 | 
					@ -209,6 +218,7 @@ action("electron_js2c") {
 | 
				
			||||||
    ":electron_isolated_renderer_bundle",
 | 
					    ":electron_isolated_renderer_bundle",
 | 
				
			||||||
    ":electron_renderer_bundle",
 | 
					    ":electron_renderer_bundle",
 | 
				
			||||||
    ":electron_sandboxed_renderer_bundle",
 | 
					    ":electron_sandboxed_renderer_bundle",
 | 
				
			||||||
 | 
					    ":electron_utility_bundle",
 | 
				
			||||||
    ":electron_worker_bundle",
 | 
					    ":electron_worker_bundle",
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -218,6 +228,7 @@ action("electron_js2c") {
 | 
				
			||||||
    "$target_gen_dir/js2c/isolated_bundle.js",
 | 
					    "$target_gen_dir/js2c/isolated_bundle.js",
 | 
				
			||||||
    "$target_gen_dir/js2c/renderer_init.js",
 | 
					    "$target_gen_dir/js2c/renderer_init.js",
 | 
				
			||||||
    "$target_gen_dir/js2c/sandbox_bundle.js",
 | 
					    "$target_gen_dir/js2c/sandbox_bundle.js",
 | 
				
			||||||
 | 
					    "$target_gen_dir/js2c/utility_init.js",
 | 
				
			||||||
    "$target_gen_dir/js2c/worker_init.js",
 | 
					    "$target_gen_dir/js2c/worker_init.js",
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -368,6 +379,7 @@ source_set("electron_lib") {
 | 
				
			||||||
    "chromium_src:chrome",
 | 
					    "chromium_src:chrome",
 | 
				
			||||||
    "chromium_src:chrome_spellchecker",
 | 
					    "chromium_src:chrome_spellchecker",
 | 
				
			||||||
    "shell/common/api:mojo",
 | 
					    "shell/common/api:mojo",
 | 
				
			||||||
 | 
					    "shell/services/node/public/mojom",
 | 
				
			||||||
    "//base:base_static",
 | 
					    "//base:base_static",
 | 
				
			||||||
    "//base/allocator:buildflags",
 | 
					    "//base/allocator:buildflags",
 | 
				
			||||||
    "//chrome:strings",
 | 
					    "//chrome:strings",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								build/webpack/webpack.config.utility.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								build/webpack/webpack.config.utility.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					module.exports = require('./webpack.config.base')({
 | 
				
			||||||
 | 
					  target: 'utility',
 | 
				
			||||||
 | 
					  alwaysHasNode: true
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										46
									
								
								docs/api/parent-port.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								docs/api/parent-port.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					# parentPort
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> Interface for communication with parent process.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Process: [Utility](../glossary.md#utility-process)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`parentPort` is an [EventEmitter][event-emitter].
 | 
				
			||||||
 | 
					_This object is not exported from the `'electron'` module. It is only available as a property of the process object in the Electron API._
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// Main process
 | 
				
			||||||
 | 
					const child = utilityProcess.fork(path.join(__dirname, 'test.js'))
 | 
				
			||||||
 | 
					child.postMessage({ message: 'hello' })
 | 
				
			||||||
 | 
					child.on('message', (data) => {
 | 
				
			||||||
 | 
					  console.log(data) // hello world!
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Child process
 | 
				
			||||||
 | 
					process.parentPort.on('message', (e) => {
 | 
				
			||||||
 | 
					  process.parentPort.postMessage(`${e.data} world!`)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Events
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `parentPort` object emits the following events:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Event: 'message'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `messageEvent` Object
 | 
				
			||||||
 | 
					  * `data` any
 | 
				
			||||||
 | 
					  * `ports` MessagePortMain[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Emitted when the process receives a message. Messages received on
 | 
				
			||||||
 | 
					this port will be queued up until a handler is registered for this
 | 
				
			||||||
 | 
					event.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `parentPort.postMessage(message)`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `message` any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sends a message from the process to its parent.
 | 
				
			||||||
| 
						 | 
					@ -113,6 +113,7 @@ A `string` representing the current process's type, can be:
 | 
				
			||||||
* `browser` - The main process
 | 
					* `browser` - The main process
 | 
				
			||||||
* `renderer` - A renderer process
 | 
					* `renderer` - A renderer process
 | 
				
			||||||
* `worker` - In a web worker
 | 
					* `worker` - In a web worker
 | 
				
			||||||
 | 
					* `utility` - In a node process launched as a service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### `process.versions.chrome` _Readonly_
 | 
					### `process.versions.chrome` _Readonly_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,6 +135,11 @@ Each frame has its own JavaScript context. When contextIsolation is enabled, the
 | 
				
			||||||
world also has a separate JavaScript context.
 | 
					world also has a separate JavaScript context.
 | 
				
			||||||
This property is only available in the renderer process.
 | 
					This property is only available in the renderer process.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `process.parentPort`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A [`Electron.ParentPort`](parent-port.md) property if this is a [`UtilityProcess`](utility-process.md)
 | 
				
			||||||
 | 
					(or `null` otherwise) allowing communication with the parent process.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Methods
 | 
					## Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `process` object has the following methods:
 | 
					The `process` object has the following methods:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										136
									
								
								docs/api/utility-process.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								docs/api/utility-process.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,136 @@
 | 
				
			||||||
 | 
					# utilityProcess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`utilityProcess` creates a child process with
 | 
				
			||||||
 | 
					Node.js and Message ports enabled. It provides the equivalent of [`child_process.fork`][] API from Node.js
 | 
				
			||||||
 | 
					but instead uses [Services API][] from Chromium to launch the child process.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Process: [Main](../glossary.md#main-process)<br />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `utilityProcess.fork(modulePath[, args][, options])`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `modulePath` string - Path to the script that should run as entrypoint in the child process.
 | 
				
			||||||
 | 
					* `args` string[] (optional) - List of string arguments that will be available as `process.argv`
 | 
				
			||||||
 | 
					  in the child process.
 | 
				
			||||||
 | 
					* `options` Object (optional)
 | 
				
			||||||
 | 
					  * `env` Object (optional) - Environment key-value pairs. Default is `process.env`.
 | 
				
			||||||
 | 
					  * `execArgv` string[] (optional) - List of string arguments passed to the executable.
 | 
				
			||||||
 | 
					  * `cwd` string (optional) - Current working directory of the child process.
 | 
				
			||||||
 | 
					  * `stdio` (string[] | string) (optional) - Allows configuring the mode for `stdout` and `stderr`
 | 
				
			||||||
 | 
					    of the child process. Default is `inherit`.
 | 
				
			||||||
 | 
					    String value can be one of `pipe`, `ignore`, `inherit`, for more details on these values you can refer to
 | 
				
			||||||
 | 
					    [stdio][] documentation from Node.js. Currently this option only supports configuring `stdout` and
 | 
				
			||||||
 | 
					    `stderr` to either `pipe`, `inherit` or `ignore`. Configuring `stdin` is not supported; `stdin` will
 | 
				
			||||||
 | 
					    always be ignored.
 | 
				
			||||||
 | 
					    For example, the supported values will be processed as following:
 | 
				
			||||||
 | 
					    * `pipe`: equivalent to ['ignore', 'pipe', 'pipe'] (the default)
 | 
				
			||||||
 | 
					    * `ignore`: equivalent to 'ignore', 'ignore', 'ignore']
 | 
				
			||||||
 | 
					    * `inherit`: equivalent to ['ignore', 'inherit', 'inherit']
 | 
				
			||||||
 | 
					  * `serviceName` string (optional) - Name of the process that will appear in `name` property of
 | 
				
			||||||
 | 
					    [`child-process-gone` event of `app`](app.md#event-child-process-gone).
 | 
				
			||||||
 | 
					    Default is `node.mojom.NodeService`.
 | 
				
			||||||
 | 
					  * `allowLoadingUnsignedLibraries` boolean (optional) _macOS_ - With this flag, the utility process will be
 | 
				
			||||||
 | 
					    launched via the `Electron Helper (Plugin).app` helper executable on macOS, which can be
 | 
				
			||||||
 | 
					    codesigned with `com.apple.security.cs.disable-library-validation` and
 | 
				
			||||||
 | 
					    `com.apple.security.cs.allow-unsigned-executable-memory` entitlements. This will allow the utility process
 | 
				
			||||||
 | 
					    to load unsigned libraries. Unless you specifically need this capability, it is best to leave this disabled.
 | 
				
			||||||
 | 
					    Default is `false`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Returns [`UtilityProcess`](utility-process.md#class-utilityprocess)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Class: UtilityProcess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> Instances of the `UtilityProcess` represent the Chromium spawned child process
 | 
				
			||||||
 | 
					> with Node.js integration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`UtilityProcess` is an [EventEmitter][event-emitter].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Instance Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `child.postMessage(message, [transfer])`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `message` any
 | 
				
			||||||
 | 
					* `transfer` MessagePortMain[] (optional)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Send a message to the child process, optionally transferring ownership of
 | 
				
			||||||
 | 
					zero or more [`MessagePortMain`][] objects.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// Main process
 | 
				
			||||||
 | 
					const { port1, port2 } = new MessageChannelMain()
 | 
				
			||||||
 | 
					const child = utilityProcess.fork(path.join(__dirname, 'test.js'))
 | 
				
			||||||
 | 
					child.postMessage({ message: 'hello' }, [port1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Child process
 | 
				
			||||||
 | 
					process.parentPort.once('message', (e) => {
 | 
				
			||||||
 | 
					  const [port] = e.ports
 | 
				
			||||||
 | 
					  // ...
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `child.kill()`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Returns `boolean`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Terminates the process gracefully. On POSIX, it uses SIGTERM
 | 
				
			||||||
 | 
					but will ensure the process is reaped on exit. This function returns
 | 
				
			||||||
 | 
					true if the kill is successful, and false otherwise.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Instance Properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `child.pid`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A `Integer | undefined` representing the process identifier (PID) of the child process.
 | 
				
			||||||
 | 
					If the child process fails to spawn due to errors, then the value is `undefined`. When
 | 
				
			||||||
 | 
					the child process exits, then the value is `undefined` after the `exit` event is emitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `child.stdout`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A `NodeJS.ReadableStream | null` that represents the child process's stdout.
 | 
				
			||||||
 | 
					If the child was spawned with options.stdio[1] set to anything other than 'pipe', then this will be `null`.
 | 
				
			||||||
 | 
					When the child process exits, then the value is `null` after the `exit` event is emitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					// Main process
 | 
				
			||||||
 | 
					const { port1, port2 } = new MessageChannelMain()
 | 
				
			||||||
 | 
					const child = utilityProcess.fork(path.join(__dirname, 'test.js'))
 | 
				
			||||||
 | 
					child.stdout.on('data', (data) => {
 | 
				
			||||||
 | 
					  console.log(`Received chunk ${data}`)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### `child.stderr`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A `NodeJS.ReadableStream | null` that represents the child process's stderr.
 | 
				
			||||||
 | 
					If the child was spawned with options.stdio[2] set to anything other than 'pipe', then this will be `null`.
 | 
				
			||||||
 | 
					When the child process exits, then the value is `null` after the `exit` event is emitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Instance Events
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Event: 'spawn'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Emitted once the child process has spawned successfully.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Event: 'exit'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `code` number - Contains the exit code for
 | 
				
			||||||
 | 
					the process obtained from waitpid on posix, or GetExitCodeProcess on windows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Emitted after the child process ends.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Event: 'message'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Returns:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `message` any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Emitted when the child process sends a message using [`process.parentPort.postMessage()`](process.md#processparentport).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[`child_process.fork`]: https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_processforkmodulepath-args-options
 | 
				
			||||||
 | 
					[Services API]: https://chromium.googlesource.com/chromium/src/+/master/docs/mojo_and_services.md
 | 
				
			||||||
 | 
					[stdio]: https://nodejs.org/dist/latest/docs/api/child_process.html#optionsstdio
 | 
				
			||||||
| 
						 | 
					@ -194,6 +194,15 @@ overly prescriptive about how it should be used. Userland enables users to
 | 
				
			||||||
create and share tools that provide additional functionality on top of what is
 | 
					create and share tools that provide additional functionality on top of what is
 | 
				
			||||||
available in "core".
 | 
					available in "core".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### utility process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The utility process is a child of the main process that allows running any
 | 
				
			||||||
 | 
					untrusted services that cannot be run in the main process. Chromium uses this
 | 
				
			||||||
 | 
					process to perform network I/O, audio/video processing, device inputs etc.
 | 
				
			||||||
 | 
					In Electron, you can create this process using [UtilityProcess][] API.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See also: [process](#process), [main process](#main-process)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### V8
 | 
					### V8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
V8 is Google's open source JavaScript engine. It is written in C++ and is
 | 
					V8 is Google's open source JavaScript engine. It is written in C++ and is
 | 
				
			||||||
| 
						 | 
					@ -231,4 +240,5 @@ embedded content.
 | 
				
			||||||
[renderer]: #renderer-process
 | 
					[renderer]: #renderer-process
 | 
				
			||||||
[userland]: #userland
 | 
					[userland]: #userland
 | 
				
			||||||
[using native node modules]: tutorial/using-native-node-modules.md
 | 
					[using native node modules]: tutorial/using-native-node-modules.md
 | 
				
			||||||
 | 
					[UtilityProcess]: api/utility-process.md
 | 
				
			||||||
[v8]: #v8
 | 
					[v8]: #v8
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +36,7 @@ auto_filenames = {
 | 
				
			||||||
    "docs/api/net-log.md",
 | 
					    "docs/api/net-log.md",
 | 
				
			||||||
    "docs/api/net.md",
 | 
					    "docs/api/net.md",
 | 
				
			||||||
    "docs/api/notification.md",
 | 
					    "docs/api/notification.md",
 | 
				
			||||||
 | 
					    "docs/api/parent-port.md",
 | 
				
			||||||
    "docs/api/power-monitor.md",
 | 
					    "docs/api/power-monitor.md",
 | 
				
			||||||
    "docs/api/power-save-blocker.md",
 | 
					    "docs/api/power-save-blocker.md",
 | 
				
			||||||
    "docs/api/process.md",
 | 
					    "docs/api/process.md",
 | 
				
			||||||
| 
						 | 
					@ -62,6 +63,7 @@ auto_filenames = {
 | 
				
			||||||
    "docs/api/touch-bar-spacer.md",
 | 
					    "docs/api/touch-bar-spacer.md",
 | 
				
			||||||
    "docs/api/touch-bar.md",
 | 
					    "docs/api/touch-bar.md",
 | 
				
			||||||
    "docs/api/tray.md",
 | 
					    "docs/api/tray.md",
 | 
				
			||||||
 | 
					    "docs/api/utility-process.md",
 | 
				
			||||||
    "docs/api/web-contents.md",
 | 
					    "docs/api/web-contents.md",
 | 
				
			||||||
    "docs/api/web-frame-main.md",
 | 
					    "docs/api/web-frame-main.md",
 | 
				
			||||||
    "docs/api/web-frame.md",
 | 
					    "docs/api/web-frame.md",
 | 
				
			||||||
| 
						 | 
					@ -220,6 +222,7 @@ auto_filenames = {
 | 
				
			||||||
    "lib/browser/api/system-preferences.ts",
 | 
					    "lib/browser/api/system-preferences.ts",
 | 
				
			||||||
    "lib/browser/api/touch-bar.ts",
 | 
					    "lib/browser/api/touch-bar.ts",
 | 
				
			||||||
    "lib/browser/api/tray.ts",
 | 
					    "lib/browser/api/tray.ts",
 | 
				
			||||||
 | 
					    "lib/browser/api/utility-process.ts",
 | 
				
			||||||
    "lib/browser/api/view.ts",
 | 
					    "lib/browser/api/view.ts",
 | 
				
			||||||
    "lib/browser/api/views/image-view.ts",
 | 
					    "lib/browser/api/views/image-view.ts",
 | 
				
			||||||
    "lib/browser/api/web-contents-view.ts",
 | 
					    "lib/browser/api/web-contents-view.ts",
 | 
				
			||||||
| 
						 | 
					@ -331,4 +334,20 @@ auto_filenames = {
 | 
				
			||||||
    "typings/internal-ambient.d.ts",
 | 
					    "typings/internal-ambient.d.ts",
 | 
				
			||||||
    "typings/internal-electron.d.ts",
 | 
					    "typings/internal-electron.d.ts",
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  utility_bundle_deps = [
 | 
				
			||||||
 | 
					    "lib/browser/message-port-main.ts",
 | 
				
			||||||
 | 
					    "lib/common/define-properties.ts",
 | 
				
			||||||
 | 
					    "lib/common/init.ts",
 | 
				
			||||||
 | 
					    "lib/common/reset-search-paths.ts",
 | 
				
			||||||
 | 
					    "lib/utility/api/exports/electron.ts",
 | 
				
			||||||
 | 
					    "lib/utility/api/module-list.ts",
 | 
				
			||||||
 | 
					    "lib/utility/init.ts",
 | 
				
			||||||
 | 
					    "lib/utility/parent-port.ts",
 | 
				
			||||||
 | 
					    "package.json",
 | 
				
			||||||
 | 
					    "tsconfig.electron.json",
 | 
				
			||||||
 | 
					    "tsconfig.json",
 | 
				
			||||||
 | 
					    "typings/internal-ambient.d.ts",
 | 
				
			||||||
 | 
					    "typings/internal-electron.d.ts",
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -311,6 +311,8 @@ filenames = {
 | 
				
			||||||
    "shell/browser/api/electron_api_tray.h",
 | 
					    "shell/browser/api/electron_api_tray.h",
 | 
				
			||||||
    "shell/browser/api/electron_api_url_loader.cc",
 | 
					    "shell/browser/api/electron_api_url_loader.cc",
 | 
				
			||||||
    "shell/browser/api/electron_api_url_loader.h",
 | 
					    "shell/browser/api/electron_api_url_loader.h",
 | 
				
			||||||
 | 
					    "shell/browser/api/electron_api_utility_process.cc",
 | 
				
			||||||
 | 
					    "shell/browser/api/electron_api_utility_process.h",
 | 
				
			||||||
    "shell/browser/api/electron_api_view.cc",
 | 
					    "shell/browser/api/electron_api_view.cc",
 | 
				
			||||||
    "shell/browser/api/electron_api_view.h",
 | 
					    "shell/browser/api/electron_api_view.h",
 | 
				
			||||||
    "shell/browser/api/electron_api_web_contents.cc",
 | 
					    "shell/browser/api/electron_api_web_contents.cc",
 | 
				
			||||||
| 
						 | 
					@ -679,6 +681,10 @@ filenames = {
 | 
				
			||||||
    "shell/renderer/renderer_client_base.h",
 | 
					    "shell/renderer/renderer_client_base.h",
 | 
				
			||||||
    "shell/renderer/web_worker_observer.cc",
 | 
					    "shell/renderer/web_worker_observer.cc",
 | 
				
			||||||
    "shell/renderer/web_worker_observer.h",
 | 
					    "shell/renderer/web_worker_observer.h",
 | 
				
			||||||
 | 
					    "shell/services/node/node_service.cc",
 | 
				
			||||||
 | 
					    "shell/services/node/node_service.h",
 | 
				
			||||||
 | 
					    "shell/services/node/parent_port.cc",
 | 
				
			||||||
 | 
					    "shell/services/node/parent_port.h",
 | 
				
			||||||
    "shell/utility/electron_content_utility_client.cc",
 | 
					    "shell/utility/electron_content_utility_client.cc",
 | 
				
			||||||
    "shell/utility/electron_content_utility_client.h",
 | 
					    "shell/utility/electron_content_utility_client.h",
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
 | 
				
			||||||
  { name: 'systemPreferences', loader: () => require('./system-preferences') },
 | 
					  { name: 'systemPreferences', loader: () => require('./system-preferences') },
 | 
				
			||||||
  { name: 'TouchBar', loader: () => require('./touch-bar') },
 | 
					  { name: 'TouchBar', loader: () => require('./touch-bar') },
 | 
				
			||||||
  { name: 'Tray', loader: () => require('./tray') },
 | 
					  { name: 'Tray', loader: () => require('./tray') },
 | 
				
			||||||
 | 
					  { name: 'utilityProcess', loader: () => require('./utility-process') },
 | 
				
			||||||
  { name: 'View', loader: () => require('./view') },
 | 
					  { name: 'View', loader: () => require('./view') },
 | 
				
			||||||
  { name: 'webContents', loader: () => require('./web-contents') },
 | 
					  { name: 'webContents', loader: () => require('./web-contents') },
 | 
				
			||||||
  { name: 'WebContentsView', loader: () => require('./web-contents-view') },
 | 
					  { name: 'WebContentsView', loader: () => require('./web-contents-view') },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										150
									
								
								lib/browser/api/utility-process.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								lib/browser/api/utility-process.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,150 @@
 | 
				
			||||||
 | 
					import { EventEmitter } from 'events';
 | 
				
			||||||
 | 
					import { Duplex, PassThrough } from 'stream';
 | 
				
			||||||
 | 
					import { Socket } from 'net';
 | 
				
			||||||
 | 
					import { MessagePortMain } from '@electron/internal/browser/message-port-main';
 | 
				
			||||||
 | 
					const { _fork } = process._linkedBinding('electron_browser_utility_process');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ForkUtilityProcess extends EventEmitter {
 | 
				
			||||||
 | 
					  #handle: ElectronInternal.UtilityProcessWrapper | null;
 | 
				
			||||||
 | 
					  #stdout: Duplex | null = null;
 | 
				
			||||||
 | 
					  #stderr: Duplex | null = null;
 | 
				
			||||||
 | 
					  constructor (modulePath: string, args?: string[], options?: Electron.ForkOptions) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!modulePath) {
 | 
				
			||||||
 | 
					      throw new Error('Missing UtilityProcess entry script.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (args == null) {
 | 
				
			||||||
 | 
					      args = [];
 | 
				
			||||||
 | 
					    } else if (typeof args === 'object' && !Array.isArray(args)) {
 | 
				
			||||||
 | 
					      options = args;
 | 
				
			||||||
 | 
					      args = [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (options == null) {
 | 
				
			||||||
 | 
					      options = {};
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      options = { ...options };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!options) {
 | 
				
			||||||
 | 
					      throw new Error('Options cannot be undefined.');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (options.execArgv != null) {
 | 
				
			||||||
 | 
					      if (!Array.isArray(options.execArgv)) {
 | 
				
			||||||
 | 
					        throw new Error('execArgv must be an array of strings.');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (options.serviceName != null) {
 | 
				
			||||||
 | 
					      if (typeof options.serviceName !== 'string') {
 | 
				
			||||||
 | 
					        throw new Error('serviceName must be a string.');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (options.cwd != null) {
 | 
				
			||||||
 | 
					      if (typeof options.cwd !== 'string') {
 | 
				
			||||||
 | 
					        throw new Error('cwd path must be a string.');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (typeof options.stdio === 'string') {
 | 
				
			||||||
 | 
					      const stdio : Array<'pipe' | 'ignore' | 'inherit'> = [];
 | 
				
			||||||
 | 
					      switch (options.stdio) {
 | 
				
			||||||
 | 
					        case 'inherit':
 | 
				
			||||||
 | 
					        case 'ignore':
 | 
				
			||||||
 | 
					          stdio.push('ignore', options.stdio, options.stdio);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case 'pipe':
 | 
				
			||||||
 | 
					          this.#stderr = new PassThrough();
 | 
				
			||||||
 | 
					          this.#stdout = new PassThrough();
 | 
				
			||||||
 | 
					          stdio.push('ignore', options.stdio, options.stdio);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          throw new Error('stdio must be of the following values: inherit, pipe, ignore');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      options.stdio = stdio;
 | 
				
			||||||
 | 
					    } else if (Array.isArray(options.stdio)) {
 | 
				
			||||||
 | 
					      if (options.stdio.length >= 3) {
 | 
				
			||||||
 | 
					        if (options.stdio[0] !== 'ignore') {
 | 
				
			||||||
 | 
					          throw new Error('stdin value other than ignore is not supported.');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (options.stdio[1] === 'pipe') {
 | 
				
			||||||
 | 
					          this.#stdout = new PassThrough();
 | 
				
			||||||
 | 
					        } else if (options.stdio[1] !== 'ignore' && options.stdio[1] !== 'inherit') {
 | 
				
			||||||
 | 
					          throw new Error('stdout configuration must be of the following values: inherit, pipe, ignore');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (options.stdio[2] === 'pipe') {
 | 
				
			||||||
 | 
					          this.#stderr = new PassThrough();
 | 
				
			||||||
 | 
					        } else if (options.stdio[2] !== 'ignore' && options.stdio[2] !== 'inherit') {
 | 
				
			||||||
 | 
					          throw new Error('stderr configuration must be of the following values: inherit, pipe, ignore');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new Error('configuration missing for stdin, stdout or stderr.');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.#handle = _fork({ options, modulePath, args });
 | 
				
			||||||
 | 
					    this.#handle!.emit = (channel: string | symbol, ...args: any[]) => {
 | 
				
			||||||
 | 
					      if (channel === 'exit') {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          this.emit('exit', ...args);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					          this.#handle = null;
 | 
				
			||||||
 | 
					          if (this.#stdout) {
 | 
				
			||||||
 | 
					            this.#stdout.removeAllListeners();
 | 
				
			||||||
 | 
					            this.#stdout = null;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (this.#stderr) {
 | 
				
			||||||
 | 
					            this.#stderr.removeAllListeners();
 | 
				
			||||||
 | 
					            this.#stderr = null;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      } else if (channel === 'stdout' && this.#stdout) {
 | 
				
			||||||
 | 
					        new Socket({ fd: args[0], readable: true }).pipe(this.#stdout);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      } else if (channel === 'stderr' && this.#stderr) {
 | 
				
			||||||
 | 
					        new Socket({ fd: args[0], readable: true }).pipe(this.#stderr);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return this.emit(channel, ...args);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get pid () {
 | 
				
			||||||
 | 
					    return this.#handle?.pid;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get stdout () {
 | 
				
			||||||
 | 
					    return this.#stdout;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get stderr () {
 | 
				
			||||||
 | 
					    return this.#stderr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  postMessage (message: any, transfer?: MessagePortMain[]) {
 | 
				
			||||||
 | 
					    if (Array.isArray(transfer)) {
 | 
				
			||||||
 | 
					      transfer = transfer.map((o: any) => o instanceof MessagePortMain ? o._internalPort : o);
 | 
				
			||||||
 | 
					      return this.#handle?.postMessage(message, transfer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return this.#handle?.postMessage(message);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  kill () : boolean {
 | 
				
			||||||
 | 
					    if (this.#handle === null) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return this.#handle.kill();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fork (modulePath: string, args?: string[], options?: Electron.ForkOptions) {
 | 
				
			||||||
 | 
					  return new ForkUtilityProcess(modulePath, args, options);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -33,20 +33,29 @@ function wrap <T extends AnyFn> (func: T, wrapper: (fn: AnyFn) => T) {
 | 
				
			||||||
  return wrapped;
 | 
					  return wrapped;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// process.nextTick and setImmediate make use of uv_check and uv_prepare to
 | 
				
			||||||
 | 
					// run the callbacks, however since we only run uv loop on requests, the
 | 
				
			||||||
 | 
					// callbacks wouldn't be called until something else activated the uv loop,
 | 
				
			||||||
 | 
					// which would delay the callbacks for arbitrary long time. So we should
 | 
				
			||||||
 | 
					// initiatively activate the uv loop once process.nextTick and setImmediate is
 | 
				
			||||||
 | 
					// called.
 | 
				
			||||||
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
 | 
					process.nextTick = wrapWithActivateUvLoop(process.nextTick);
 | 
				
			||||||
 | 
					 | 
				
			||||||
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
 | 
					global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
 | 
				
			||||||
global.clearImmediate = timers.clearImmediate;
 | 
					global.clearImmediate = timers.clearImmediate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setTimeout needs to update the polling timeout of the event loop, when
 | 
					// setTimeout needs to update the polling timeout of the event loop, when
 | 
				
			||||||
// called under Chromium's event loop the node's event loop won't get a chance
 | 
					// called under Chromium's event loop the node's event loop won't get a chance
 | 
				
			||||||
// to update the timeout, so we have to force the node's event loop to
 | 
					// to update the timeout, so we have to force the node's event loop to
 | 
				
			||||||
// recalculate the timeout in browser process.
 | 
					// recalculate the timeout in the process.
 | 
				
			||||||
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
 | 
					timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
 | 
				
			||||||
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
 | 
					timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Only override the global setTimeout/setInterval impls in the browser process
 | 
					// Update the global version of the timer apis to use the above wrapper
 | 
				
			||||||
if (process.type === 'browser') {
 | 
					// only in the process that runs node event loop alongside chromium
 | 
				
			||||||
 | 
					// event loop. We skip renderer with nodeIntegration here because node globals
 | 
				
			||||||
 | 
					// are deleted in these processes, see renderer/init.js for reference.
 | 
				
			||||||
 | 
					if (process.type === 'browser' ||
 | 
				
			||||||
 | 
					    process.type === 'utility') {
 | 
				
			||||||
  global.setTimeout = timers.setTimeout;
 | 
					  global.setTimeout = timers.setTimeout;
 | 
				
			||||||
  global.setInterval = timers.setInterval;
 | 
					  global.setInterval = timers.setInterval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								lib/utility/.eslintrc.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lib/utility/.eslintrc.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "rules": {
 | 
				
			||||||
 | 
					    "no-restricted-imports": [
 | 
				
			||||||
 | 
					      "error",
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "paths": [
 | 
				
			||||||
 | 
					          "electron",
 | 
				
			||||||
 | 
					          "electron/renderer"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "patterns": [
 | 
				
			||||||
 | 
					          "./*",
 | 
				
			||||||
 | 
					          "../*",
 | 
				
			||||||
 | 
					          "@electron/internal/isolated_renderer/*",
 | 
				
			||||||
 | 
					          "@electron/internal/renderer/*",
 | 
				
			||||||
 | 
					          "@electron/internal/sandboxed_worker/*",
 | 
				
			||||||
 | 
					          "@electron/internal/worker/*"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								lib/utility/api/exports/electron.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								lib/utility/api/exports/electron.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import { defineProperties } from '@electron/internal/common/define-properties';
 | 
				
			||||||
 | 
					import { utilityNodeModuleList } from '@electron/internal/utility/api/module-list';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					defineProperties(module.exports, utilityNodeModuleList);
 | 
				
			||||||
							
								
								
									
										2
									
								
								lib/utility/api/module-list.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								lib/utility/api/module-list.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					// Utility side modules, please sort alphabetically.
 | 
				
			||||||
 | 
					export const utilityNodeModuleList: ElectronInternal.ModuleEntry[] = [];
 | 
				
			||||||
							
								
								
									
										38
									
								
								lib/utility/init.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/utility/init.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					import { ParentPort } from '@electron/internal/utility/parent-port';
 | 
				
			||||||
 | 
					const Module = require('module');
 | 
				
			||||||
 | 
					const v8Util = process._linkedBinding('electron_common_v8_util');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const entryScript: string = v8Util.getHiddenValue(process, '_serviceStartupScript');
 | 
				
			||||||
 | 
					// We modified the original process.argv to let node.js load the init.js,
 | 
				
			||||||
 | 
					// we need to restore it here.
 | 
				
			||||||
 | 
					process.argv.splice(1, 1, entryScript);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clear search paths.
 | 
				
			||||||
 | 
					require('../common/reset-search-paths');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Import common settings.
 | 
				
			||||||
 | 
					require('@electron/internal/common/init');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const parentPort: ParentPort = new ParentPort();
 | 
				
			||||||
 | 
					Object.defineProperty(process, 'parentPort', {
 | 
				
			||||||
 | 
					  enumerable: true,
 | 
				
			||||||
 | 
					  writable: false,
 | 
				
			||||||
 | 
					  value: parentPort
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Based on third_party/electron_node/lib/internal/worker/io.js
 | 
				
			||||||
 | 
					parentPort.on('newListener', (name: string) => {
 | 
				
			||||||
 | 
					  if (name === 'message' && parentPort.listenerCount('message') === 0) {
 | 
				
			||||||
 | 
					    parentPort.start();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					parentPort.on('removeListener', (name: string) => {
 | 
				
			||||||
 | 
					  if (name === 'message' && parentPort.listenerCount('message') === 0) {
 | 
				
			||||||
 | 
					    parentPort.pause();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Finally load entry script.
 | 
				
			||||||
 | 
					process._firstFileName = Module._resolveFilename(entryScript, null, false);
 | 
				
			||||||
 | 
					Module._load(entryScript, Module, true);
 | 
				
			||||||
							
								
								
									
										30
									
								
								lib/utility/parent-port.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/utility/parent-port.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					import { EventEmitter } from 'events';
 | 
				
			||||||
 | 
					import { MessagePortMain } from '@electron/internal/browser/message-port-main';
 | 
				
			||||||
 | 
					const { createParentPort } = process._linkedBinding('electron_utility_parent_port');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ParentPort extends EventEmitter {
 | 
				
			||||||
 | 
					  #port: ParentPort
 | 
				
			||||||
 | 
					  constructor () {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.#port = createParentPort();
 | 
				
			||||||
 | 
					    this.#port.emit = (channel: string | symbol, event: { ports: any[] }) => {
 | 
				
			||||||
 | 
					      if (channel === 'message') {
 | 
				
			||||||
 | 
					        event = { ...event, ports: event.ports.map(p => new MessagePortMain(p)) };
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.emit(channel, event);
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  start () : void {
 | 
				
			||||||
 | 
					    this.#port.start();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pause () : void {
 | 
				
			||||||
 | 
					    this.#port.pause();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  postMessage (message: any) : void {
 | 
				
			||||||
 | 
					    this.#port.postMessage(message);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -115,6 +115,7 @@ add_electron_deps_to_license_credits_file.patch
 | 
				
			||||||
fix_crash_loading_non-standard_schemes_in_iframes.patch
 | 
					fix_crash_loading_non-standard_schemes_in_iframes.patch
 | 
				
			||||||
fix_return_v8_value_from_localframe_requestexecutescript.patch
 | 
					fix_return_v8_value_from_localframe_requestexecutescript.patch
 | 
				
			||||||
create_browser_v8_snapshot_file_name_fuse.patch
 | 
					create_browser_v8_snapshot_file_name_fuse.patch
 | 
				
			||||||
 | 
					feat_configure_launch_options_for_service_process.patch
 | 
				
			||||||
feat_ensure_mas_builds_of_the_same_application_can_use_safestorage.patch
 | 
					feat_ensure_mas_builds_of_the_same_application_can_use_safestorage.patch
 | 
				
			||||||
fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch
 | 
					fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch
 | 
				
			||||||
preconnect_manager.patch
 | 
					preconnect_manager.patch
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,28 +3,28 @@ From: Jeremy Apthorp <nornagon@nornagon.net>
 | 
				
			||||||
Date: Mon, 26 Aug 2019 12:02:51 -0700
 | 
					Date: Mon, 26 Aug 2019 12:02:51 -0700
 | 
				
			||||||
Subject: allow new privileges in unsandboxed child processes
 | 
					Subject: allow new privileges in unsandboxed child processes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This allows unsandboxed renderers to launch setuid processes on Linux.
 | 
					This allows unsandboxed child process to launch setuid processes on Linux.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
 | 
					diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
 | 
				
			||||||
index dd5ccfc0bdc2e071999d1bf864dc065dd1311407..7464e84f6e610749dce5c3a46afce262f29020cc 100644
 | 
					index dd5ccfc0bdc2e071999d1bf864dc065dd1311407..cfadd28fca9f80bf57578db78d5472c4f75414e1 100644
 | 
				
			||||||
--- a/content/browser/child_process_launcher_helper_linux.cc
 | 
					--- a/content/browser/child_process_launcher_helper_linux.cc
 | 
				
			||||||
+++ b/content/browser/child_process_launcher_helper_linux.cc
 | 
					+++ b/content/browser/child_process_launcher_helper_linux.cc
 | 
				
			||||||
@@ -54,6 +54,18 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
 | 
					@@ -56,6 +56,18 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
 | 
				
			||||||
   if (GetProcessType() == switches::kRendererProcess) {
 | 
					 | 
				
			||||||
     const int sandbox_fd = SandboxHostLinux::GetInstance()->GetChildSocket();
 | 
					 | 
				
			||||||
     options->fds_to_remap.push_back(std::make_pair(sandbox_fd, GetSandboxFD()));
 | 
					     options->fds_to_remap.push_back(std::make_pair(sandbox_fd, GetSandboxFD()));
 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+    // (For Electron), if we're launching without zygote, that means we're
 | 
					 | 
				
			||||||
+    // launching an unsandboxed process (since all sandboxed processes are
 | 
					 | 
				
			||||||
+    // forked from the zygote). Relax the allow_new_privs option to permit
 | 
					 | 
				
			||||||
+    // launching suid processes from unsandboxed renderers.
 | 
					 | 
				
			||||||
+    ZygoteHandle zygote_handle =
 | 
					 | 
				
			||||||
+        base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote)
 | 
					 | 
				
			||||||
+            ? nullptr
 | 
					 | 
				
			||||||
+            : delegate_->GetZygote();
 | 
					 | 
				
			||||||
+    if (!zygote_handle) {
 | 
					 | 
				
			||||||
+      options->allow_new_privs = true;
 | 
					 | 
				
			||||||
+    }
 | 
					 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
 | 
					+  // (For Electron), if we're launching without zygote, that means we're
 | 
				
			||||||
 | 
					+  // launching an unsandboxed process (since all sandboxed processes are
 | 
				
			||||||
 | 
					+  // forked from the zygote). Relax the allow_new_privs option to permit
 | 
				
			||||||
 | 
					+  // launching suid processes from unsandboxed child processes.
 | 
				
			||||||
 | 
					+  ZygoteHandle zygote_handle =
 | 
				
			||||||
 | 
					+      base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote)
 | 
				
			||||||
 | 
					+          ? nullptr
 | 
				
			||||||
 | 
					+          : delegate_->GetZygote();
 | 
				
			||||||
 | 
					+  if (!zygote_handle) {
 | 
				
			||||||
 | 
					+    options->allow_new_privs = true;
 | 
				
			||||||
 | 
					+  }
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
   for (const auto& remapped_fd : file_data_->additional_remapped_fds) {
 | 
					   for (const auto& remapped_fd : file_data_->additional_remapped_fds) {
 | 
				
			||||||
 | 
					     options->fds_to_remap.emplace_back(remapped_fd.second.get(),
 | 
				
			||||||
 | 
					                                        remapped_fd.first);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,671 @@
 | 
				
			||||||
 | 
					From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 | 
				
			||||||
 | 
					From: deepak1556 <hop2deep@gmail.com>
 | 
				
			||||||
 | 
					Date: Wed, 17 Aug 2022 22:04:47 +0900
 | 
				
			||||||
 | 
					Subject: feat: configure launch options for service process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- POSIX:
 | 
				
			||||||
 | 
					  Allows configuring base::LaunchOptions::fds_to_remap when launching the child process.
 | 
				
			||||||
 | 
					- Win:
 | 
				
			||||||
 | 
					  Allows configuring base::LaunchOptions::handles_to_inherit, base::LaunchOptions::stdout_handle
 | 
				
			||||||
 | 
					  and base::LaunchOptions::stderr_handle when launching the child process.
 | 
				
			||||||
 | 
					- All:
 | 
				
			||||||
 | 
					  Allows configuring base::LauncOptions::current_directory, base::LaunchOptions::enviroment
 | 
				
			||||||
 | 
					  and base::LaunchOptions::clear_environment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An example use of this option, UtilityProcess API allows reading the output From
 | 
				
			||||||
 | 
					stdout and stderr of child process by creating a pipe, whose write end is remapped
 | 
				
			||||||
 | 
					to STDOUT_FILENO/STD_OUTPUT_HANDLE and STDERR_FILENO/STD_ERROR_HANDLE allowing the
 | 
				
			||||||
 | 
					parent process to read from the pipe.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
 | 
				
			||||||
 | 
					index ba1f0d6e958cdb534b8af7717a0d6d8f2ee296bf..626f771ffbd88f1cf2e9475b745456f98575cda1 100644
 | 
				
			||||||
 | 
					--- a/content/browser/child_process_launcher.h
 | 
				
			||||||
 | 
					+++ b/content/browser/child_process_launcher.h
 | 
				
			||||||
 | 
					@@ -31,6 +31,7 @@
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					 #include "base/win/windows_types.h"
 | 
				
			||||||
 | 
					+#include "base/win/scoped_handle.h"
 | 
				
			||||||
 | 
					 #endif
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					@@ -163,7 +164,10 @@ struct ChildProcessLauncherFileData {
 | 
				
			||||||
 | 
					       delete;
 | 
				
			||||||
 | 
					   ~ChildProcessLauncherFileData();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+  base::win::ScopedHandle stdout_handle;
 | 
				
			||||||
 | 
					+  base::win::ScopedHandle stderr_handle;
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					   // Files opened by the browser and passed as corresponding file descriptors
 | 
				
			||||||
 | 
					   // in the child process.
 | 
				
			||||||
 | 
					   // Currently only supported on Linux, ChromeOS and Android platforms.
 | 
				
			||||||
 | 
					diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
 | 
				
			||||||
 | 
					index cfadd28fca9f80bf57578db78d5472c4f75414e1..4925dc5cafbf312c3c9640d5873d62193e87f636 100644
 | 
				
			||||||
 | 
					--- a/content/browser/child_process_launcher_helper_linux.cc
 | 
				
			||||||
 | 
					+++ b/content/browser/child_process_launcher_helper_linux.cc
 | 
				
			||||||
 | 
					@@ -73,7 +73,9 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
 | 
				
			||||||
 | 
					                                        remapped_fd.first);
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+  options->current_directory = delegate_->GetCurrentDirectory();
 | 
				
			||||||
 | 
					   options->environment = delegate_->GetEnvironment();
 | 
				
			||||||
 | 
					+  options->clear_environment = !delegate_->ShouldInheritEnvironment();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   return true;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
 | 
				
			||||||
 | 
					index d74a40c0e5731281b132cc1c3dc2416f9dc2b083..dd8a9d35af617441c6643ed643b459a35b612969 100644
 | 
				
			||||||
 | 
					--- a/content/browser/child_process_launcher_helper_mac.cc
 | 
				
			||||||
 | 
					+++ b/content/browser/child_process_launcher_helper_mac.cc
 | 
				
			||||||
 | 
					@@ -73,7 +73,8 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
 | 
				
			||||||
 | 
					       'mojo', base::MachRendezvousPort(endpoint.TakeMachReceiveRight())));
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   options->environment = delegate_->GetEnvironment();
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					+  options->clear_environment = !delegate_->ShouldInheritEnvironment();
 | 
				
			||||||
 | 
					+  options->current_directory = delegate_->GetCurrentDirectory();
 | 
				
			||||||
 | 
					   options->disclaim_responsibility = delegate_->DisclaimResponsibility();
 | 
				
			||||||
 | 
					   options->enable_cpu_security_mitigations =
 | 
				
			||||||
 | 
					       delegate_->EnableCpuSecurityMitigations();
 | 
				
			||||||
 | 
					diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
 | 
				
			||||||
 | 
					index 799ad0a6e0b5c629d10f481d10dd4d6959d40b42..13c610ae1bb24fb6d274a082562dcd103df50513 100644
 | 
				
			||||||
 | 
					--- a/content/browser/child_process_launcher_helper_win.cc
 | 
				
			||||||
 | 
					+++ b/content/browser/child_process_launcher_helper_win.cc
 | 
				
			||||||
 | 
					@@ -19,6 +19,8 @@
 | 
				
			||||||
 | 
					 #include "sandbox/policy/win/sandbox_win.h"
 | 
				
			||||||
 | 
					 #include "sandbox/win/src/sandbox_types.h"
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#include <windows.h>
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 namespace content {
 | 
				
			||||||
 | 
					 namespace internal {
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -54,6 +56,30 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
 | 
				
			||||||
 | 
					     mojo_channel_->PrepareToPassRemoteEndpoint(&options->handles_to_inherit,
 | 
				
			||||||
 | 
					                                                command_line());
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  if (file_data_->stdout_handle.IsValid() || file_data_->stderr_handle.IsValid()) {
 | 
				
			||||||
 | 
					+    // base::LaunchProcess requires that if any of the stdio handle is customized then
 | 
				
			||||||
 | 
					+    // the other two handles should also be set.
 | 
				
			||||||
 | 
					+    // https://source.chromium.org/chromium/chromium/src/+/main:base/process/launch_win.cc;l=341-350
 | 
				
			||||||
 | 
					+    options->stdin_handle = INVALID_HANDLE_VALUE;
 | 
				
			||||||
 | 
					+    if (file_data_->stdout_handle.IsValid()) {
 | 
				
			||||||
 | 
					+      options->stdout_handle = file_data_->stdout_handle.get();
 | 
				
			||||||
 | 
					+    } else {
 | 
				
			||||||
 | 
					+      options->stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
 | 
				
			||||||
 | 
					+    }
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+    if (file_data_->stderr_handle.IsValid()) {
 | 
				
			||||||
 | 
					+      options->stderr_handle = file_data_->stderr_handle.get();
 | 
				
			||||||
 | 
					+    } else {
 | 
				
			||||||
 | 
					+      options->stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
 | 
				
			||||||
 | 
					+    }
 | 
				
			||||||
 | 
					+    options->handles_to_inherit.push_back(options->stdout_handle);
 | 
				
			||||||
 | 
					+    options->handles_to_inherit.push_back(options->stderr_handle);
 | 
				
			||||||
 | 
					+  }
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  options->current_directory = delegate_->GetCurrentDirectory();
 | 
				
			||||||
 | 
					+  options->environment = delegate_->GetEnvironment();
 | 
				
			||||||
 | 
					+  options->clear_environment = !delegate_->ShouldInheritEnvironment();
 | 
				
			||||||
 | 
					   return true;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -81,7 +107,7 @@ ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
 | 
				
			||||||
 | 
					   ChildProcessLauncherHelper::Process process;
 | 
				
			||||||
 | 
					   *launch_result =
 | 
				
			||||||
 | 
					       StartSandboxedProcess(delegate_.get(), *command_line(),
 | 
				
			||||||
 | 
					-                            options.handles_to_inherit, &process.process);
 | 
				
			||||||
 | 
					+                            options, &process.process);
 | 
				
			||||||
 | 
					   return process;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
 | 
				
			||||||
 | 
					index e547f42bc0d06b485797ccc1605969259631831f..0f3041f4a5b636440d9579303721f2ae7e1855c6 100644
 | 
				
			||||||
 | 
					--- a/content/browser/service_process_host_impl.cc
 | 
				
			||||||
 | 
					+++ b/content/browser/service_process_host_impl.cc
 | 
				
			||||||
 | 
					@@ -190,6 +190,15 @@ void LaunchServiceProcess(mojo::GenericPendingReceiver receiver,
 | 
				
			||||||
 | 
					   host->SetExtraCommandLineSwitches(std::move(options.extra_switches));
 | 
				
			||||||
 | 
					   if (options.child_flags)
 | 
				
			||||||
 | 
					     host->set_child_flags(*options.child_flags);
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+  host->SetStdioHandles(std::move(options.stdout_handle), std::move(options.stderr_handle));
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+  host->SetAdditionalFds(std::move(options.fds_to_remap));
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+  host->SetCurrentDirectory(options.current_directory);
 | 
				
			||||||
 | 
					+  host->SetEnv(options.environment);
 | 
				
			||||||
 | 
					+  if (options.clear_environment)
 | 
				
			||||||
 | 
					+    host->ClearEnvironment();
 | 
				
			||||||
 | 
					   host->Start();
 | 
				
			||||||
 | 
					   host->GetChildProcess()->BindServiceInterface(std::move(receiver));
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
 | 
				
			||||||
 | 
					index 171b8440c25580d717f87c4f68bd8f4734b5fcf1..35826081dc3fc2f17fd7ceaf25c2c014ae623304 100644
 | 
				
			||||||
 | 
					--- a/content/browser/utility_process_host.cc
 | 
				
			||||||
 | 
					+++ b/content/browser/utility_process_host.cc
 | 
				
			||||||
 | 
					@@ -108,11 +108,13 @@ const ChildProcessData& UtilityProcessHost::GetData() {
 | 
				
			||||||
 | 
					   return process_->GetData();
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					 void UtilityProcessHost::SetEnv(const base::EnvironmentMap& env) {
 | 
				
			||||||
 | 
					   env_ = env;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					-#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+void UtilityProcessHost::ClearEnvironment() {
 | 
				
			||||||
 | 
					+  inherit_environment_ = false;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 bool UtilityProcessHost::Start() {
 | 
				
			||||||
 | 
					   return StartProcess();
 | 
				
			||||||
 | 
					@@ -153,6 +155,24 @@ void UtilityProcessHost::SetExtraCommandLineSwitches(
 | 
				
			||||||
 | 
					   extra_switches_ = std::move(switches);
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+void UtilityProcessHost::SetStdioHandles(
 | 
				
			||||||
 | 
					+    base::win::ScopedHandle stdout_handle,
 | 
				
			||||||
 | 
					+    base::win::ScopedHandle stderr_handle) {
 | 
				
			||||||
 | 
					+  stdout_handle_ = std::move(stdout_handle);
 | 
				
			||||||
 | 
					+  stderr_handle_ = std::move(stderr_handle);
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+void UtilityProcessHost::SetAdditionalFds(base::FileHandleMappingVector mapping) {
 | 
				
			||||||
 | 
					+  fds_to_remap_ = std::move(mapping);
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+void UtilityProcessHost::SetCurrentDirectory(
 | 
				
			||||||
 | 
					+    const base::FilePath& cwd) {
 | 
				
			||||||
 | 
					+  current_directory_ = cwd;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 mojom::ChildProcess* UtilityProcessHost::GetChildProcess() {
 | 
				
			||||||
 | 
					   return static_cast<ChildProcessHostImpl*>(process_->GetHost())
 | 
				
			||||||
 | 
					       ->child_process();
 | 
				
			||||||
 | 
					@@ -356,9 +376,22 @@ bool UtilityProcessHost::StartProcess() {
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+    file_data->stdout_handle = std::move(stdout_handle_);
 | 
				
			||||||
 | 
					+    file_data->stderr_handle = std::move(stderr_handle_);
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+    if (!fds_to_remap_.empty()) {
 | 
				
			||||||
 | 
					+      for (const auto& remapped_fd : fds_to_remap_) {
 | 
				
			||||||
 | 
					+        file_data->additional_remapped_fds.emplace(
 | 
				
			||||||
 | 
					+            remapped_fd.second, remapped_fd.first);
 | 
				
			||||||
 | 
					+      }
 | 
				
			||||||
 | 
					+    }
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					     std::unique_ptr<UtilitySandboxedProcessLauncherDelegate> delegate =
 | 
				
			||||||
 | 
					         std::make_unique<UtilitySandboxedProcessLauncherDelegate>(
 | 
				
			||||||
 | 
					-            sandbox_type_, env_, *cmd_line);
 | 
				
			||||||
 | 
					+            sandbox_type_, env_, current_directory_, *cmd_line,
 | 
				
			||||||
 | 
					+            inherit_environment_);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					     process_->LaunchWithFileData(std::move(delegate), std::move(cmd_line),
 | 
				
			||||||
 | 
					                                  std::move(file_data), true);
 | 
				
			||||||
 | 
					diff --git a/content/browser/utility_process_host.h b/content/browser/utility_process_host.h
 | 
				
			||||||
 | 
					index 13de4795df7731f27760901aff17c143008a72c1..3b8af456d86e7aaf3b57e6b039c7f444e1c9e5fe 100644
 | 
				
			||||||
 | 
					--- a/content/browser/utility_process_host.h
 | 
				
			||||||
 | 
					+++ b/content/browser/utility_process_host.h
 | 
				
			||||||
 | 
					@@ -29,6 +29,10 @@
 | 
				
			||||||
 | 
					 #include "mojo/public/cpp/system/message_pipe.h"
 | 
				
			||||||
 | 
					 #endif
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+#include "base/win/scoped_handle.h"
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 namespace base {
 | 
				
			||||||
 | 
					 class Thread;
 | 
				
			||||||
 | 
					 }  // namespace base
 | 
				
			||||||
 | 
					@@ -87,9 +91,13 @@ class CONTENT_EXPORT UtilityProcessHost
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   // Returns information about the utility child process.
 | 
				
			||||||
 | 
					   const ChildProcessData& GetData();
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // Set/Unset environment variables.
 | 
				
			||||||
 | 
					   void SetEnv(const base::EnvironmentMap& env);
 | 
				
			||||||
 | 
					-#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // Clear the environment for the new process before processing
 | 
				
			||||||
 | 
					+  // changes from SetEnv.
 | 
				
			||||||
 | 
					+  void ClearEnvironment();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   // Starts the utility process.
 | 
				
			||||||
 | 
					   bool Start();
 | 
				
			||||||
 | 
					@@ -118,6 +126,16 @@ class CONTENT_EXPORT UtilityProcessHost
 | 
				
			||||||
 | 
					   // Provides extra switches to append to the process's command line.
 | 
				
			||||||
 | 
					   void SetExtraCommandLineSwitches(std::vector<std::string> switches);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+  void SetStdioHandles(base::win::ScopedHandle stdout_handle,
 | 
				
			||||||
 | 
					+                       base::win::ScopedHandle stderr_handle);
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+  void SetAdditionalFds(base::FileHandleMappingVector mapping);
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // Sets the working directory of the process.
 | 
				
			||||||
 | 
					+  void SetCurrentDirectory(const base::FilePath& cwd);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					   // Returns a control interface for the running child process.
 | 
				
			||||||
 | 
					   mojom::ChildProcess* GetChildProcess();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -159,6 +177,22 @@ class CONTENT_EXPORT UtilityProcessHost
 | 
				
			||||||
 | 
					   // Extra command line switches to append.
 | 
				
			||||||
 | 
					   std::vector<std::string> extra_switches_;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+  // Specifies the handles for redirection of stdout and stderr.
 | 
				
			||||||
 | 
					+  base::win::ScopedHandle stdout_handle_;
 | 
				
			||||||
 | 
					+  base::win::ScopedHandle stderr_handle_;
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+  // Specifies file descriptors to propagate into the child process
 | 
				
			||||||
 | 
					+  // based on the mapping.
 | 
				
			||||||
 | 
					+  base::FileHandleMappingVector fds_to_remap_;
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // If not empty, change to this directory before executing the new process.
 | 
				
			||||||
 | 
					+  base::FilePath current_directory_;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // Inherit enviroment from parent process.
 | 
				
			||||||
 | 
					+  bool inherit_environment_ = true;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					   // Indicates whether the process has been successfully launched yet, or if
 | 
				
			||||||
 | 
					   // launch failed.
 | 
				
			||||||
 | 
					   enum class LaunchState {
 | 
				
			||||||
 | 
					diff --git a/content/browser/utility_sandbox_delegate.cc b/content/browser/utility_sandbox_delegate.cc
 | 
				
			||||||
 | 
					index 070ee151ee96baa771cec6fe4de9f8762eff91bc..d7621b234e45f94a2ca8bc79f25345025b3bc48a 100644
 | 
				
			||||||
 | 
					--- a/content/browser/utility_sandbox_delegate.cc
 | 
				
			||||||
 | 
					+++ b/content/browser/utility_sandbox_delegate.cc
 | 
				
			||||||
 | 
					@@ -29,13 +29,15 @@ UtilitySandboxedProcessLauncherDelegate::
 | 
				
			||||||
 | 
					     UtilitySandboxedProcessLauncherDelegate(
 | 
				
			||||||
 | 
					         sandbox::mojom::Sandbox sandbox_type,
 | 
				
			||||||
 | 
					         const base::EnvironmentMap& env,
 | 
				
			||||||
 | 
					-        const base::CommandLine& cmd_line)
 | 
				
			||||||
 | 
					+        const base::FilePath& cwd,
 | 
				
			||||||
 | 
					+        const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					+        bool inherit_environment)
 | 
				
			||||||
 | 
					     :
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					       env_(env),
 | 
				
			||||||
 | 
					-#endif
 | 
				
			||||||
 | 
					+      current_directory_(cwd),
 | 
				
			||||||
 | 
					       sandbox_type_(sandbox_type),
 | 
				
			||||||
 | 
					-      cmd_line_(cmd_line) {
 | 
				
			||||||
 | 
					+      cmd_line_(cmd_line),
 | 
				
			||||||
 | 
					+      inherit_environment_(inherit_environment) {
 | 
				
			||||||
 | 
					 #if DCHECK_IS_ON()
 | 
				
			||||||
 | 
					   bool supported_sandbox_type =
 | 
				
			||||||
 | 
					       sandbox_type_ == sandbox::mojom::Sandbox::kNoSandbox ||
 | 
				
			||||||
 | 
					@@ -93,11 +95,17 @@ UtilitySandboxedProcessLauncherDelegate::GetSandboxType() {
 | 
				
			||||||
 | 
					   return sandbox_type_;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					 base::EnvironmentMap UtilitySandboxedProcessLauncherDelegate::GetEnvironment() {
 | 
				
			||||||
 | 
					   return env_;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					-#endif  // BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+bool UtilitySandboxedProcessLauncherDelegate::ShouldInheritEnvironment() {
 | 
				
			||||||
 | 
					+  return inherit_environment_;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+base::FilePath UtilitySandboxedProcessLauncherDelegate::GetCurrentDirectory() {
 | 
				
			||||||
 | 
					+  return current_directory_;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #if BUILDFLAG(USE_ZYGOTE_HANDLE)
 | 
				
			||||||
 | 
					 ZygoteHandle UtilitySandboxedProcessLauncherDelegate::GetZygote() {
 | 
				
			||||||
 | 
					diff --git a/content/browser/utility_sandbox_delegate.h b/content/browser/utility_sandbox_delegate.h
 | 
				
			||||||
 | 
					index 41d93b41e7fff8ba4a7138d05035e4bc24b7a85b..20cb410fc71994e26cff6ac9801d42ebd11d9fee 100644
 | 
				
			||||||
 | 
					--- a/content/browser/utility_sandbox_delegate.h
 | 
				
			||||||
 | 
					+++ b/content/browser/utility_sandbox_delegate.h
 | 
				
			||||||
 | 
					@@ -26,7 +26,9 @@ class UtilitySandboxedProcessLauncherDelegate
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					   UtilitySandboxedProcessLauncherDelegate(sandbox::mojom::Sandbox sandbox_type,
 | 
				
			||||||
 | 
					                                           const base::EnvironmentMap& env,
 | 
				
			||||||
 | 
					-                                          const base::CommandLine& cmd_line);
 | 
				
			||||||
 | 
					+                                          const base::FilePath& cwd,
 | 
				
			||||||
 | 
					+                                          const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					+                                          bool inherit_environment);
 | 
				
			||||||
 | 
					   ~UtilitySandboxedProcessLauncherDelegate() override;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   sandbox::mojom::Sandbox GetSandboxType() override;
 | 
				
			||||||
 | 
					@@ -45,16 +47,16 @@ class UtilitySandboxedProcessLauncherDelegate
 | 
				
			||||||
 | 
					   ZygoteHandle GetZygote() override;
 | 
				
			||||||
 | 
					 #endif  // BUILDFLAG(USE_ZYGOTE_HANDLE)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					   base::EnvironmentMap GetEnvironment() override;
 | 
				
			||||||
 | 
					-#endif  // BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+  bool ShouldInheritEnvironment() override;
 | 
				
			||||||
 | 
					+  base::FilePath GetCurrentDirectory() override;
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					   base::EnvironmentMap env_;
 | 
				
			||||||
 | 
					-#endif  // BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+  base::FilePath current_directory_;
 | 
				
			||||||
 | 
					   sandbox::mojom::Sandbox sandbox_type_;
 | 
				
			||||||
 | 
					   base::CommandLine cmd_line_;
 | 
				
			||||||
 | 
					+  bool inherit_environment_;
 | 
				
			||||||
 | 
					 };
 | 
				
			||||||
 | 
					 }  // namespace content
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					diff --git a/content/common/sandbox_init_win.cc b/content/common/sandbox_init_win.cc
 | 
				
			||||||
 | 
					index 498f60227d13eb2e476413f88eaa58cc0babf461..b2d7a009477293bf73f3ae4a0c8452d1b1bf1dd8 100644
 | 
				
			||||||
 | 
					--- a/content/common/sandbox_init_win.cc
 | 
				
			||||||
 | 
					+++ b/content/common/sandbox_init_win.cc
 | 
				
			||||||
 | 
					@@ -23,7 +23,7 @@ namespace content {
 | 
				
			||||||
 | 
					 sandbox::ResultCode StartSandboxedProcess(
 | 
				
			||||||
 | 
					     SandboxedProcessLauncherDelegate* delegate,
 | 
				
			||||||
 | 
					     const base::CommandLine& target_command_line,
 | 
				
			||||||
 | 
					-    const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+    const base::LaunchOptions& options,
 | 
				
			||||||
 | 
					     base::Process* process) {
 | 
				
			||||||
 | 
					   std::string type_str =
 | 
				
			||||||
 | 
					       target_command_line.GetSwitchValueASCII(switches::kProcessType);
 | 
				
			||||||
 | 
					@@ -45,7 +45,7 @@ sandbox::ResultCode StartSandboxedProcess(
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   return sandbox::policy::SandboxWin::StartSandboxedProcess(
 | 
				
			||||||
 | 
					-      full_command_line, type_str, handles_to_inherit, delegate, process);
 | 
				
			||||||
 | 
					+      full_command_line, type_str, options, delegate, process);
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 }  // namespace content
 | 
				
			||||||
 | 
					diff --git a/content/public/browser/service_process_host.cc b/content/public/browser/service_process_host.cc
 | 
				
			||||||
 | 
					index 6d25170e3badb65745c7dbea9c9664bdf8c91b0e..df79ba6137c8a9264ba32e4f9e1c1d7893e8f38a 100644
 | 
				
			||||||
 | 
					--- a/content/public/browser/service_process_host.cc
 | 
				
			||||||
 | 
					+++ b/content/public/browser/service_process_host.cc
 | 
				
			||||||
 | 
					@@ -46,12 +46,45 @@ ServiceProcessHost::Options::WithExtraCommandLineSwitches(
 | 
				
			||||||
 | 
					   return *this;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+ServiceProcessHost::Options& ServiceProcessHost::Options::WithStdoutHandle(
 | 
				
			||||||
 | 
					+    base::win::ScopedHandle handle) {
 | 
				
			||||||
 | 
					+  stdout_handle = std::move(handle);
 | 
				
			||||||
 | 
					+  return *this;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+ServiceProcessHost::Options& ServiceProcessHost::Options::WithStderrHandle(
 | 
				
			||||||
 | 
					+    base::win::ScopedHandle handle) {
 | 
				
			||||||
 | 
					+  stderr_handle = std::move(handle);
 | 
				
			||||||
 | 
					+  return *this;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+ServiceProcessHost::Options& ServiceProcessHost::Options::WithAdditionalFds(
 | 
				
			||||||
 | 
					+    base::FileHandleMappingVector mapping) {
 | 
				
			||||||
 | 
					+  fds_to_remap = std::move(mapping);
 | 
				
			||||||
 | 
					+  return *this;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 ServiceProcessHost::Options& ServiceProcessHost::Options::WithProcessCallback(
 | 
				
			||||||
 | 
					     base::OnceCallback<void(const base::Process&)> callback) {
 | 
				
			||||||
 | 
					   process_callback = std::move(callback);
 | 
				
			||||||
 | 
					   return *this;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+ServiceProcessHost::Options& ServiceProcessHost::Options::WithCurrentDirectory(
 | 
				
			||||||
 | 
					+    const base::FilePath& cwd) {
 | 
				
			||||||
 | 
					+  current_directory = cwd;
 | 
				
			||||||
 | 
					+  return *this;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+ServiceProcessHost::Options& ServiceProcessHost::Options::WithEnvironment(
 | 
				
			||||||
 | 
					+    const base::EnvironmentMap& env, bool new_environment) {
 | 
				
			||||||
 | 
					+  environment = env;
 | 
				
			||||||
 | 
					+  clear_environment = new_environment;
 | 
				
			||||||
 | 
					+  return *this;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 ServiceProcessHost::Options ServiceProcessHost::Options::Pass() {
 | 
				
			||||||
 | 
					   return std::move(*this);
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
 | 
				
			||||||
 | 
					index a308d46612c1b30163cf9988117d2224a43ab5ad..5a41c3c907c0f0cf42759c52e7493cbf675f6fa6 100644
 | 
				
			||||||
 | 
					--- a/content/public/browser/service_process_host.h
 | 
				
			||||||
 | 
					+++ b/content/public/browser/service_process_host.h
 | 
				
			||||||
 | 
					@@ -13,6 +13,7 @@
 | 
				
			||||||
 | 
					 #include "base/callback.h"
 | 
				
			||||||
 | 
					 #include "base/command_line.h"
 | 
				
			||||||
 | 
					 #include "base/observer_list_types.h"
 | 
				
			||||||
 | 
					+#include "base/process/launch.h"
 | 
				
			||||||
 | 
					 #include "base/process/process_handle.h"
 | 
				
			||||||
 | 
					 #include "base/strings/string_piece.h"
 | 
				
			||||||
 | 
					 #include "build/chromecast_buildflags.h"
 | 
				
			||||||
 | 
					@@ -29,6 +30,10 @@
 | 
				
			||||||
 | 
					 #include "mojo/public/cpp/system/message_pipe.h"
 | 
				
			||||||
 | 
					 #endif
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+#include "base/win/scoped_handle.h"
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 namespace base {
 | 
				
			||||||
 | 
					 class Process;
 | 
				
			||||||
 | 
					 }  // namespace base
 | 
				
			||||||
 | 
					@@ -88,11 +93,30 @@ class CONTENT_EXPORT ServiceProcessHost {
 | 
				
			||||||
 | 
					     // Specifies extra command line switches to append before launch.
 | 
				
			||||||
 | 
					     Options& WithExtraCommandLineSwitches(std::vector<std::string> switches);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+    // Specifies the handles for redirection of stdout and stderr.
 | 
				
			||||||
 | 
					+    Options& WithStdoutHandle(base::win::ScopedHandle stdout_handle);
 | 
				
			||||||
 | 
					+    Options& WithStderrHandle(base::win::ScopedHandle stderr_handle);
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+    // Specifies file descriptors to propagate into the child process
 | 
				
			||||||
 | 
					+    // based on the mapping.
 | 
				
			||||||
 | 
					+    Options& WithAdditionalFds(base::FileHandleMappingVector mapping);
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					     // Specifies a callback to be invoked with service process once it's
 | 
				
			||||||
 | 
					     // launched. Will be on UI thread.
 | 
				
			||||||
 | 
					     Options& WithProcessCallback(
 | 
				
			||||||
 | 
					         base::OnceCallback<void(const base::Process&)>);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+    // Specifies the working directory for the launched process.
 | 
				
			||||||
 | 
					+    Options& WithCurrentDirectory(const base::FilePath& cwd);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+    // Specifies the environment that should be applied to the process.
 | 
				
			||||||
 | 
					+    // |new_environment| controls whether the process should inherit
 | 
				
			||||||
 | 
					+    // environment from the parent process.
 | 
				
			||||||
 | 
					+    Options& WithEnvironment(const base::EnvironmentMap& environment,
 | 
				
			||||||
 | 
					+                             bool new_environment);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					     // Passes the contents of this Options object to a newly returned Options
 | 
				
			||||||
 | 
					     // value. This must be called when moving a built Options object into a call
 | 
				
			||||||
 | 
					     // to |Launch()|.
 | 
				
			||||||
 | 
					@@ -101,7 +125,16 @@ class CONTENT_EXPORT ServiceProcessHost {
 | 
				
			||||||
 | 
					     std::u16string display_name;
 | 
				
			||||||
 | 
					     absl::optional<int> child_flags;
 | 
				
			||||||
 | 
					     std::vector<std::string> extra_switches;
 | 
				
			||||||
 | 
					+#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					+    base::win::ScopedHandle stdout_handle;
 | 
				
			||||||
 | 
					+    base::win::ScopedHandle stderr_handle;
 | 
				
			||||||
 | 
					+#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+    base::FileHandleMappingVector fds_to_remap;
 | 
				
			||||||
 | 
					+#endif
 | 
				
			||||||
 | 
					     base::OnceCallback<void(const base::Process&)> process_callback;
 | 
				
			||||||
 | 
					+    base::FilePath current_directory;
 | 
				
			||||||
 | 
					+    base::EnvironmentMap environment;
 | 
				
			||||||
 | 
					+    bool clear_environment = false;
 | 
				
			||||||
 | 
					   };
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   // An interface which can be implemented and registered/unregistered with
 | 
				
			||||||
 | 
					diff --git a/content/public/common/sandbox_init_win.h b/content/public/common/sandbox_init_win.h
 | 
				
			||||||
 | 
					index 9bb4b30ba0f5d37ec2b28f0848d94f34c24f9423..c19cceae4215d74ae74f6e6005125f326453f955 100644
 | 
				
			||||||
 | 
					--- a/content/public/common/sandbox_init_win.h
 | 
				
			||||||
 | 
					+++ b/content/public/common/sandbox_init_win.h
 | 
				
			||||||
 | 
					@@ -29,7 +29,7 @@ class SandboxedProcessLauncherDelegate;
 | 
				
			||||||
 | 
					 CONTENT_EXPORT sandbox::ResultCode StartSandboxedProcess(
 | 
				
			||||||
 | 
					     SandboxedProcessLauncherDelegate* delegate,
 | 
				
			||||||
 | 
					     const base::CommandLine& target_command_line,
 | 
				
			||||||
 | 
					-    const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+    const base::LaunchOptions& options,
 | 
				
			||||||
 | 
					     base::Process* process);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 }  // namespace content
 | 
				
			||||||
 | 
					diff --git a/content/public/common/sandboxed_process_launcher_delegate.cc b/content/public/common/sandboxed_process_launcher_delegate.cc
 | 
				
			||||||
 | 
					index ee7cdddba192f151346b74b68ef1eabe5f46e84a..4378d5ac7f455eb54f9f39364184649d7a63666f 100644
 | 
				
			||||||
 | 
					--- a/content/public/common/sandboxed_process_launcher_delegate.cc
 | 
				
			||||||
 | 
					+++ b/content/public/common/sandboxed_process_launcher_delegate.cc
 | 
				
			||||||
 | 
					@@ -53,11 +53,17 @@ ZygoteHandle SandboxedProcessLauncherDelegate::GetZygote() {
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 #endif  // BUILDFLAG(USE_ZYGOTE_HANDLE)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					 base::EnvironmentMap SandboxedProcessLauncherDelegate::GetEnvironment() {
 | 
				
			||||||
 | 
					   return base::EnvironmentMap();
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					-#endif  // BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+bool SandboxedProcessLauncherDelegate::ShouldInheritEnvironment() {
 | 
				
			||||||
 | 
					+  return true;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+base::FilePath SandboxedProcessLauncherDelegate::GetCurrentDirectory() {
 | 
				
			||||||
 | 
					+  return base::FilePath();
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #if BUILDFLAG(IS_MAC)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					diff --git a/content/public/common/sandboxed_process_launcher_delegate.h b/content/public/common/sandboxed_process_launcher_delegate.h
 | 
				
			||||||
 | 
					index 1e8f3994764a2b4e4efb87a08c522cc0e0103e18..83cc16ffbf484aa78b1c350d20a5a15ffd0dd0e8 100644
 | 
				
			||||||
 | 
					--- a/content/public/common/sandboxed_process_launcher_delegate.h
 | 
				
			||||||
 | 
					+++ b/content/public/common/sandboxed_process_launcher_delegate.h
 | 
				
			||||||
 | 
					@@ -6,6 +6,7 @@
 | 
				
			||||||
 | 
					 #define CONTENT_PUBLIC_COMMON_SANDBOXED_PROCESS_LAUNCHER_DELEGATE_H_
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #include "base/environment.h"
 | 
				
			||||||
 | 
					+#include "base/files/file_path.h"
 | 
				
			||||||
 | 
					 #include "base/files/scoped_file.h"
 | 
				
			||||||
 | 
					 #include "base/process/process.h"
 | 
				
			||||||
 | 
					 #include "build/build_config.h"
 | 
				
			||||||
 | 
					@@ -48,10 +49,14 @@ class CONTENT_EXPORT SandboxedProcessLauncherDelegate
 | 
				
			||||||
 | 
					   virtual ZygoteHandle GetZygote();
 | 
				
			||||||
 | 
					 #endif  // BUILDFLAG(USE_ZYGOTE_HANDLE)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					   // Override this if the process needs a non-empty environment map.
 | 
				
			||||||
 | 
					   virtual base::EnvironmentMap GetEnvironment();
 | 
				
			||||||
 | 
					-#endif  // BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // Override this if the process should not inherit parent environment.
 | 
				
			||||||
 | 
					+  virtual bool ShouldInheritEnvironment();
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+  // Specifies the directory to change to before executing the process.
 | 
				
			||||||
 | 
					+  virtual base::FilePath GetCurrentDirectory();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #if BUILDFLAG(IS_MAC)
 | 
				
			||||||
 | 
					   // Whether or not to disclaim TCC responsibility for the process, defaults to
 | 
				
			||||||
 | 
					diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
 | 
				
			||||||
 | 
					index 2191f51de17cfde5bb39f8231c8210dea6aa4fdd..6239f68771832d245d7270fd83e04f4fdce44032 100644
 | 
				
			||||||
 | 
					--- a/sandbox/policy/win/sandbox_win.cc
 | 
				
			||||||
 | 
					+++ b/sandbox/policy/win/sandbox_win.cc
 | 
				
			||||||
 | 
					@@ -851,11 +851,9 @@ ResultCode GenerateConfigForSandboxedProcess(const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					 // command line flag.
 | 
				
			||||||
 | 
					 ResultCode LaunchWithoutSandbox(
 | 
				
			||||||
 | 
					     const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					-    const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+    base::LaunchOptions options,
 | 
				
			||||||
 | 
					     SandboxDelegate* delegate,
 | 
				
			||||||
 | 
					     base::Process* process) {
 | 
				
			||||||
 | 
					-  base::LaunchOptions options;
 | 
				
			||||||
 | 
					-  options.handles_to_inherit = handles_to_inherit;
 | 
				
			||||||
 | 
					   // Network process runs in a job even when unsandboxed. This is to ensure it
 | 
				
			||||||
 | 
					   // does not outlive the browser, which could happen if there is a lot of I/O
 | 
				
			||||||
 | 
					   // on process shutdown, in which case TerminateProcess can fail. See
 | 
				
			||||||
 | 
					@@ -1091,7 +1089,7 @@ bool SandboxWin::InitTargetServices(TargetServices* target_services) {
 | 
				
			||||||
 | 
					 ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
 | 
				
			||||||
 | 
					     const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					     const std::string& process_type,
 | 
				
			||||||
 | 
					-    const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+    const base::LaunchOptions& options,
 | 
				
			||||||
 | 
					     SandboxDelegate* delegate,
 | 
				
			||||||
 | 
					     TargetPolicy* policy) {
 | 
				
			||||||
 | 
					   const base::CommandLine& launcher_process_command_line =
 | 
				
			||||||
 | 
					@@ -1105,7 +1103,7 @@ ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   // Add any handles to be inherited to the policy.
 | 
				
			||||||
 | 
					-  for (HANDLE handle : handles_to_inherit)
 | 
				
			||||||
 | 
					+  for (HANDLE handle : options.handles_to_inherit)
 | 
				
			||||||
 | 
					     policy->AddHandleToShare(handle);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   if (!policy->GetConfig()->IsConfigured()) {
 | 
				
			||||||
 | 
					@@ -1120,6 +1118,13 @@ ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
 | 
				
			||||||
 | 
					   // have no effect. These calls can fail with SBOX_ERROR_BAD_PARAMS.
 | 
				
			||||||
 | 
					   policy->SetStdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE));
 | 
				
			||||||
 | 
					   policy->SetStderrHandle(GetStdHandle(STD_ERROR_HANDLE));
 | 
				
			||||||
 | 
					+#else
 | 
				
			||||||
 | 
					+  if (options.stdout_handle != nullptr && options.stdout_handle != INVALID_HANDLE_VALUE) {
 | 
				
			||||||
 | 
					+    policy->SetStdoutHandle(options.stdout_handle);
 | 
				
			||||||
 | 
					+  }
 | 
				
			||||||
 | 
					+  if (options.stderr_handle != nullptr && options.stderr_handle != INVALID_HANDLE_VALUE) {
 | 
				
			||||||
 | 
					+    policy->SetStderrHandle(options.stderr_handle);
 | 
				
			||||||
 | 
					+  }
 | 
				
			||||||
 | 
					 #endif
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					   if (!delegate->PreSpawnTarget(policy))
 | 
				
			||||||
 | 
					@@ -1132,7 +1137,7 @@ ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
 | 
				
			||||||
 | 
					 ResultCode SandboxWin::StartSandboxedProcess(
 | 
				
			||||||
 | 
					     const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					     const std::string& process_type,
 | 
				
			||||||
 | 
					-    const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+    const base::LaunchOptions& options,
 | 
				
			||||||
 | 
					     SandboxDelegate* delegate,
 | 
				
			||||||
 | 
					     base::Process* process) {
 | 
				
			||||||
 | 
					   const base::ElapsedTimer timer;
 | 
				
			||||||
 | 
					@@ -1140,7 +1145,7 @@ ResultCode SandboxWin::StartSandboxedProcess(
 | 
				
			||||||
 | 
					   // Avoid making a policy if we won't use it.
 | 
				
			||||||
 | 
					   if (IsUnsandboxedProcess(delegate->GetSandboxType(), cmd_line,
 | 
				
			||||||
 | 
					                            *base::CommandLine::ForCurrentProcess())) {
 | 
				
			||||||
 | 
					-    return LaunchWithoutSandbox(cmd_line, handles_to_inherit, delegate,
 | 
				
			||||||
 | 
					+    return LaunchWithoutSandbox(cmd_line, options, delegate,
 | 
				
			||||||
 | 
					                                 process);
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -1151,7 +1156,7 @@ ResultCode SandboxWin::StartSandboxedProcess(
 | 
				
			||||||
 | 
					   auto policy = g_broker_services->CreatePolicy(tag);
 | 
				
			||||||
 | 
					   auto time_policy_created = timer.Elapsed();
 | 
				
			||||||
 | 
					   ResultCode result = GeneratePolicyForSandboxedProcess(
 | 
				
			||||||
 | 
					-      cmd_line, process_type, handles_to_inherit, delegate, policy.get());
 | 
				
			||||||
 | 
					+      cmd_line, process_type, options, delegate, policy.get());
 | 
				
			||||||
 | 
					   if (SBOX_ALL_OK != result)
 | 
				
			||||||
 | 
					     return result;
 | 
				
			||||||
 | 
					   auto time_policy_generated = timer.Elapsed();
 | 
				
			||||||
 | 
					diff --git a/sandbox/policy/win/sandbox_win.h b/sandbox/policy/win/sandbox_win.h
 | 
				
			||||||
 | 
					index d1adadc10de3053f69fde39387d196054a96beda..0111a9c4becca009f17a3839d4d4bef3d9d880b8 100644
 | 
				
			||||||
 | 
					--- a/sandbox/policy/win/sandbox_win.h
 | 
				
			||||||
 | 
					+++ b/sandbox/policy/win/sandbox_win.h
 | 
				
			||||||
 | 
					@@ -50,7 +50,7 @@ class SANDBOX_POLICY_EXPORT SandboxWin {
 | 
				
			||||||
 | 
					   static ResultCode StartSandboxedProcess(
 | 
				
			||||||
 | 
					       const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					       const std::string& process_type,
 | 
				
			||||||
 | 
					-      const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+      const base::LaunchOptions& options,
 | 
				
			||||||
 | 
					       SandboxDelegate* delegate,
 | 
				
			||||||
 | 
					       base::Process* process);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -64,7 +64,7 @@ class SANDBOX_POLICY_EXPORT SandboxWin {
 | 
				
			||||||
 | 
					   static ResultCode GeneratePolicyForSandboxedProcess(
 | 
				
			||||||
 | 
					       const base::CommandLine& cmd_line,
 | 
				
			||||||
 | 
					       const std::string& process_type,
 | 
				
			||||||
 | 
					-      const base::HandlesToInheritVector& handles_to_inherit,
 | 
				
			||||||
 | 
					+      const base::LaunchOptions& options,
 | 
				
			||||||
 | 
					       SandboxDelegate* delegate,
 | 
				
			||||||
 | 
					       TargetPolicy* policy);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,10 @@ const main = async () => {
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      name: 'asar_bundle_deps',
 | 
					      name: 'asar_bundle_deps',
 | 
				
			||||||
      config: 'webpack.config.asar.js'
 | 
					      config: 'webpack.config.asar.js'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'utility_bundle_deps',
 | 
				
			||||||
 | 
					      config: 'webpack.config.utility.js'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,6 +45,7 @@
 | 
				
			||||||
#include "shell/app/command_line_args.h"
 | 
					#include "shell/app/command_line_args.h"
 | 
				
			||||||
#include "shell/browser/api/electron_api_menu.h"
 | 
					#include "shell/browser/api/electron_api_menu.h"
 | 
				
			||||||
#include "shell/browser/api/electron_api_session.h"
 | 
					#include "shell/browser/api/electron_api_session.h"
 | 
				
			||||||
 | 
					#include "shell/browser/api/electron_api_utility_process.h"
 | 
				
			||||||
#include "shell/browser/api/electron_api_web_contents.h"
 | 
					#include "shell/browser/api/electron_api_web_contents.h"
 | 
				
			||||||
#include "shell/browser/api/gpuinfo_manager.h"
 | 
					#include "shell/browser/api/gpuinfo_manager.h"
 | 
				
			||||||
#include "shell/browser/browser_process_impl.h"
 | 
					#include "shell/browser/browser_process_impl.h"
 | 
				
			||||||
| 
						 | 
					@ -922,6 +923,12 @@ void App::BrowserChildProcessCrashedOrKilled(
 | 
				
			||||||
  if (!data.name.empty()) {
 | 
					  if (!data.name.empty()) {
 | 
				
			||||||
    details.Set("name", data.name);
 | 
					    details.Set("name", data.name);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (data.process_type == content::PROCESS_TYPE_UTILITY) {
 | 
				
			||||||
 | 
					    base::ProcessId pid = data.GetProcess().Pid();
 | 
				
			||||||
 | 
					    auto utility_process_wrapper = UtilityProcessWrapper::FromProcessId(pid);
 | 
				
			||||||
 | 
					    if (utility_process_wrapper)
 | 
				
			||||||
 | 
					      utility_process_wrapper->Shutdown(info.exit_code);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  Emit("child-process-gone", details);
 | 
					  Emit("child-process-gone", details);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										420
									
								
								shell/browser/api/electron_api_utility_process.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								shell/browser/api/electron_api_utility_process.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,420 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "shell/browser/api/electron_api_utility_process.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "base/bind.h"
 | 
				
			||||||
 | 
					#include "base/files/file_util.h"
 | 
				
			||||||
 | 
					#include "base/no_destructor.h"
 | 
				
			||||||
 | 
					#include "base/process/kill.h"
 | 
				
			||||||
 | 
					#include "base/process/launch.h"
 | 
				
			||||||
 | 
					#include "base/process/process.h"
 | 
				
			||||||
 | 
					#include "content/public/browser/service_process_host.h"
 | 
				
			||||||
 | 
					#include "content/public/common/child_process_host.h"
 | 
				
			||||||
 | 
					#include "content/public/common/result_codes.h"
 | 
				
			||||||
 | 
					#include "gin/handle.h"
 | 
				
			||||||
 | 
					#include "gin/object_template_builder.h"
 | 
				
			||||||
 | 
					#include "gin/wrappable.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/pending_receiver.h"
 | 
				
			||||||
 | 
					#include "shell/browser/api/message_port.h"
 | 
				
			||||||
 | 
					#include "shell/browser/javascript_environment.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_converters/callback_converter.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_converters/file_path_converter.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_helper/dictionary.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_helper/object_template_builder.h"
 | 
				
			||||||
 | 
					#include "shell/common/node_includes.h"
 | 
				
			||||||
 | 
					#include "shell/common/v8_value_serializer.h"
 | 
				
			||||||
 | 
					#include "third_party/blink/public/common/messaging/message_port_descriptor.h"
 | 
				
			||||||
 | 
					#include "third_party/blink/public/common/messaging/transferable_message_mojom_traits.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					#include "base/posix/eintr_wrapper.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					#include <io.h>
 | 
				
			||||||
 | 
					#include "base/win/windows_types.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					base::IDMap<api::UtilityProcessWrapper*, base::ProcessId>&
 | 
				
			||||||
 | 
					GetAllUtilityProcessWrappers() {
 | 
				
			||||||
 | 
					  static base::NoDestructor<
 | 
				
			||||||
 | 
					      base::IDMap<api::UtilityProcessWrapper*, base::ProcessId>>
 | 
				
			||||||
 | 
					      s_all_utility_process_wrappers;
 | 
				
			||||||
 | 
					  return *s_all_utility_process_wrappers;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gin::WrapperInfo UtilityProcessWrapper::kWrapperInfo = {
 | 
				
			||||||
 | 
					    gin::kEmbedderNativeGin};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UtilityProcessWrapper::UtilityProcessWrapper(
 | 
				
			||||||
 | 
					    node::mojom::NodeServiceParamsPtr params,
 | 
				
			||||||
 | 
					    std::u16string display_name,
 | 
				
			||||||
 | 
					    std::map<IOHandle, IOType> stdio,
 | 
				
			||||||
 | 
					    base::EnvironmentMap env_map,
 | 
				
			||||||
 | 
					    base::FilePath current_working_directory,
 | 
				
			||||||
 | 
					    bool use_plugin_helper) {
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					  base::win::ScopedHandle stdout_write(nullptr);
 | 
				
			||||||
 | 
					  base::win::ScopedHandle stderr_write(nullptr);
 | 
				
			||||||
 | 
					#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					  base::FileHandleMappingVector fds_to_remap;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  for (const auto& [io_handle, io_type] : stdio) {
 | 
				
			||||||
 | 
					    if (io_type == IOType::IO_PIPE) {
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					      HANDLE read = nullptr;
 | 
				
			||||||
 | 
					      HANDLE write = nullptr;
 | 
				
			||||||
 | 
					      // Ideally we would create with SECURITY_ATTRIBUTES.bInheritHandles
 | 
				
			||||||
 | 
					      // set to TRUE so that the write handle can be duplicated into the
 | 
				
			||||||
 | 
					      // child process for use,
 | 
				
			||||||
 | 
					      // See
 | 
				
			||||||
 | 
					      // https://learn.microsoft.com/en-us/windows/win32/procthread/inheritance#inheriting-handles
 | 
				
			||||||
 | 
					      // for inheritance behavior of child process. But we don't do it here
 | 
				
			||||||
 | 
					      // since base::Launch already takes of setting the
 | 
				
			||||||
 | 
					      // inherit attribute when configuring
 | 
				
			||||||
 | 
					      // `base::LaunchOptions::handles_to_inherit` Refs
 | 
				
			||||||
 | 
					      // https://source.chromium.org/chromium/chromium/src/+/main:base/process/launch_win.cc;l=303-332
 | 
				
			||||||
 | 
					      if (!::CreatePipe(&read, &write, nullptr, 0)) {
 | 
				
			||||||
 | 
					        PLOG(ERROR) << "pipe creation failed";
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (io_handle == IOHandle::STDOUT) {
 | 
				
			||||||
 | 
					        stdout_write.Set(write);
 | 
				
			||||||
 | 
					        stdout_read_handle_ = read;
 | 
				
			||||||
 | 
					        stdout_read_fd_ =
 | 
				
			||||||
 | 
					            _open_osfhandle(reinterpret_cast<intptr_t>(read), _O_RDONLY);
 | 
				
			||||||
 | 
					      } else if (io_handle == IOHandle::STDERR) {
 | 
				
			||||||
 | 
					        stderr_write.Set(write);
 | 
				
			||||||
 | 
					        stderr_read_handle_ = read;
 | 
				
			||||||
 | 
					        stderr_read_fd_ =
 | 
				
			||||||
 | 
					            _open_osfhandle(reinterpret_cast<intptr_t>(read), _O_RDONLY);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					      int pipe_fd[2];
 | 
				
			||||||
 | 
					      if (HANDLE_EINTR(pipe(pipe_fd)) < 0) {
 | 
				
			||||||
 | 
					        PLOG(ERROR) << "pipe creation failed";
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (io_handle == IOHandle::STDOUT) {
 | 
				
			||||||
 | 
					        fds_to_remap.push_back(std::make_pair(pipe_fd[1], STDOUT_FILENO));
 | 
				
			||||||
 | 
					        stdout_read_fd_ = pipe_fd[0];
 | 
				
			||||||
 | 
					      } else if (io_handle == IOHandle::STDERR) {
 | 
				
			||||||
 | 
					        fds_to_remap.push_back(std::make_pair(pipe_fd[1], STDERR_FILENO));
 | 
				
			||||||
 | 
					        stderr_read_fd_ = pipe_fd[0];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    } else if (io_type == IOType::IO_IGNORE) {
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					      HANDLE handle =
 | 
				
			||||||
 | 
					          CreateFileW(L"NUL", FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
 | 
				
			||||||
 | 
					                      FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
 | 
				
			||||||
 | 
					                      OPEN_EXISTING, 0, nullptr);
 | 
				
			||||||
 | 
					      if (handle == INVALID_HANDLE_VALUE) {
 | 
				
			||||||
 | 
					        PLOG(ERROR) << "Failed to create null handle";
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (io_handle == IOHandle::STDOUT) {
 | 
				
			||||||
 | 
					        stdout_write.Set(handle);
 | 
				
			||||||
 | 
					      } else if (io_handle == IOHandle::STDERR) {
 | 
				
			||||||
 | 
					        stderr_write.Set(handle);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					      int devnull = open("/dev/null", O_WRONLY);
 | 
				
			||||||
 | 
					      if (devnull < 0) {
 | 
				
			||||||
 | 
					        PLOG(ERROR) << "failed to open /dev/null";
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (io_handle == IOHandle::STDOUT) {
 | 
				
			||||||
 | 
					        fds_to_remap.push_back(std::make_pair(devnull, STDOUT_FILENO));
 | 
				
			||||||
 | 
					      } else if (io_handle == IOHandle::STDERR) {
 | 
				
			||||||
 | 
					        fds_to_remap.push_back(std::make_pair(devnull, STDERR_FILENO));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mojo::PendingReceiver<node::mojom::NodeService> receiver =
 | 
				
			||||||
 | 
					      node_service_remote_.BindNewPipeAndPassReceiver();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  content::ServiceProcessHost::Launch(
 | 
				
			||||||
 | 
					      std::move(receiver),
 | 
				
			||||||
 | 
					      content::ServiceProcessHost::Options()
 | 
				
			||||||
 | 
					          .WithDisplayName(display_name.empty()
 | 
				
			||||||
 | 
					                               ? std::u16string(u"Node Utility Process")
 | 
				
			||||||
 | 
					                               : display_name)
 | 
				
			||||||
 | 
					          .WithExtraCommandLineSwitches(params->exec_args)
 | 
				
			||||||
 | 
					          .WithCurrentDirectory(current_working_directory)
 | 
				
			||||||
 | 
					          // Inherit parent process environment when there is no custom
 | 
				
			||||||
 | 
					          // environment provided by the user.
 | 
				
			||||||
 | 
					          .WithEnvironment(env_map,
 | 
				
			||||||
 | 
					                           env_map.empty() ? false : true /*clear_environment*/)
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					          .WithStdoutHandle(std::move(stdout_write))
 | 
				
			||||||
 | 
					          .WithStderrHandle(std::move(stderr_write))
 | 
				
			||||||
 | 
					#elif BUILDFLAG(IS_POSIX)
 | 
				
			||||||
 | 
					          .WithAdditionalFds(std::move(fds_to_remap))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_MAC)
 | 
				
			||||||
 | 
					          .WithChildFlags(use_plugin_helper
 | 
				
			||||||
 | 
					                              ? content::ChildProcessHost::CHILD_PLUGIN
 | 
				
			||||||
 | 
					                              : content::ChildProcessHost::CHILD_NORMAL)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					          .WithProcessCallback(
 | 
				
			||||||
 | 
					              base::BindOnce(&UtilityProcessWrapper::OnServiceProcessLaunched,
 | 
				
			||||||
 | 
					                             weak_factory_.GetWeakPtr()))
 | 
				
			||||||
 | 
					          .Pass());
 | 
				
			||||||
 | 
					  node_service_remote_.set_disconnect_with_reason_handler(
 | 
				
			||||||
 | 
					      base::BindOnce(&UtilityProcessWrapper::OnServiceProcessDisconnected,
 | 
				
			||||||
 | 
					                     weak_factory_.GetWeakPtr()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // We use a separate message pipe to support postMessage API
 | 
				
			||||||
 | 
					  // instead of the existing receiver interface so that we can
 | 
				
			||||||
 | 
					  // support queuing of messages without having to block other
 | 
				
			||||||
 | 
					  // interfaces.
 | 
				
			||||||
 | 
					  blink::MessagePortDescriptorPair pipe;
 | 
				
			||||||
 | 
					  host_port_ = pipe.TakePort0();
 | 
				
			||||||
 | 
					  params->port = pipe.TakePort1();
 | 
				
			||||||
 | 
					  connector_ = std::make_unique<mojo::Connector>(
 | 
				
			||||||
 | 
					      host_port_.TakeHandleToEntangleWithEmbedder(),
 | 
				
			||||||
 | 
					      mojo::Connector::SINGLE_THREADED_SEND,
 | 
				
			||||||
 | 
					      base::ThreadTaskRunnerHandle::Get());
 | 
				
			||||||
 | 
					  connector_->set_incoming_receiver(this);
 | 
				
			||||||
 | 
					  connector_->set_connection_error_handler(base::BindOnce(
 | 
				
			||||||
 | 
					      &UtilityProcessWrapper::CloseConnectorPort, weak_factory_.GetWeakPtr()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  node_service_remote_->Initialize(std::move(params));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					UtilityProcessWrapper::~UtilityProcessWrapper() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void UtilityProcessWrapper::OnServiceProcessLaunched(
 | 
				
			||||||
 | 
					    const base::Process& process) {
 | 
				
			||||||
 | 
					  DCHECK(node_service_remote_.is_connected());
 | 
				
			||||||
 | 
					  pid_ = process.Pid();
 | 
				
			||||||
 | 
					  GetAllUtilityProcessWrappers().AddWithID(this, pid_);
 | 
				
			||||||
 | 
					  if (stdout_read_fd_ != -1) {
 | 
				
			||||||
 | 
					    EmitWithoutCustomEvent("stdout", stdout_read_fd_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (stderr_read_fd_ != -1) {
 | 
				
			||||||
 | 
					    EmitWithoutCustomEvent("stderr", stderr_read_fd_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // Emit 'spawn' event
 | 
				
			||||||
 | 
					  EmitWithoutCustomEvent("spawn");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void UtilityProcessWrapper::OnServiceProcessDisconnected(
 | 
				
			||||||
 | 
					    uint32_t error_code,
 | 
				
			||||||
 | 
					    const std::string& description) {
 | 
				
			||||||
 | 
					  if (pid_ != base::kNullProcessId)
 | 
				
			||||||
 | 
					    GetAllUtilityProcessWrappers().Remove(pid_);
 | 
				
			||||||
 | 
					  CloseConnectorPort();
 | 
				
			||||||
 | 
					  // Emit 'exit' event
 | 
				
			||||||
 | 
					  EmitWithoutCustomEvent("exit", error_code);
 | 
				
			||||||
 | 
					  Unpin();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void UtilityProcessWrapper::CloseConnectorPort() {
 | 
				
			||||||
 | 
					  if (!connector_closed_ && connector_->is_valid()) {
 | 
				
			||||||
 | 
					    host_port_.GiveDisentangledHandle(connector_->PassMessagePipe());
 | 
				
			||||||
 | 
					    connector_ = nullptr;
 | 
				
			||||||
 | 
					    host_port_.Reset();
 | 
				
			||||||
 | 
					    connector_closed_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void UtilityProcessWrapper::Shutdown(int exit_code) {
 | 
				
			||||||
 | 
					  if (pid_ != base::kNullProcessId)
 | 
				
			||||||
 | 
					    GetAllUtilityProcessWrappers().Remove(pid_);
 | 
				
			||||||
 | 
					  node_service_remote_.reset();
 | 
				
			||||||
 | 
					  CloseConnectorPort();
 | 
				
			||||||
 | 
					  // Emit 'exit' event
 | 
				
			||||||
 | 
					  EmitWithoutCustomEvent("exit", exit_code);
 | 
				
			||||||
 | 
					  Unpin();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void UtilityProcessWrapper::PostMessage(gin::Arguments* args) {
 | 
				
			||||||
 | 
					  if (!node_service_remote_.is_connected())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  blink::TransferableMessage transferable_message;
 | 
				
			||||||
 | 
					  v8::Local<v8::Value> message_value;
 | 
				
			||||||
 | 
					  if (args->GetNext(&message_value)) {
 | 
				
			||||||
 | 
					    if (!electron::SerializeV8Value(args->isolate(), message_value,
 | 
				
			||||||
 | 
					                                    &transferable_message)) {
 | 
				
			||||||
 | 
					      // SerializeV8Value sets an exception.
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  v8::Local<v8::Value> transferables;
 | 
				
			||||||
 | 
					  std::vector<gin::Handle<MessagePort>> wrapped_ports;
 | 
				
			||||||
 | 
					  if (args->GetNext(&transferables)) {
 | 
				
			||||||
 | 
					    if (!gin::ConvertFromV8(args->isolate(), transferables, &wrapped_ports)) {
 | 
				
			||||||
 | 
					      gin_helper::ErrorThrower(args->isolate())
 | 
				
			||||||
 | 
					          .ThrowTypeError("Invalid value for transfer");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool threw_exception = false;
 | 
				
			||||||
 | 
					  transferable_message.ports = MessagePort::DisentanglePorts(
 | 
				
			||||||
 | 
					      args->isolate(), wrapped_ports, &threw_exception);
 | 
				
			||||||
 | 
					  if (threw_exception)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mojo::Message mojo_message = blink::mojom::TransferableMessage::WrapAsMessage(
 | 
				
			||||||
 | 
					      std::move(transferable_message));
 | 
				
			||||||
 | 
					  connector_->Accept(&mojo_message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool UtilityProcessWrapper::Kill() const {
 | 
				
			||||||
 | 
					  if (pid_ == base::kNullProcessId)
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  base::Process process = base::Process::Open(pid_);
 | 
				
			||||||
 | 
					  bool result = process.Terminate(content::RESULT_CODE_NORMAL_EXIT, false);
 | 
				
			||||||
 | 
					  // Refs https://bugs.chromium.org/p/chromium/issues/detail?id=818244
 | 
				
			||||||
 | 
					  // Currently utility process is not sandboxed which
 | 
				
			||||||
 | 
					  // means Zygote is not used on linux, refs
 | 
				
			||||||
 | 
					  // content::UtilitySandboxedProcessLauncherDelegate::GetZygote.
 | 
				
			||||||
 | 
					  // If sandbox feature is enabled for the utility process, then the
 | 
				
			||||||
 | 
					  // process reap should be signaled through the zygote via
 | 
				
			||||||
 | 
					  // content::ZygoteCommunication::EnsureProcessTerminated.
 | 
				
			||||||
 | 
					  base::EnsureProcessTerminated(std::move(process));
 | 
				
			||||||
 | 
					  return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					v8::Local<v8::Value> UtilityProcessWrapper::GetOSProcessId(
 | 
				
			||||||
 | 
					    v8::Isolate* isolate) const {
 | 
				
			||||||
 | 
					  if (pid_ == base::kNullProcessId)
 | 
				
			||||||
 | 
					    return v8::Undefined(isolate);
 | 
				
			||||||
 | 
					  return gin::ConvertToV8(isolate, pid_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool UtilityProcessWrapper::Accept(mojo::Message* mojo_message) {
 | 
				
			||||||
 | 
					  blink::TransferableMessage message;
 | 
				
			||||||
 | 
					  if (!blink::mojom::TransferableMessage::DeserializeFromMessage(
 | 
				
			||||||
 | 
					          std::move(*mojo_message), &message)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
 | 
				
			||||||
 | 
					  v8::HandleScope handle_scope(isolate);
 | 
				
			||||||
 | 
					  v8::Local<v8::Value> message_value =
 | 
				
			||||||
 | 
					      electron::DeserializeV8Value(isolate, message);
 | 
				
			||||||
 | 
					  EmitWithoutCustomEvent("message", message_value);
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					raw_ptr<UtilityProcessWrapper> UtilityProcessWrapper::FromProcessId(
 | 
				
			||||||
 | 
					    base::ProcessId pid) {
 | 
				
			||||||
 | 
					  auto* utility_process_wrapper = GetAllUtilityProcessWrappers().Lookup(pid);
 | 
				
			||||||
 | 
					  return !!utility_process_wrapper ? utility_process_wrapper : nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					gin::Handle<UtilityProcessWrapper> UtilityProcessWrapper::Create(
 | 
				
			||||||
 | 
					    gin::Arguments* args) {
 | 
				
			||||||
 | 
					  gin_helper::Dictionary dict;
 | 
				
			||||||
 | 
					  if (!args->GetNext(&dict)) {
 | 
				
			||||||
 | 
					    args->ThrowTypeError("Options must be an object.");
 | 
				
			||||||
 | 
					    return gin::Handle<UtilityProcessWrapper>();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::u16string display_name;
 | 
				
			||||||
 | 
					  bool use_plugin_helper = false;
 | 
				
			||||||
 | 
					  std::map<IOHandle, IOType> stdio;
 | 
				
			||||||
 | 
					  base::FilePath current_working_directory;
 | 
				
			||||||
 | 
					  base::EnvironmentMap env_map;
 | 
				
			||||||
 | 
					  node::mojom::NodeServiceParamsPtr params =
 | 
				
			||||||
 | 
					      node::mojom::NodeServiceParams::New();
 | 
				
			||||||
 | 
					  dict.Get("modulePath", ¶ms->script);
 | 
				
			||||||
 | 
					  if (dict.Has("args") && !dict.Get("args", ¶ms->args)) {
 | 
				
			||||||
 | 
					    args->ThrowTypeError("Invalid value for args");
 | 
				
			||||||
 | 
					    return gin::Handle<UtilityProcessWrapper>();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gin_helper::Dictionary opts;
 | 
				
			||||||
 | 
					  if (dict.Get("options", &opts)) {
 | 
				
			||||||
 | 
					    if (opts.Has("env") && !opts.Get("env", &env_map)) {
 | 
				
			||||||
 | 
					      args->ThrowTypeError("Invalid value for env");
 | 
				
			||||||
 | 
					      return gin::Handle<UtilityProcessWrapper>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opts.Has("execArgv") && !opts.Get("execArgv", ¶ms->exec_args)) {
 | 
				
			||||||
 | 
					      args->ThrowTypeError("Invalid value for execArgv");
 | 
				
			||||||
 | 
					      return gin::Handle<UtilityProcessWrapper>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    opts.Get("serviceName", &display_name);
 | 
				
			||||||
 | 
					    opts.Get("cwd", ¤t_working_directory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<std::string> stdio_arr{"ignore", "inherit", "inherit"};
 | 
				
			||||||
 | 
					    opts.Get("stdio", &stdio_arr);
 | 
				
			||||||
 | 
					    for (size_t i = 0; i < 3; i++) {
 | 
				
			||||||
 | 
					      IOType type;
 | 
				
			||||||
 | 
					      if (stdio_arr[i] == "ignore")
 | 
				
			||||||
 | 
					        type = IOType::IO_IGNORE;
 | 
				
			||||||
 | 
					      else if (stdio_arr[i] == "inherit")
 | 
				
			||||||
 | 
					        type = IOType::IO_INHERIT;
 | 
				
			||||||
 | 
					      else if (stdio_arr[i] == "pipe")
 | 
				
			||||||
 | 
					        type = IOType::IO_PIPE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      stdio.emplace(static_cast<IOHandle>(i), type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_MAC)
 | 
				
			||||||
 | 
					    opts.Get("allowLoadingUnsignedLibraries", &use_plugin_helper);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  auto handle = gin::CreateHandle(
 | 
				
			||||||
 | 
					      args->isolate(),
 | 
				
			||||||
 | 
					      new UtilityProcessWrapper(std::move(params), display_name,
 | 
				
			||||||
 | 
					                                std::move(stdio), env_map,
 | 
				
			||||||
 | 
					                                current_working_directory, use_plugin_helper));
 | 
				
			||||||
 | 
					  handle->Pin(args->isolate());
 | 
				
			||||||
 | 
					  return handle;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					gin::ObjectTemplateBuilder UtilityProcessWrapper::GetObjectTemplateBuilder(
 | 
				
			||||||
 | 
					    v8::Isolate* isolate) {
 | 
				
			||||||
 | 
					  return gin_helper::EventEmitterMixin<
 | 
				
			||||||
 | 
					             UtilityProcessWrapper>::GetObjectTemplateBuilder(isolate)
 | 
				
			||||||
 | 
					      .SetMethod("postMessage", &UtilityProcessWrapper::PostMessage)
 | 
				
			||||||
 | 
					      .SetMethod("kill", &UtilityProcessWrapper::Kill)
 | 
				
			||||||
 | 
					      .SetProperty("pid", &UtilityProcessWrapper::GetOSProcessId);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* UtilityProcessWrapper::GetTypeName() {
 | 
				
			||||||
 | 
					  return "UtilityProcessWrapper";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Initialize(v8::Local<v8::Object> exports,
 | 
				
			||||||
 | 
					                v8::Local<v8::Value> unused,
 | 
				
			||||||
 | 
					                v8::Local<v8::Context> context,
 | 
				
			||||||
 | 
					                void* priv) {
 | 
				
			||||||
 | 
					  v8::Isolate* isolate = context->GetIsolate();
 | 
				
			||||||
 | 
					  gin_helper::Dictionary dict(isolate, exports);
 | 
				
			||||||
 | 
					  dict.SetMethod("_fork", &electron::api::UtilityProcessWrapper::Create);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NODE_LINKED_MODULE_CONTEXT_AWARE(electron_browser_utility_process, Initialize)
 | 
				
			||||||
							
								
								
									
										100
									
								
								shell/browser/api/electron_api_utility_process.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								shell/browser/api/electron_api_utility_process.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,100 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_UTILITY_PROCESS_H_
 | 
				
			||||||
 | 
					#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_UTILITY_PROCESS_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "base/containers/id_map.h"
 | 
				
			||||||
 | 
					#include "base/environment.h"
 | 
				
			||||||
 | 
					#include "base/memory/weak_ptr.h"
 | 
				
			||||||
 | 
					#include "base/process/process_handle.h"
 | 
				
			||||||
 | 
					#include "gin/wrappable.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/connector.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/message.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/remote.h"
 | 
				
			||||||
 | 
					#include "shell/browser/event_emitter_mixin.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_helper/pinnable.h"
 | 
				
			||||||
 | 
					#include "shell/services/node/public/mojom/node_service.mojom.h"
 | 
				
			||||||
 | 
					#include "v8/include/v8.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace gin {
 | 
				
			||||||
 | 
					class Arguments;
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					class Handle;
 | 
				
			||||||
 | 
					}  // namespace gin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace base {
 | 
				
			||||||
 | 
					class Process;
 | 
				
			||||||
 | 
					}  // namespace base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace api {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UtilityProcessWrapper
 | 
				
			||||||
 | 
					    : public gin::Wrappable<UtilityProcessWrapper>,
 | 
				
			||||||
 | 
					      public gin_helper::Pinnable<UtilityProcessWrapper>,
 | 
				
			||||||
 | 
					      public gin_helper::EventEmitterMixin<UtilityProcessWrapper>,
 | 
				
			||||||
 | 
					      public mojo::MessageReceiver {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  enum class IOHandle : size_t { STDIN = 0, STDOUT = 1, STDERR = 2 };
 | 
				
			||||||
 | 
					  enum class IOType { IO_PIPE, IO_INHERIT, IO_IGNORE };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ~UtilityProcessWrapper() override;
 | 
				
			||||||
 | 
					  static gin::Handle<UtilityProcessWrapper> Create(gin::Arguments* args);
 | 
				
			||||||
 | 
					  static raw_ptr<UtilityProcessWrapper> FromProcessId(base::ProcessId pid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void Shutdown(int exit_code);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // gin::Wrappable
 | 
				
			||||||
 | 
					  static gin::WrapperInfo kWrapperInfo;
 | 
				
			||||||
 | 
					  gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
 | 
				
			||||||
 | 
					      v8::Isolate* isolate) override;
 | 
				
			||||||
 | 
					  const char* GetTypeName() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  UtilityProcessWrapper(node::mojom::NodeServiceParamsPtr params,
 | 
				
			||||||
 | 
					                        std::u16string display_name,
 | 
				
			||||||
 | 
					                        std::map<IOHandle, IOType> stdio,
 | 
				
			||||||
 | 
					                        base::EnvironmentMap env_map,
 | 
				
			||||||
 | 
					                        base::FilePath current_working_directory,
 | 
				
			||||||
 | 
					                        bool use_plugin_helper);
 | 
				
			||||||
 | 
					  void OnServiceProcessDisconnected(uint32_t error_code,
 | 
				
			||||||
 | 
					                                    const std::string& description);
 | 
				
			||||||
 | 
					  void OnServiceProcessLaunched(const base::Process& process);
 | 
				
			||||||
 | 
					  void CloseConnectorPort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void PostMessage(gin::Arguments* args);
 | 
				
			||||||
 | 
					  bool Kill() const;
 | 
				
			||||||
 | 
					  v8::Local<v8::Value> GetOSProcessId(v8::Isolate* isolate) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // mojo::MessageReceiver
 | 
				
			||||||
 | 
					  bool Accept(mojo::Message* mojo_message) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  base::ProcessId pid_ = base::kNullProcessId;
 | 
				
			||||||
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
 | 
					  // Non-owning handles, these will be closed when the
 | 
				
			||||||
 | 
					  // corresponding FD are closed via _close.
 | 
				
			||||||
 | 
					  HANDLE stdout_read_handle_;
 | 
				
			||||||
 | 
					  HANDLE stderr_read_handle_;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  int stdout_read_fd_ = -1;
 | 
				
			||||||
 | 
					  int stderr_read_fd_ = -1;
 | 
				
			||||||
 | 
					  bool connector_closed_ = false;
 | 
				
			||||||
 | 
					  std::unique_ptr<mojo::Connector> connector_;
 | 
				
			||||||
 | 
					  blink::MessagePortDescriptor host_port_;
 | 
				
			||||||
 | 
					  mojo::Remote<node::mojom::NodeService> node_service_remote_;
 | 
				
			||||||
 | 
					  base::WeakPtrFactory<UtilityProcessWrapper> weak_factory_{this};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif  // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_UTILITY_PROCESS_H_
 | 
				
			||||||
| 
						 | 
					@ -24,13 +24,17 @@
 | 
				
			||||||
#include "components/os_crypt/key_storage_config_linux.h"
 | 
					#include "components/os_crypt/key_storage_config_linux.h"
 | 
				
			||||||
#include "components/os_crypt/os_crypt.h"
 | 
					#include "components/os_crypt/os_crypt.h"
 | 
				
			||||||
#include "content/browser/browser_main_loop.h"  // nogncheck
 | 
					#include "content/browser/browser_main_loop.h"  // nogncheck
 | 
				
			||||||
 | 
					#include "content/public/browser/browser_child_process_host_delegate.h"
 | 
				
			||||||
 | 
					#include "content/public/browser/browser_child_process_host_iterator.h"
 | 
				
			||||||
#include "content/public/browser/browser_thread.h"
 | 
					#include "content/public/browser/browser_thread.h"
 | 
				
			||||||
 | 
					#include "content/public/browser/child_process_data.h"
 | 
				
			||||||
#include "content/public/browser/child_process_security_policy.h"
 | 
					#include "content/public/browser/child_process_security_policy.h"
 | 
				
			||||||
#include "content/public/browser/device_service.h"
 | 
					#include "content/public/browser/device_service.h"
 | 
				
			||||||
#include "content/public/browser/first_party_sets_handler.h"
 | 
					#include "content/public/browser/first_party_sets_handler.h"
 | 
				
			||||||
#include "content/public/browser/web_ui_controller_factory.h"
 | 
					#include "content/public/browser/web_ui_controller_factory.h"
 | 
				
			||||||
#include "content/public/common/content_features.h"
 | 
					#include "content/public/common/content_features.h"
 | 
				
			||||||
#include "content/public/common/content_switches.h"
 | 
					#include "content/public/common/content_switches.h"
 | 
				
			||||||
 | 
					#include "content/public/common/process_type.h"
 | 
				
			||||||
#include "content/public/common/result_codes.h"
 | 
					#include "content/public/common/result_codes.h"
 | 
				
			||||||
#include "electron/buildflags/buildflags.h"
 | 
					#include "electron/buildflags/buildflags.h"
 | 
				
			||||||
#include "electron/fuses.h"
 | 
					#include "electron/fuses.h"
 | 
				
			||||||
| 
						 | 
					@ -39,6 +43,7 @@
 | 
				
			||||||
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
 | 
					#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
 | 
				
			||||||
#include "shell/app/electron_main_delegate.h"
 | 
					#include "shell/app/electron_main_delegate.h"
 | 
				
			||||||
#include "shell/browser/api/electron_api_app.h"
 | 
					#include "shell/browser/api/electron_api_app.h"
 | 
				
			||||||
 | 
					#include "shell/browser/api/electron_api_utility_process.h"
 | 
				
			||||||
#include "shell/browser/browser.h"
 | 
					#include "shell/browser/browser.h"
 | 
				
			||||||
#include "shell/browser/browser_process_impl.h"
 | 
					#include "shell/browser/browser_process_impl.h"
 | 
				
			||||||
#include "shell/browser/electron_browser_client.h"
 | 
					#include "shell/browser/electron_browser_client.h"
 | 
				
			||||||
| 
						 | 
					@ -273,12 +278,15 @@ void ElectronBrowserMainParts::PostEarlyInitialization() {
 | 
				
			||||||
  // Add Electron extended APIs.
 | 
					  // Add Electron extended APIs.
 | 
				
			||||||
  electron_bindings_->BindTo(js_env_->isolate(), env->process_object());
 | 
					  electron_bindings_->BindTo(js_env_->isolate(), env->process_object());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Load everything.
 | 
					  // Create explicit microtasks runner.
 | 
				
			||||||
  node_bindings_->LoadEnvironment(env);
 | 
					  js_env_->CreateMicrotasksRunner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Wrap the uv loop with global env.
 | 
					  // Wrap the uv loop with global env.
 | 
				
			||||||
  node_bindings_->set_uv_env(env);
 | 
					  node_bindings_->set_uv_env(env);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Load everything.
 | 
				
			||||||
 | 
					  node_bindings_->LoadEnvironment(env);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // We already initialized the feature list in PreEarlyInitialization(), but
 | 
					  // We already initialized the feature list in PreEarlyInitialization(), but
 | 
				
			||||||
  // the user JS script would not have had a chance to alter the command-line
 | 
					  // the user JS script would not have had a chance to alter the command-line
 | 
				
			||||||
  // switches at that point. Lets reinitialize it here to pick up the
 | 
					  // switches at that point. Lets reinitialize it here to pick up the
 | 
				
			||||||
| 
						 | 
					@ -503,7 +511,6 @@ int ElectronBrowserMainParts::PreMainMessageLoopRun() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ElectronBrowserMainParts::WillRunMainMessageLoop(
 | 
					void ElectronBrowserMainParts::WillRunMainMessageLoop(
 | 
				
			||||||
    std::unique_ptr<base::RunLoop>& run_loop) {
 | 
					    std::unique_ptr<base::RunLoop>& run_loop) {
 | 
				
			||||||
  js_env_->OnMessageLoopCreated();
 | 
					 | 
				
			||||||
  exit_code_ = content::RESULT_CODE_NORMAL_EXIT;
 | 
					  exit_code_ = content::RESULT_CODE_NORMAL_EXIT;
 | 
				
			||||||
  Browser::Get()->SetMainMessageLoopQuitClosure(
 | 
					  Browser::Get()->SetMainMessageLoopQuitClosure(
 | 
				
			||||||
      run_loop->QuitWhenIdleClosure());
 | 
					      run_loop->QuitWhenIdleClosure());
 | 
				
			||||||
| 
						 | 
					@ -565,10 +572,39 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Shutdown utility process created with Electron API before
 | 
				
			||||||
 | 
					  // stopping Node.js so that exit events can be emitted. We don't let
 | 
				
			||||||
 | 
					  // content layer perform this action since it destroys
 | 
				
			||||||
 | 
					  // child process only after this step (PostMainMessageLoopRun) via
 | 
				
			||||||
 | 
					  // BrowserProcessIOThread::ProcessHostCleanUp() which is too late for our
 | 
				
			||||||
 | 
					  // use case.
 | 
				
			||||||
 | 
					  // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/browser_main_loop.cc;l=1086-1108
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // The following logic is based on
 | 
				
			||||||
 | 
					  // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/browser_process_io_thread.cc;l=127-159
 | 
				
			||||||
 | 
					  //
 | 
				
			||||||
 | 
					  // Although content::BrowserChildProcessHostIterator is only to be called from
 | 
				
			||||||
 | 
					  // IO thread, it is safe to call from PostMainMessageLoopRun because thread
 | 
				
			||||||
 | 
					  // restrictions have been lifted.
 | 
				
			||||||
 | 
					  // https://source.chromium.org/chromium/chromium/src/+/main:content/browser/browser_main_loop.cc;l=1062-1078
 | 
				
			||||||
 | 
					  for (content::BrowserChildProcessHostIterator it(
 | 
				
			||||||
 | 
					           content::PROCESS_TYPE_UTILITY);
 | 
				
			||||||
 | 
					       !it.Done(); ++it) {
 | 
				
			||||||
 | 
					    if (it.GetDelegate()->GetServiceName() == node::mojom::NodeService::Name_) {
 | 
				
			||||||
 | 
					      auto& process = it.GetData().GetProcess();
 | 
				
			||||||
 | 
					      if (!process.IsValid())
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      auto utility_process_wrapper =
 | 
				
			||||||
 | 
					          api::UtilityProcessWrapper::FromProcessId(process.Pid());
 | 
				
			||||||
 | 
					      if (utility_process_wrapper)
 | 
				
			||||||
 | 
					        utility_process_wrapper->Shutdown(0 /* exit_code */);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Destroy node platform after all destructors_ are executed, as they may
 | 
					  // Destroy node platform after all destructors_ are executed, as they may
 | 
				
			||||||
  // invoke Node/V8 APIs inside them.
 | 
					  // invoke Node/V8 APIs inside them.
 | 
				
			||||||
  node_env_->env()->set_trace_sync_io(false);
 | 
					  node_env_->env()->set_trace_sync_io(false);
 | 
				
			||||||
  js_env_->OnMessageLoopDestroying();
 | 
					  js_env_->DestroyMicrotasksRunner();
 | 
				
			||||||
  node::Stop(node_env_->env());
 | 
					  node::Stop(node_env_->env());
 | 
				
			||||||
  node_env_.reset();
 | 
					  node_env_.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,17 @@ class EventEmitterMixin {
 | 
				
			||||||
                         std::forward<Args>(args)...);
 | 
					                         std::forward<Args>(args)...);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // this.emit(name, args...);
 | 
				
			||||||
 | 
					  template <typename... Args>
 | 
				
			||||||
 | 
					  void EmitWithoutCustomEvent(base::StringPiece name, Args&&... args) {
 | 
				
			||||||
 | 
					    v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
 | 
				
			||||||
 | 
					    v8::HandleScope handle_scope(isolate);
 | 
				
			||||||
 | 
					    v8::Local<v8::Object> wrapper;
 | 
				
			||||||
 | 
					    if (!static_cast<T*>(this)->GetWrapper(isolate).ToLocal(&wrapper))
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    gin_helper::EmitEvent(isolate, wrapper, name, std::forward<Args>(args)...);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // this.emit(name, event, args...);
 | 
					  // this.emit(name, event, args...);
 | 
				
			||||||
  template <typename... Args>
 | 
					  template <typename... Args>
 | 
				
			||||||
  bool EmitCustomEvent(base::StringPiece name,
 | 
					  bool EmitCustomEvent(base::StringPiece name,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -287,13 +287,13 @@ v8::Isolate* JavascriptEnvironment::GetIsolate() {
 | 
				
			||||||
  return g_isolate;
 | 
					  return g_isolate;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JavascriptEnvironment::OnMessageLoopCreated() {
 | 
					void JavascriptEnvironment::CreateMicrotasksRunner() {
 | 
				
			||||||
  DCHECK(!microtasks_runner_);
 | 
					  DCHECK(!microtasks_runner_);
 | 
				
			||||||
  microtasks_runner_ = std::make_unique<MicrotasksRunner>(isolate());
 | 
					  microtasks_runner_ = std::make_unique<MicrotasksRunner>(isolate());
 | 
				
			||||||
  base::CurrentThread::Get()->AddTaskObserver(microtasks_runner_.get());
 | 
					  base::CurrentThread::Get()->AddTaskObserver(microtasks_runner_.get());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JavascriptEnvironment::OnMessageLoopDestroying() {
 | 
					void JavascriptEnvironment::DestroyMicrotasksRunner() {
 | 
				
			||||||
  DCHECK(microtasks_runner_);
 | 
					  DCHECK(microtasks_runner_);
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    v8::HandleScope scope(isolate_);
 | 
					    v8::HandleScope scope(isolate_);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,8 +29,8 @@ class JavascriptEnvironment {
 | 
				
			||||||
  JavascriptEnvironment(const JavascriptEnvironment&) = delete;
 | 
					  JavascriptEnvironment(const JavascriptEnvironment&) = delete;
 | 
				
			||||||
  JavascriptEnvironment& operator=(const JavascriptEnvironment&) = delete;
 | 
					  JavascriptEnvironment& operator=(const JavascriptEnvironment&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void OnMessageLoopCreated();
 | 
					  void CreateMicrotasksRunner();
 | 
				
			||||||
  void OnMessageLoopDestroying();
 | 
					  void DestroyMicrotasksRunner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  node::MultiIsolatePlatform* platform() const { return platform_; }
 | 
					  node::MultiIsolatePlatform* platform() const { return platform_; }
 | 
				
			||||||
  v8::Isolate* isolate() const { return isolate_; }
 | 
					  v8::Isolate* isolate() const { return isolate_; }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,7 @@
 | 
				
			||||||
  V(electron_browser_system_preferences) \
 | 
					  V(electron_browser_system_preferences) \
 | 
				
			||||||
  V(electron_browser_base_window)        \
 | 
					  V(electron_browser_base_window)        \
 | 
				
			||||||
  V(electron_browser_tray)               \
 | 
					  V(electron_browser_tray)               \
 | 
				
			||||||
 | 
					  V(electron_browser_utility_process)    \
 | 
				
			||||||
  V(electron_browser_view)               \
 | 
					  V(electron_browser_view)               \
 | 
				
			||||||
  V(electron_browser_web_contents)       \
 | 
					  V(electron_browser_web_contents)       \
 | 
				
			||||||
  V(electron_browser_web_contents_view)  \
 | 
					  V(electron_browser_web_contents_view)  \
 | 
				
			||||||
| 
						 | 
					@ -87,7 +88,8 @@
 | 
				
			||||||
  V(electron_renderer_context_bridge)    \
 | 
					  V(electron_renderer_context_bridge)    \
 | 
				
			||||||
  V(electron_renderer_crash_reporter)    \
 | 
					  V(electron_renderer_crash_reporter)    \
 | 
				
			||||||
  V(electron_renderer_ipc)               \
 | 
					  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)
 | 
					#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> argv = {"electron"};
 | 
				
			||||||
  std::vector<std::string> exec_argv;
 | 
					  std::vector<std::string> exec_argv;
 | 
				
			||||||
  std::vector<std::string> errors;
 | 
					  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())
 | 
					  if (!fuses::IsNodeOptionsEnabled())
 | 
				
			||||||
    process_flags |= node::ProcessFlags::kDisableNodeOptionsEnv;
 | 
					    process_flags |= node::ProcessFlags::kDisableNodeOptionsEnv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -417,16 +423,9 @@ void NodeBindings::Initialize() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
node::Environment* NodeBindings::CreateEnvironment(
 | 
					node::Environment* NodeBindings::CreateEnvironment(
 | 
				
			||||||
    v8::Handle<v8::Context> context,
 | 
					    v8::Handle<v8::Context> context,
 | 
				
			||||||
    node::MultiIsolatePlatform* platform) {
 | 
					    node::MultiIsolatePlatform* platform,
 | 
				
			||||||
#if BUILDFLAG(IS_WIN)
 | 
					    std::vector<std::string> args,
 | 
				
			||||||
  auto& atom_args = ElectronCommandLine::argv();
 | 
					    std::vector<std::string> exec_args) {
 | 
				
			||||||
  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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Feed node the path to initialization script.
 | 
					  // Feed node the path to initialization script.
 | 
				
			||||||
  std::string process_type;
 | 
					  std::string process_type;
 | 
				
			||||||
  switch (browser_env_) {
 | 
					  switch (browser_env_) {
 | 
				
			||||||
| 
						 | 
					@ -439,14 +438,20 @@ node::Environment* NodeBindings::CreateEnvironment(
 | 
				
			||||||
    case BrowserEnvironment::kWorker:
 | 
					    case BrowserEnvironment::kWorker:
 | 
				
			||||||
      process_type = "worker";
 | 
					      process_type = "worker";
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case BrowserEnvironment::kUtility:
 | 
				
			||||||
 | 
					      process_type = "utility";
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  v8::Isolate* isolate = context->GetIsolate();
 | 
					  v8::Isolate* isolate = context->GetIsolate();
 | 
				
			||||||
  gin_helper::Dictionary global(isolate, context->Global());
 | 
					  gin_helper::Dictionary global(isolate, context->Global());
 | 
				
			||||||
  // Do not set DOM globals for renderer process.
 | 
					  // Avoids overriding globals like setImmediate, clearImmediate
 | 
				
			||||||
  // We must set this before the node bootstrapper which is run inside
 | 
					  // queueMicrotask etc during the bootstrap phase of Node.js
 | 
				
			||||||
  // CreateEnvironment
 | 
					  // for processes that already have these defined by DOM.
 | 
				
			||||||
  if (browser_env_ != BrowserEnvironment::kBrowser)
 | 
					  // 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);
 | 
					    global.Set("_noBrowserGlobals", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (browser_env_ == BrowserEnvironment::kBrowser) {
 | 
					  if (browser_env_ == BrowserEnvironment::kBrowser) {
 | 
				
			||||||
| 
						 | 
					@ -464,7 +469,6 @@ node::Environment* NodeBindings::CreateEnvironment(
 | 
				
			||||||
                             : search_paths));
 | 
					                             : search_paths));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<std::string> exec_args;
 | 
					 | 
				
			||||||
  base::FilePath resources_path = GetResourcesPath();
 | 
					  base::FilePath resources_path = GetResourcesPath();
 | 
				
			||||||
  std::string init_script = "electron/js2c/" + process_type + "_init";
 | 
					  std::string init_script = "electron/js2c/" + process_type + "_init";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -478,7 +482,8 @@ node::Environment* NodeBindings::CreateEnvironment(
 | 
				
			||||||
                   node::EnvironmentFlags::kHideConsoleWindows |
 | 
					                   node::EnvironmentFlags::kHideConsoleWindows |
 | 
				
			||||||
                   node::EnvironmentFlags::kNoGlobalSearchPaths;
 | 
					                   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 -
 | 
					    // Only one ESM loader can be registered per isolate -
 | 
				
			||||||
    // in renderer processes this should be blink. We need to tell Node.js
 | 
					    // in renderer processes this should be blink. We need to tell Node.js
 | 
				
			||||||
    // not to register its handler (overriding blinks) in non-browser processes.
 | 
					    // 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
 | 
					  // Clean up the global _noBrowserGlobals that we unironically injected into
 | 
				
			||||||
  // the global scope
 | 
					  // 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
 | 
					    // We need to bootstrap the env in non-browser processes so that
 | 
				
			||||||
    // _noBrowserGlobals is read correctly before we remove it
 | 
					    // _noBrowserGlobals is read correctly before we remove it
 | 
				
			||||||
    global.Delete("_noBrowserGlobals");
 | 
					    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 don't want to abort either in the renderer or browser processes.
 | 
				
			||||||
  // We already listen for uncaught exceptions and handle them there.
 | 
					  // We already listen for uncaught exceptions and handle them there.
 | 
				
			||||||
  is.should_abort_on_uncaught_exception_callback = [](v8::Isolate*) {
 | 
					  // For utility process we expect the process to behave as standard
 | 
				
			||||||
    return false;
 | 
					  // 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
 | 
					  // Use a custom callback here to allow us to leverage Blink's logic in the
 | 
				
			||||||
  // renderer process.
 | 
					  // renderer process.
 | 
				
			||||||
  is.allow_wasm_code_generation_callback = AllowWasmCodeGenerationCallback;
 | 
					  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.
 | 
					    // Node.js requires that microtask checkpoints be explicitly invoked.
 | 
				
			||||||
    is.policy = v8::MicrotasksPolicy::kExplicit;
 | 
					    is.policy = v8::MicrotasksPolicy::kExplicit;
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
| 
						 | 
					@ -585,6 +597,20 @@ node::Environment* NodeBindings::CreateEnvironment(
 | 
				
			||||||
  return env;
 | 
					  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) {
 | 
					void NodeBindings::LoadEnvironment(node::Environment* env) {
 | 
				
			||||||
  node::LoadEnvironment(env, node::StartExecutionCallback{});
 | 
					  node::LoadEnvironment(env, node::StartExecutionCallback{});
 | 
				
			||||||
  gin_helper::EmitEvent(env->isolate(), env->process_object(), "loaded");
 | 
					  gin_helper::EmitEvent(env->isolate(), env->process_object(), "loaded");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,9 @@
 | 
				
			||||||
#ifndef ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
 | 
					#ifndef ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
 | 
				
			||||||
#define ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
 | 
					#define ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
#include <type_traits>
 | 
					#include <type_traits>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "base/files/file_path.h"
 | 
					#include "base/files/file_path.h"
 | 
				
			||||||
#include "base/memory/weak_ptr.h"
 | 
					#include "base/memory/weak_ptr.h"
 | 
				
			||||||
| 
						 | 
					@ -74,7 +76,7 @@ class UvHandle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NodeBindings {
 | 
					class NodeBindings {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  enum class BrowserEnvironment { kBrowser, kRenderer, kWorker };
 | 
					  enum class BrowserEnvironment { kBrowser, kRenderer, kUtility, kWorker };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static NodeBindings* Create(BrowserEnvironment browser_env);
 | 
					  static NodeBindings* Create(BrowserEnvironment browser_env);
 | 
				
			||||||
  static void RegisterBuiltinModules();
 | 
					  static void RegisterBuiltinModules();
 | 
				
			||||||
| 
						 | 
					@ -86,6 +88,10 @@ class NodeBindings {
 | 
				
			||||||
  void Initialize();
 | 
					  void Initialize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Create the environment and load node.js.
 | 
					  // 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::Environment* CreateEnvironment(v8::Handle<v8::Context> context,
 | 
				
			||||||
                                       node::MultiIsolatePlatform* platform);
 | 
					                                       node::MultiIsolatePlatform* platform);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										104
									
								
								shell/services/node/node_service.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								shell/services/node/node_service.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "shell/services/node/node_service.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "base/command_line.h"
 | 
				
			||||||
 | 
					#include "base/strings/utf_string_conversions.h"
 | 
				
			||||||
 | 
					#include "shell/browser/javascript_environment.h"
 | 
				
			||||||
 | 
					#include "shell/common/api/electron_bindings.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_converters/file_path_converter.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_helper/dictionary.h"
 | 
				
			||||||
 | 
					#include "shell/common/node_bindings.h"
 | 
				
			||||||
 | 
					#include "shell/common/node_includes.h"
 | 
				
			||||||
 | 
					#include "shell/services/node/parent_port.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NodeService::NodeService(
 | 
				
			||||||
 | 
					    mojo::PendingReceiver<node::mojom::NodeService> receiver)
 | 
				
			||||||
 | 
					    : node_bindings_(
 | 
				
			||||||
 | 
					          NodeBindings::Create(NodeBindings::BrowserEnvironment::kUtility)),
 | 
				
			||||||
 | 
					      electron_bindings_(
 | 
				
			||||||
 | 
					          std::make_unique<ElectronBindings>(node_bindings_->uv_loop())) {
 | 
				
			||||||
 | 
					  if (receiver.is_valid())
 | 
				
			||||||
 | 
					    receiver_.Bind(std::move(receiver));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NodeService::~NodeService() {
 | 
				
			||||||
 | 
					  if (!node_env_stopped_) {
 | 
				
			||||||
 | 
					    node_env_->env()->set_trace_sync_io(false);
 | 
				
			||||||
 | 
					    js_env_->DestroyMicrotasksRunner();
 | 
				
			||||||
 | 
					    node::Stop(node_env_->env());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NodeService::Initialize(node::mojom::NodeServiceParamsPtr params) {
 | 
				
			||||||
 | 
					  if (NodeBindings::IsInitialized())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ParentPort::GetInstance()->Initialize(std::move(params->port));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  js_env_ = std::make_unique<JavascriptEnvironment>(node_bindings_->uv_loop());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  v8::HandleScope scope(js_env_->isolate());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  node_bindings_->Initialize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Append program path for process.argv0
 | 
				
			||||||
 | 
					  auto program = base::CommandLine::ForCurrentProcess()->GetProgram();
 | 
				
			||||||
 | 
					#if defined(OS_WIN)
 | 
				
			||||||
 | 
					  params->args.insert(params->args.begin(), base::WideToUTF8(program.value()));
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					  params->args.insert(params->args.begin(), program.value());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Create the global environment.
 | 
				
			||||||
 | 
					  node::Environment* env = node_bindings_->CreateEnvironment(
 | 
				
			||||||
 | 
					      js_env_->context(), js_env_->platform(), params->args, params->exec_args);
 | 
				
			||||||
 | 
					  node_env_ = std::make_unique<NodeEnvironment>(env);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  node::SetProcessExitHandler(env,
 | 
				
			||||||
 | 
					                              [this](node::Environment* env, int exit_code) {
 | 
				
			||||||
 | 
					                                // Destroy node platform.
 | 
				
			||||||
 | 
					                                env->set_trace_sync_io(false);
 | 
				
			||||||
 | 
					                                js_env_->DestroyMicrotasksRunner();
 | 
				
			||||||
 | 
					                                node::Stop(env);
 | 
				
			||||||
 | 
					                                node_env_stopped_ = true;
 | 
				
			||||||
 | 
					                                receiver_.ResetWithReason(exit_code, "");
 | 
				
			||||||
 | 
					                              });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  env->set_trace_sync_io(env->options()->trace_sync_io);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Add Electron extended APIs.
 | 
				
			||||||
 | 
					  electron_bindings_->BindTo(env->isolate(), env->process_object());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Add entry script to process object.
 | 
				
			||||||
 | 
					  gin_helper::Dictionary process(env->isolate(), env->process_object());
 | 
				
			||||||
 | 
					  process.SetHidden("_serviceStartupScript", params->script);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Setup microtask runner.
 | 
				
			||||||
 | 
					  js_env_->CreateMicrotasksRunner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Wrap the uv loop with global env.
 | 
				
			||||||
 | 
					  node_bindings_->set_uv_env(env);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // LoadEnvironment should be called after setting up
 | 
				
			||||||
 | 
					  // JavaScriptEnvironment including the microtask runner
 | 
				
			||||||
 | 
					  // since this call will start compilation and execution
 | 
				
			||||||
 | 
					  // of the entry script. If there is an uncaught exception
 | 
				
			||||||
 | 
					  // the exit handler set above will be triggered and it expects
 | 
				
			||||||
 | 
					  // both Node Env and JavaScriptEnviroment are setup to perform
 | 
				
			||||||
 | 
					  // a clean shutdown of this process.
 | 
				
			||||||
 | 
					  node_bindings_->LoadEnvironment(env);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Run entry script.
 | 
				
			||||||
 | 
					  node_bindings_->PrepareEmbedThread();
 | 
				
			||||||
 | 
					  node_bindings_->StartPolling();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron
 | 
				
			||||||
							
								
								
									
										44
									
								
								shell/services/node/node_service.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								shell/services/node/node_service.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ELECTRON_SHELL_SERVICES_NODE_NODE_SERVICE_H_
 | 
				
			||||||
 | 
					#define ELECTRON_SHELL_SERVICES_NODE_NODE_SERVICE_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/pending_receiver.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/receiver.h"
 | 
				
			||||||
 | 
					#include "shell/services/node/public/mojom/node_service.mojom.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ElectronBindings;
 | 
				
			||||||
 | 
					class JavascriptEnvironment;
 | 
				
			||||||
 | 
					class NodeBindings;
 | 
				
			||||||
 | 
					class NodeEnvironment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NodeService : public node::mojom::NodeService {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  explicit NodeService(
 | 
				
			||||||
 | 
					      mojo::PendingReceiver<node::mojom::NodeService> receiver);
 | 
				
			||||||
 | 
					  ~NodeService() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  NodeService(const NodeService&) = delete;
 | 
				
			||||||
 | 
					  NodeService& operator=(const NodeService&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // mojom::NodeService implementation:
 | 
				
			||||||
 | 
					  void Initialize(node::mojom::NodeServiceParamsPtr params) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  bool node_env_stopped_ = false;
 | 
				
			||||||
 | 
					  std::unique_ptr<JavascriptEnvironment> js_env_;
 | 
				
			||||||
 | 
					  std::unique_ptr<NodeBindings> node_bindings_;
 | 
				
			||||||
 | 
					  std::unique_ptr<ElectronBindings> electron_bindings_;
 | 
				
			||||||
 | 
					  std::unique_ptr<NodeEnvironment> node_env_;
 | 
				
			||||||
 | 
					  mojo::Receiver<node::mojom::NodeService> receiver_{this};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif  // ELECTRON_SHELL_SERVICES_NODE_NODE_SERVICE_H_
 | 
				
			||||||
							
								
								
									
										133
									
								
								shell/services/node/parent_port.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								shell/services/node/parent_port.cc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,133 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "shell/services/node/parent_port.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "base/no_destructor.h"
 | 
				
			||||||
 | 
					#include "gin/data_object_builder.h"
 | 
				
			||||||
 | 
					#include "gin/handle.h"
 | 
				
			||||||
 | 
					#include "shell/browser/api/message_port.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_helper/dictionary.h"
 | 
				
			||||||
 | 
					#include "shell/common/gin_helper/event_emitter_caller.h"
 | 
				
			||||||
 | 
					#include "shell/common/node_includes.h"
 | 
				
			||||||
 | 
					#include "shell/common/v8_value_serializer.h"
 | 
				
			||||||
 | 
					#include "third_party/blink/public/common/messaging/transferable_message_mojom_traits.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gin::WrapperInfo ParentPort::kWrapperInfo = {gin::kEmbedderNativeGin};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ParentPort* ParentPort::GetInstance() {
 | 
				
			||||||
 | 
					  static base::NoDestructor<ParentPort> instance;
 | 
				
			||||||
 | 
					  return instance.get();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ParentPort::ParentPort() = default;
 | 
				
			||||||
 | 
					ParentPort::~ParentPort() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ParentPort::Initialize(blink::MessagePortDescriptor port) {
 | 
				
			||||||
 | 
					  port_ = std::move(port);
 | 
				
			||||||
 | 
					  connector_ = std::make_unique<mojo::Connector>(
 | 
				
			||||||
 | 
					      port_.TakeHandleToEntangleWithEmbedder(),
 | 
				
			||||||
 | 
					      mojo::Connector::SINGLE_THREADED_SEND,
 | 
				
			||||||
 | 
					      base::ThreadTaskRunnerHandle::Get());
 | 
				
			||||||
 | 
					  connector_->PauseIncomingMethodCallProcessing();
 | 
				
			||||||
 | 
					  connector_->set_incoming_receiver(this);
 | 
				
			||||||
 | 
					  connector_->set_connection_error_handler(
 | 
				
			||||||
 | 
					      base::BindOnce(&ParentPort::Close, base::Unretained(this)));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ParentPort::PostMessage(v8::Local<v8::Value> message_value) {
 | 
				
			||||||
 | 
					  if (!connector_closed_ && connector_ && connector_->is_valid()) {
 | 
				
			||||||
 | 
					    v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
 | 
				
			||||||
 | 
					    blink::TransferableMessage transferable_message;
 | 
				
			||||||
 | 
					    electron::SerializeV8Value(isolate, message_value, &transferable_message);
 | 
				
			||||||
 | 
					    mojo::Message mojo_message =
 | 
				
			||||||
 | 
					        blink::mojom::TransferableMessage::WrapAsMessage(
 | 
				
			||||||
 | 
					            std::move(transferable_message));
 | 
				
			||||||
 | 
					    connector_->Accept(&mojo_message);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ParentPort::Close() {
 | 
				
			||||||
 | 
					  if (!connector_closed_ && connector_->is_valid()) {
 | 
				
			||||||
 | 
					    port_.GiveDisentangledHandle(connector_->PassMessagePipe());
 | 
				
			||||||
 | 
					    connector_ = nullptr;
 | 
				
			||||||
 | 
					    port_.Reset();
 | 
				
			||||||
 | 
					    connector_closed_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ParentPort::Start() {
 | 
				
			||||||
 | 
					  if (!connector_closed_ && connector_ && connector_->is_valid()) {
 | 
				
			||||||
 | 
					    connector_->ResumeIncomingMethodCallProcessing();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ParentPort::Pause() {
 | 
				
			||||||
 | 
					  if (!connector_closed_ && connector_ && connector_->is_valid()) {
 | 
				
			||||||
 | 
					    connector_->PauseIncomingMethodCallProcessing();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ParentPort::Accept(mojo::Message* mojo_message) {
 | 
				
			||||||
 | 
					  blink::TransferableMessage message;
 | 
				
			||||||
 | 
					  if (!blink::mojom::TransferableMessage::DeserializeFromMessage(
 | 
				
			||||||
 | 
					          std::move(*mojo_message), &message)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
 | 
				
			||||||
 | 
					  v8::HandleScope handle_scope(isolate);
 | 
				
			||||||
 | 
					  auto wrapped_ports =
 | 
				
			||||||
 | 
					      MessagePort::EntanglePorts(isolate, std::move(message.ports));
 | 
				
			||||||
 | 
					  v8::Local<v8::Value> message_value =
 | 
				
			||||||
 | 
					      electron::DeserializeV8Value(isolate, message);
 | 
				
			||||||
 | 
					  v8::Local<v8::Object> self;
 | 
				
			||||||
 | 
					  if (!GetWrapper(isolate).ToLocal(&self))
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  auto event = gin::DataObjectBuilder(isolate)
 | 
				
			||||||
 | 
					                   .Set("data", message_value)
 | 
				
			||||||
 | 
					                   .Set("ports", wrapped_ports)
 | 
				
			||||||
 | 
					                   .Build();
 | 
				
			||||||
 | 
					  gin_helper::EmitEvent(isolate, self, "message", event);
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					gin::Handle<ParentPort> ParentPort::Create(v8::Isolate* isolate) {
 | 
				
			||||||
 | 
					  return gin::CreateHandle(isolate, ParentPort::GetInstance());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// static
 | 
				
			||||||
 | 
					gin::ObjectTemplateBuilder ParentPort::GetObjectTemplateBuilder(
 | 
				
			||||||
 | 
					    v8::Isolate* isolate) {
 | 
				
			||||||
 | 
					  return gin::Wrappable<ParentPort>::GetObjectTemplateBuilder(isolate)
 | 
				
			||||||
 | 
					      .SetMethod("postMessage", &ParentPort::PostMessage)
 | 
				
			||||||
 | 
					      .SetMethod("start", &ParentPort::Start)
 | 
				
			||||||
 | 
					      .SetMethod("pause", &ParentPort::Pause);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* ParentPort::GetTypeName() {
 | 
				
			||||||
 | 
					  return "ParentPort";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Initialize(v8::Local<v8::Object> exports,
 | 
				
			||||||
 | 
					                v8::Local<v8::Value> unused,
 | 
				
			||||||
 | 
					                v8::Local<v8::Context> context,
 | 
				
			||||||
 | 
					                void* priv) {
 | 
				
			||||||
 | 
					  v8::Isolate* isolate = context->GetIsolate();
 | 
				
			||||||
 | 
					  gin_helper::Dictionary dict(isolate, exports);
 | 
				
			||||||
 | 
					  dict.SetMethod("createParentPort", &electron::ParentPort::Create);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NODE_LINKED_MODULE_CONTEXT_AWARE(electron_utility_parent_port, Initialize)
 | 
				
			||||||
							
								
								
									
										68
									
								
								shell/services/node/parent_port.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								shell/services/node/parent_port.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ELECTRON_SHELL_SERVICES_NODE_PARENT_PORT_H_
 | 
				
			||||||
 | 
					#define ELECTRON_SHELL_SERVICES_NODE_PARENT_PORT_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "gin/wrappable.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/connector.h"
 | 
				
			||||||
 | 
					#include "mojo/public/cpp/bindings/message.h"
 | 
				
			||||||
 | 
					#include "shell/browser/event_emitter_mixin.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace v8 {
 | 
				
			||||||
 | 
					template <class T>
 | 
				
			||||||
 | 
					class Local;
 | 
				
			||||||
 | 
					class Value;
 | 
				
			||||||
 | 
					class Isolate;
 | 
				
			||||||
 | 
					}  // namespace v8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace gin {
 | 
				
			||||||
 | 
					class Arguments;
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					class Handle;
 | 
				
			||||||
 | 
					}  // namespace gin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace electron {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// There is only a single instance of this class
 | 
				
			||||||
 | 
					// for the lifetime of a Utility Process which
 | 
				
			||||||
 | 
					// also means that GC lifecycle is ignored by this class.
 | 
				
			||||||
 | 
					class ParentPort : public gin::Wrappable<ParentPort>,
 | 
				
			||||||
 | 
					                   public mojo::MessageReceiver {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  static ParentPort* GetInstance();
 | 
				
			||||||
 | 
					  static gin::Handle<ParentPort> Create(v8::Isolate* isolate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ParentPort(const ParentPort&) = delete;
 | 
				
			||||||
 | 
					  ParentPort& operator=(const ParentPort&) = delete;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ParentPort();
 | 
				
			||||||
 | 
					  ~ParentPort() override;
 | 
				
			||||||
 | 
					  void Initialize(blink::MessagePortDescriptor port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // gin::Wrappable
 | 
				
			||||||
 | 
					  static gin::WrapperInfo kWrapperInfo;
 | 
				
			||||||
 | 
					  gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
 | 
				
			||||||
 | 
					      v8::Isolate* isolate) override;
 | 
				
			||||||
 | 
					  const char* GetTypeName() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  void PostMessage(v8::Local<v8::Value> message_value);
 | 
				
			||||||
 | 
					  void Close();
 | 
				
			||||||
 | 
					  void Start();
 | 
				
			||||||
 | 
					  void Pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // mojo::MessageReceiver
 | 
				
			||||||
 | 
					  bool Accept(mojo::Message* mojo_message) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool connector_closed_ = false;
 | 
				
			||||||
 | 
					  std::unique_ptr<mojo::Connector> connector_;
 | 
				
			||||||
 | 
					  blink::MessagePortDescriptor port_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace electron
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif  // ELECTRON_SHELL_SERVICES_NODE_PARENT_PORT_H_
 | 
				
			||||||
							
								
								
									
										14
									
								
								shell/services/node/public/mojom/BUILD.gn
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								shell/services/node/public/mojom/BUILD.gn
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					# Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					# Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					# found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import("//mojo/public/tools/bindings/mojom.gni")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mojom("mojom") {
 | 
				
			||||||
 | 
					  sources = [ "node_service.mojom" ]
 | 
				
			||||||
 | 
					  public_deps = [
 | 
				
			||||||
 | 
					    "//mojo/public/mojom/base",
 | 
				
			||||||
 | 
					    "//sandbox/policy/mojom",
 | 
				
			||||||
 | 
					    "//third_party/blink/public/mojom:mojom_core",
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								shell/services/node/public/mojom/node_service.mojom
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								shell/services/node/public/mojom/node_service.mojom
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					// Copyright (c) 2022 Microsoft, Inc.
 | 
				
			||||||
 | 
					// Use of this source code is governed by the MIT license that can be
 | 
				
			||||||
 | 
					// found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module node.mojom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mojo/public/mojom/base/file_path.mojom";
 | 
				
			||||||
 | 
					import "sandbox/policy/mojom/sandbox.mojom";
 | 
				
			||||||
 | 
					import "third_party/blink/public/mojom/messaging/message_port_descriptor.mojom";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct NodeServiceParams {
 | 
				
			||||||
 | 
					  mojo_base.mojom.FilePath script;
 | 
				
			||||||
 | 
					  array<string> args;
 | 
				
			||||||
 | 
					  array<string> exec_args;
 | 
				
			||||||
 | 
					  blink.mojom.MessagePortDescriptor port;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ServiceSandbox=sandbox.mojom.Sandbox.kNoSandbox]
 | 
				
			||||||
 | 
					interface NodeService {
 | 
				
			||||||
 | 
					  Initialize(NodeServiceParams params);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,8 @@
 | 
				
			||||||
#include "services/proxy_resolver/proxy_resolver_factory_impl.h"
 | 
					#include "services/proxy_resolver/proxy_resolver_factory_impl.h"
 | 
				
			||||||
#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
 | 
					#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
 | 
				
			||||||
#include "services/service_manager/public/cpp/service.h"
 | 
					#include "services/service_manager/public/cpp/service.h"
 | 
				
			||||||
 | 
					#include "shell/services/node/node_service.h"
 | 
				
			||||||
 | 
					#include "shell/services/node/public/mojom/node_service.mojom.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if BUILDFLAG(IS_WIN)
 | 
					#if BUILDFLAG(IS_WIN)
 | 
				
			||||||
#include "chrome/services/util_win/public/mojom/util_read_icon.mojom.h"
 | 
					#include "chrome/services/util_win/public/mojom/util_read_icon.mojom.h"
 | 
				
			||||||
| 
						 | 
					@ -72,6 +74,10 @@ auto RunProxyResolver(
 | 
				
			||||||
      std::move(receiver));
 | 
					      std::move(receiver));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto RunNodeService(mojo::PendingReceiver<node::mojom::NodeService> receiver) {
 | 
				
			||||||
 | 
					  return std::make_unique<electron::NodeService>(std::move(receiver));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElectronContentUtilityClient::ElectronContentUtilityClient() = default;
 | 
					ElectronContentUtilityClient::ElectronContentUtilityClient() = default;
 | 
				
			||||||
| 
						 | 
					@ -115,6 +121,8 @@ void ElectronContentUtilityClient::RegisterMainThreadServices(
 | 
				
			||||||
    (BUILDFLAG(ENABLE_PRINTING) && BUILDFLAG(IS_WIN))
 | 
					    (BUILDFLAG(ENABLE_PRINTING) && BUILDFLAG(IS_WIN))
 | 
				
			||||||
  services.Add(RunPrintingService);
 | 
					  services.Add(RunPrintingService);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  services.Add(RunNodeService);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ElectronContentUtilityClient::RegisterIOThreadServices(
 | 
					void ElectronContentUtilityClient::RegisterIOThreadServices(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										364
									
								
								spec/api-utility-process-spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								spec/api-utility-process-spec.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,364 @@
 | 
				
			||||||
 | 
					import { expect } from 'chai';
 | 
				
			||||||
 | 
					import * as childProcess from 'child_process';
 | 
				
			||||||
 | 
					import * as path from 'path';
 | 
				
			||||||
 | 
					import { BrowserWindow, MessageChannelMain, utilityProcess } from 'electron/main';
 | 
				
			||||||
 | 
					import { emittedOnce } from './events-helpers';
 | 
				
			||||||
 | 
					import { ifit } from './spec-helpers';
 | 
				
			||||||
 | 
					import { closeWindow } from './window-helpers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process');
 | 
				
			||||||
 | 
					const isWindowsOnArm = process.platform === 'win32' && process.arch === 'arm64';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('utilityProcess module', () => {
 | 
				
			||||||
 | 
					  describe('UtilityProcess constructor', () => {
 | 
				
			||||||
 | 
					    it('throws when empty script path is provided', async () => {
 | 
				
			||||||
 | 
					      expect(() => {
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					        utilityProcess.fork('');
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					      }).to.throw();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('throws when options.stdio is not valid', async () => {
 | 
				
			||||||
 | 
					      expect(() => {
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					        utilityProcess.fork(path.join(fixturesPath, 'empty.js'), [], {
 | 
				
			||||||
 | 
					          execArgv: ['--test', '--test2'],
 | 
				
			||||||
 | 
					          serviceName: 'test',
 | 
				
			||||||
 | 
					          stdio: 'ipc'
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					      }).to.throw(/stdio must be of the following values: inherit, pipe, ignore/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(() => {
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					        utilityProcess.fork(path.join(fixturesPath, 'empty.js'), [], {
 | 
				
			||||||
 | 
					          execArgv: ['--test', '--test2'],
 | 
				
			||||||
 | 
					          serviceName: 'test',
 | 
				
			||||||
 | 
					          stdio: ['ignore', 'ignore']
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					      }).to.throw(/configuration missing for stdin, stdout or stderr/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(() => {
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					        utilityProcess.fork(path.join(fixturesPath, 'empty.js'), [], {
 | 
				
			||||||
 | 
					          execArgv: ['--test', '--test2'],
 | 
				
			||||||
 | 
					          serviceName: 'test',
 | 
				
			||||||
 | 
					          stdio: ['pipe', 'inherit', 'inherit']
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        /* eslint-disable no-new */
 | 
				
			||||||
 | 
					      }).to.throw(/stdin value other than ignore is not supported/);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('lifecycle events', () => {
 | 
				
			||||||
 | 
					    it('emits \'spawn\' when child process successfully launches', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('emits \'exit\' when child process exits gracefully', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js'));
 | 
				
			||||||
 | 
					      const [code] = await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(code).to.equal(0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('emits \'exit\' when child process crashes', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'crash.js'));
 | 
				
			||||||
 | 
					      // Do not check for exit code in this case,
 | 
				
			||||||
 | 
					      // SIGSEGV code can be 139 or 11 across our different CI pipeline.
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('emits \'exit\' corresponding to the child process', async () => {
 | 
				
			||||||
 | 
					      const child1 = utilityProcess.fork(path.join(fixturesPath, 'endless.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child1, 'spawn');
 | 
				
			||||||
 | 
					      const child2 = utilityProcess.fork(path.join(fixturesPath, 'crash.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child2, 'exit');
 | 
				
			||||||
 | 
					      expect(child1.kill()).to.be.true();
 | 
				
			||||||
 | 
					      await emittedOnce(child1, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('emits \'exit\' when there is uncaught exception', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'exception.js'));
 | 
				
			||||||
 | 
					      const [code] = await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(code).to.equal(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('emits \'exit\' when process.exit is called', async () => {
 | 
				
			||||||
 | 
					      const exitCode = 2;
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'custom-exit.js'), [`--exitCode=${exitCode}`]);
 | 
				
			||||||
 | 
					      const [code] = await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(code).to.equal(exitCode);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('kill() API', () => {
 | 
				
			||||||
 | 
					    it('terminates the child process gracefully', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'endless.js'), [], {
 | 
				
			||||||
 | 
					        serviceName: 'endless'
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.kill()).to.be.true();
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('pid property', () => {
 | 
				
			||||||
 | 
					    it('is valid when child process launches successfully', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.pid).to.not.be.null();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('is undefined when child process fails to launch', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'does-not-exist.js'));
 | 
				
			||||||
 | 
					      expect(child.pid).to.be.undefined();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('stdout property', () => {
 | 
				
			||||||
 | 
					    it('is null when child process launches with default stdio', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stdout).to.be.null();
 | 
				
			||||||
 | 
					      expect(child.stderr).to.be.null();
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('is null when child process launches with ignore stdio configuration', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], {
 | 
				
			||||||
 | 
					        stdio: 'ignore'
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stdout).to.be.null();
 | 
				
			||||||
 | 
					      expect(child.stderr).to.be.null();
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('is valid when child process launches with pipe stdio configuration', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], {
 | 
				
			||||||
 | 
					        stdio: 'pipe'
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stdout).to.not.be.null();
 | 
				
			||||||
 | 
					      let log = '';
 | 
				
			||||||
 | 
					      child.stdout!.on('data', (chunk) => {
 | 
				
			||||||
 | 
					        log += chunk.toString('utf8');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(log).to.equal('hello\n');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('stderr property', () => {
 | 
				
			||||||
 | 
					    it('is null when child process launches with default stdio', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stdout).to.be.null();
 | 
				
			||||||
 | 
					      expect(child.stderr).to.be.null();
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('is null when child process launches with ignore stdio configuration', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], {
 | 
				
			||||||
 | 
					        stdio: 'ignore'
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stderr).to.be.null();
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ifit(!isWindowsOnArm)('is valid when child process launches with pipe stdio configuration', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], {
 | 
				
			||||||
 | 
					        stdio: ['ignore', 'pipe', 'pipe']
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stderr).to.not.be.null();
 | 
				
			||||||
 | 
					      let log = '';
 | 
				
			||||||
 | 
					      child.stderr!.on('data', (chunk) => {
 | 
				
			||||||
 | 
					        log += chunk.toString('utf8');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(log).to.equal('world');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('postMessage() API', () => {
 | 
				
			||||||
 | 
					    it('establishes a default ipc channel with the child process', async () => {
 | 
				
			||||||
 | 
					      const result = 'I will be echoed.';
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'post-message.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      child.postMessage(result);
 | 
				
			||||||
 | 
					      const [data] = await emittedOnce(child, 'message');
 | 
				
			||||||
 | 
					      expect(data).to.equal(result);
 | 
				
			||||||
 | 
					      const exit = emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(child.kill()).to.be.true();
 | 
				
			||||||
 | 
					      await exit;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('supports queuing messages on the receiving end', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'post-message-queue.js'));
 | 
				
			||||||
 | 
					      const p = emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      child.postMessage('This message');
 | 
				
			||||||
 | 
					      child.postMessage(' is');
 | 
				
			||||||
 | 
					      child.postMessage(' queued');
 | 
				
			||||||
 | 
					      await p;
 | 
				
			||||||
 | 
					      const [data] = await emittedOnce(child, 'message');
 | 
				
			||||||
 | 
					      expect(data).to.equal('This message is queued');
 | 
				
			||||||
 | 
					      const exit = emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(child.kill()).to.be.true();
 | 
				
			||||||
 | 
					      await exit;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('behavior', () => {
 | 
				
			||||||
 | 
					    it('supports starting the v8 inspector with --inspect-brk', (done) => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], {
 | 
				
			||||||
 | 
					        stdio: 'pipe',
 | 
				
			||||||
 | 
					        execArgv: ['--inspect-brk']
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let output = '';
 | 
				
			||||||
 | 
					      const cleanup = () => {
 | 
				
			||||||
 | 
					        child.stderr!.removeListener('data', listener);
 | 
				
			||||||
 | 
					        child.stdout!.removeListener('data', listener);
 | 
				
			||||||
 | 
					        child.once('exit', () => { done(); });
 | 
				
			||||||
 | 
					        child.kill();
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const listener = (data: Buffer) => {
 | 
				
			||||||
 | 
					        output += data;
 | 
				
			||||||
 | 
					        if (/Debugger listening on ws:/m.test(output)) {
 | 
				
			||||||
 | 
					          cleanup();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      child.stderr!.on('data', listener);
 | 
				
			||||||
 | 
					      child.stdout!.on('data', listener);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('supports starting the v8 inspector with --inspect and a provided port', (done) => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], {
 | 
				
			||||||
 | 
					        stdio: 'pipe',
 | 
				
			||||||
 | 
					        execArgv: ['--inspect=17364']
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let output = '';
 | 
				
			||||||
 | 
					      const cleanup = () => {
 | 
				
			||||||
 | 
					        child.stderr!.removeListener('data', listener);
 | 
				
			||||||
 | 
					        child.stdout!.removeListener('data', listener);
 | 
				
			||||||
 | 
					        child.once('exit', () => { done(); });
 | 
				
			||||||
 | 
					        child.kill();
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const listener = (data: Buffer) => {
 | 
				
			||||||
 | 
					        output += data;
 | 
				
			||||||
 | 
					        if (/Debugger listening on ws:/m.test(output)) {
 | 
				
			||||||
 | 
					          expect(output.trim()).to.contain(':17364', 'should be listening on port 17364');
 | 
				
			||||||
 | 
					          cleanup();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      child.stderr!.on('data', listener);
 | 
				
			||||||
 | 
					      child.stdout!.on('data', listener);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ifit(process.platform !== 'win32')('supports redirecting stdout to parent process', async () => {
 | 
				
			||||||
 | 
					      const result = 'Output from utility process';
 | 
				
			||||||
 | 
					      const appProcess = childProcess.spawn(process.execPath, [path.join(fixturesPath, 'inherit-stdout'), `--payload=${result}`]);
 | 
				
			||||||
 | 
					      let output = '';
 | 
				
			||||||
 | 
					      appProcess.stdout.on('data', (data: Buffer) => { output += data; });
 | 
				
			||||||
 | 
					      await emittedOnce(appProcess, 'exit');
 | 
				
			||||||
 | 
					      expect(output).to.equal(result);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ifit(process.platform !== 'win32')('supports redirecting stderr to parent process', async () => {
 | 
				
			||||||
 | 
					      const result = 'Error from utility process';
 | 
				
			||||||
 | 
					      const appProcess = childProcess.spawn(process.execPath, [path.join(fixturesPath, 'inherit-stderr'), `--payload=${result}`]);
 | 
				
			||||||
 | 
					      let output = '';
 | 
				
			||||||
 | 
					      appProcess.stderr.on('data', (data: Buffer) => { output += data; });
 | 
				
			||||||
 | 
					      await emittedOnce(appProcess, 'exit');
 | 
				
			||||||
 | 
					      expect(output).to.include(result);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('can establish communication channel with sandboxed renderer', async () => {
 | 
				
			||||||
 | 
					      const result = 'Message from sandboxed renderer';
 | 
				
			||||||
 | 
					      const w = new BrowserWindow({
 | 
				
			||||||
 | 
					        show: false,
 | 
				
			||||||
 | 
					        webPreferences: {
 | 
				
			||||||
 | 
					          preload: path.join(fixturesPath, 'preload.js')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await w.loadFile(path.join(__dirname, 'fixtures', 'blank.html'));
 | 
				
			||||||
 | 
					      // Create Message port pair for Renderer <-> Utility Process.
 | 
				
			||||||
 | 
					      const { port1: rendererPort, port2: childPort1 } = new MessageChannelMain();
 | 
				
			||||||
 | 
					      w.webContents.postMessage('port', result, [rendererPort]);
 | 
				
			||||||
 | 
					      // Send renderer and main channel port to utility process.
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'receive-message.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      child.postMessage('', [childPort1]);
 | 
				
			||||||
 | 
					      const [data] = await emittedOnce(child, 'message');
 | 
				
			||||||
 | 
					      expect(data).to.equal(result);
 | 
				
			||||||
 | 
					      // Cleanup.
 | 
				
			||||||
 | 
					      const exit = emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(child.kill()).to.be.true();
 | 
				
			||||||
 | 
					      await exit;
 | 
				
			||||||
 | 
					      await closeWindow(w);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ifit(process.platform === 'linux')('allows executing a setuid binary with child_process', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork(path.join(fixturesPath, 'suid.js'));
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      const [data] = await emittedOnce(child, 'message');
 | 
				
			||||||
 | 
					      expect(data).to.not.be.empty();
 | 
				
			||||||
 | 
					      const exit = emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(child.kill()).to.be.true();
 | 
				
			||||||
 | 
					      await exit;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('inherits parent env as default', async () => {
 | 
				
			||||||
 | 
					      const appProcess = childProcess.spawn(process.execPath, [path.join(fixturesPath, 'env-app')], {
 | 
				
			||||||
 | 
					        env: {
 | 
				
			||||||
 | 
					          FROM: 'parent',
 | 
				
			||||||
 | 
					          ...process.env
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      let output = '';
 | 
				
			||||||
 | 
					      appProcess.stdout.on('data', (data: Buffer) => { output += data; });
 | 
				
			||||||
 | 
					      await emittedOnce(appProcess.stdout, 'end');
 | 
				
			||||||
 | 
					      const result = process.platform === 'win32' ? '\r\nparent' : 'parent';
 | 
				
			||||||
 | 
					      expect(output).to.equal(result);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('does not inherit parent env when custom env is provided', async () => {
 | 
				
			||||||
 | 
					      const appProcess = childProcess.spawn(process.execPath, [path.join(fixturesPath, 'env-app'), '--create-custom-env'], {
 | 
				
			||||||
 | 
					        env: {
 | 
				
			||||||
 | 
					          FROM: 'parent',
 | 
				
			||||||
 | 
					          ...process.env
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      let output = '';
 | 
				
			||||||
 | 
					      appProcess.stdout.on('data', (data: Buffer) => { output += data; });
 | 
				
			||||||
 | 
					      await emittedOnce(appProcess.stdout, 'end');
 | 
				
			||||||
 | 
					      const result = process.platform === 'win32' ? '\r\nchild' : 'child';
 | 
				
			||||||
 | 
					      expect(output).to.equal(result);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('changes working directory with cwd', async () => {
 | 
				
			||||||
 | 
					      const child = utilityProcess.fork('./log.js', [], {
 | 
				
			||||||
 | 
					        cwd: fixturesPath,
 | 
				
			||||||
 | 
					        stdio: ['ignore', 'pipe', 'ignore']
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'spawn');
 | 
				
			||||||
 | 
					      expect(child.stdout).to.not.be.null();
 | 
				
			||||||
 | 
					      let log = '';
 | 
				
			||||||
 | 
					      child.stdout!.on('data', (chunk) => {
 | 
				
			||||||
 | 
					        log += chunk.toString('utf8');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await emittedOnce(child, 'exit');
 | 
				
			||||||
 | 
					      expect(log).to.equal('hello\n');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										1
									
								
								spec/fixtures/api/utility-process/crash.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								spec/fixtures/api/utility-process/crash.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					process.crash();
 | 
				
			||||||
							
								
								
									
										3
									
								
								spec/fixtures/api/utility-process/custom-exit.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fixtures/api/utility-process/custom-exit.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					const arg = process.argv[2];
 | 
				
			||||||
 | 
					const code = arg.split('=')[1];
 | 
				
			||||||
 | 
					process.exit(code);
 | 
				
			||||||
							
								
								
									
										1
									
								
								spec/fixtures/api/utility-process/empty.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								spec/fixtures/api/utility-process/empty.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					process.exit(0);
 | 
				
			||||||
							
								
								
									
										1
									
								
								spec/fixtures/api/utility-process/endless.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								spec/fixtures/api/utility-process/endless.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					setInterval(() => {}, 2000);
 | 
				
			||||||
							
								
								
									
										22
									
								
								spec/fixtures/api/utility-process/env-app/main.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								spec/fixtures/api/utility-process/env-app/main.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					const { app, utilityProcess } = require('electron');
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.whenReady().then(() => {
 | 
				
			||||||
 | 
					  let child = null;
 | 
				
			||||||
 | 
					  if (app.commandLine.hasSwitch('create-custom-env')) {
 | 
				
			||||||
 | 
					    child = utilityProcess.fork(path.join(__dirname, 'test.js'), {
 | 
				
			||||||
 | 
					      env: {
 | 
				
			||||||
 | 
					        FROM: 'child'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    child = utilityProcess.fork(path.join(__dirname, 'test.js'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  child.on('message', (data) => {
 | 
				
			||||||
 | 
					    process.stdout.write(data);
 | 
				
			||||||
 | 
					    process.stdout.end();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  child.on('exit', () => {
 | 
				
			||||||
 | 
					    app.quit();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										4
									
								
								spec/fixtures/api/utility-process/env-app/package.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								spec/fixtures/api/utility-process/env-app/package.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "electron-test-utility-process-env-app",
 | 
				
			||||||
 | 
					  "main": "main.js"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								spec/fixtures/api/utility-process/env-app/test.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								spec/fixtures/api/utility-process/env-app/test.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					process.parentPort.postMessage(process.env.FROM);
 | 
				
			||||||
 | 
					process.exit(0);
 | 
				
			||||||
							
								
								
									
										1
									
								
								spec/fixtures/api/utility-process/exception.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								spec/fixtures/api/utility-process/exception.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					nonExistingFunc(); // eslint-disable-line
 | 
				
			||||||
							
								
								
									
										10
									
								
								spec/fixtures/api/utility-process/inherit-stderr/main.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								spec/fixtures/api/utility-process/inherit-stderr/main.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					const { app, utilityProcess } = require('electron');
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.whenReady().then(() => {
 | 
				
			||||||
 | 
					  const payload = app.commandLine.getSwitchValue('payload');
 | 
				
			||||||
 | 
					  const child = utilityProcess.fork(path.join(__dirname, 'test.js'), [`--payload=${payload}`]);
 | 
				
			||||||
 | 
					  child.on('exit', () => {
 | 
				
			||||||
 | 
					    app.quit();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										4
									
								
								spec/fixtures/api/utility-process/inherit-stderr/package.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								spec/fixtures/api/utility-process/inherit-stderr/package.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "electron-test-utility-process-inherit-stderr",
 | 
				
			||||||
 | 
					  "main": "main.js"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								spec/fixtures/api/utility-process/inherit-stderr/test.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fixtures/api/utility-process/inherit-stderr/test.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					process.stderr.write(process.argv[2].split('--payload=')[1]);
 | 
				
			||||||
 | 
					process.stderr.end();
 | 
				
			||||||
 | 
					process.exit(0);
 | 
				
			||||||
							
								
								
									
										10
									
								
								spec/fixtures/api/utility-process/inherit-stdout/main.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								spec/fixtures/api/utility-process/inherit-stdout/main.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					const { app, utilityProcess } = require('electron');
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.whenReady().then(() => {
 | 
				
			||||||
 | 
					  const payload = app.commandLine.getSwitchValue('payload');
 | 
				
			||||||
 | 
					  const child = utilityProcess.fork(path.join(__dirname, 'test.js'), [`--payload=${payload}`]);
 | 
				
			||||||
 | 
					  child.on('exit', () => {
 | 
				
			||||||
 | 
					    app.quit();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										4
									
								
								spec/fixtures/api/utility-process/inherit-stdout/package.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								spec/fixtures/api/utility-process/inherit-stdout/package.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "electron-test-utility-process-inherit-stdout",
 | 
				
			||||||
 | 
					  "main": "main.js"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								spec/fixtures/api/utility-process/inherit-stdout/test.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fixtures/api/utility-process/inherit-stdout/test.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					process.stdout.write(process.argv[2].split('--payload=')[1]);
 | 
				
			||||||
 | 
					process.stdout.end();
 | 
				
			||||||
 | 
					process.exit(0);
 | 
				
			||||||
							
								
								
									
										3
									
								
								spec/fixtures/api/utility-process/log.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fixtures/api/utility-process/log.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					console.log('hello');
 | 
				
			||||||
 | 
					process.stderr.write('world');
 | 
				
			||||||
 | 
					process.exit(0);
 | 
				
			||||||
							
								
								
									
										10
									
								
								spec/fixtures/api/utility-process/post-message-queue.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								spec/fixtures/api/utility-process/post-message-queue.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					setTimeout(() => {
 | 
				
			||||||
 | 
					  let called = 0;
 | 
				
			||||||
 | 
					  let result = '';
 | 
				
			||||||
 | 
					  process.parentPort.on('message', (e) => {
 | 
				
			||||||
 | 
					    result += e.data;
 | 
				
			||||||
 | 
					    if (++called === 3) {
 | 
				
			||||||
 | 
					      process.parentPort.postMessage(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}, 3000);
 | 
				
			||||||
							
								
								
									
										3
									
								
								spec/fixtures/api/utility-process/post-message.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								spec/fixtures/api/utility-process/post-message.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					process.parentPort.on('message', (e) => {
 | 
				
			||||||
 | 
					  process.parentPort.postMessage(e.data);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										5
									
								
								spec/fixtures/api/utility-process/preload.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/fixtures/api/utility-process/preload.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					const { ipcRenderer } = require('electron');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ipcRenderer.on('port', (e, msg) => {
 | 
				
			||||||
 | 
					  e.ports[0].postMessage(msg);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										6
									
								
								spec/fixtures/api/utility-process/receive-message.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								spec/fixtures/api/utility-process/receive-message.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					process.parentPort.on('message', (e) => {
 | 
				
			||||||
 | 
					  e.ports[0].on('message', (ev) => {
 | 
				
			||||||
 | 
					    process.parentPort.postMessage(ev.data);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  e.ports[0].start();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										2
									
								
								spec/fixtures/api/utility-process/suid.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								spec/fixtures/api/utility-process/suid.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					const result = require('child_process').execSync('sudo --help');
 | 
				
			||||||
 | 
					process.parentPort.postMessage(result);
 | 
				
			||||||
							
								
								
									
										1
									
								
								typings/internal-ambient.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								typings/internal-ambient.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -251,6 +251,7 @@ declare namespace NodeJS {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Additional properties
 | 
					    // Additional properties
 | 
				
			||||||
    _firstFileName?: string;
 | 
					    _firstFileName?: string;
 | 
				
			||||||
 | 
					    _serviceStartupScript: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    helperExecPath: string;
 | 
					    helperExecPath: string;
 | 
				
			||||||
    mainModule?: NodeJS.Module | undefined;
 | 
					    mainModule?: NodeJS.Module | undefined;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								typings/internal-electron.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								typings/internal-electron.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -9,7 +9,8 @@ declare namespace Electron {
 | 
				
			||||||
  enum ProcessType {
 | 
					  enum ProcessType {
 | 
				
			||||||
    browser = 'browser',
 | 
					    browser = 'browser',
 | 
				
			||||||
    renderer = 'renderer',
 | 
					    renderer = 'renderer',
 | 
				
			||||||
    worker = 'worker'
 | 
					    worker = 'worker',
 | 
				
			||||||
 | 
					    utility = 'utility'
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  interface App {
 | 
					  interface App {
 | 
				
			||||||
| 
						 | 
					@ -254,6 +255,18 @@ declare namespace ElectronInternal {
 | 
				
			||||||
    loader: ModuleLoader;
 | 
					    loader: ModuleLoader;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface UtilityProcessWrapper extends NodeJS.EventEmitter {
 | 
				
			||||||
 | 
					    readonly pid: (number) | (undefined);
 | 
				
			||||||
 | 
					    kill(): boolean;
 | 
				
			||||||
 | 
					    postMessage(message: any, transfer?: any[]): void;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface ParentPort extends NodeJS.EventEmitter {
 | 
				
			||||||
 | 
					    start(): void;
 | 
				
			||||||
 | 
					    pause(): void;
 | 
				
			||||||
 | 
					    postMessage(message: any): void;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  class WebViewElement extends HTMLElement {
 | 
					  class WebViewElement extends HTMLElement {
 | 
				
			||||||
    static observedAttributes: Array<string>;
 | 
					    static observedAttributes: Array<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue