mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
33 Commits
v22.0.0-al
...
v22.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
895f991816 | ||
|
|
d843ae327d | ||
|
|
5d418c5dab | ||
|
|
39f23b057e | ||
|
|
d8ddf31aa7 | ||
|
|
7a5d49bb8d | ||
|
|
44d72e39f7 | ||
|
|
ede27b75b0 | ||
|
|
96e82dc368 | ||
|
|
6908088d38 | ||
|
|
548a91a24b | ||
|
|
04b3fb30ea | ||
|
|
3395a813fb | ||
|
|
d299d267b3 | ||
|
|
71701978b8 | ||
|
|
7a8346cc11 | ||
|
|
ddcc9b20f2 | ||
|
|
4a13d2708c | ||
|
|
53ebcb0110 | ||
|
|
3a179fb2b7 | ||
|
|
820b80b320 | ||
|
|
60889443f7 | ||
|
|
15e0366c35 | ||
|
|
65c4cd7f19 | ||
|
|
286b533ec1 | ||
|
|
d0d7dfab67 | ||
|
|
3d619ac79f | ||
|
|
163518b80f | ||
|
|
b930387036 | ||
|
|
ba08f4c157 | ||
|
|
51f6a644e9 | ||
|
|
2b13ba9231 | ||
|
|
dc73409683 |
@@ -455,7 +455,7 @@ step-delete-git-directories: &step-delete-git-directories
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
cd src
|
||||
( find . -type d -name ".git" -not -path "./third_party/angle/*" -not -path "./third_party/dawn/*" ) | xargs rm -rf
|
||||
( find . -type d -name ".git" -not -path "./third_party/angle/*" -not -path "./third_party/dawn/*" -not -path "./electron/*" ) | xargs rm -rf
|
||||
fi
|
||||
|
||||
# On macOS the yarn install command during gclient sync was run on a linux
|
||||
@@ -814,7 +814,7 @@ step-maybe-zip-symbols: &step-maybe-zip-symbols
|
||||
cd src
|
||||
export BUILD_PATH="$PWD/out/Default"
|
||||
ninja -C out/Default electron:licenses
|
||||
ninja -C out/Default electron:electron_version
|
||||
ninja -C out/Default electron:electron_version_file
|
||||
DELETE_DSYMS_AFTER_ZIP=1 electron/script/zip-symbols.py -b $BUILD_PATH
|
||||
|
||||
step-maybe-cross-arch-snapshot: &step-maybe-cross-arch-snapshot
|
||||
@@ -874,12 +874,12 @@ step-touch-sync-done: &step-touch-sync-done
|
||||
step-maybe-restore-src-cache: &step-maybe-restore-src-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- v14-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
- v16-src-cache-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache
|
||||
step-maybe-restore-src-cache-marker: &step-maybe-restore-src-cache-marker
|
||||
restore_cache:
|
||||
keys:
|
||||
- v14-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
- v16-src-cache-marker-{{ checksum "src/electron/.depshash" }}
|
||||
name: Restoring src cache marker
|
||||
|
||||
# Restore exact or closest git cache based on the hash of DEPS and .circle-sync-done
|
||||
@@ -894,14 +894,6 @@ step-maybe-restore-git-cache: &step-maybe-restore-git-cache
|
||||
- v1-git-cache-{{ checksum "src/electron/.circle-sync-done" }}
|
||||
name: Conditionally restoring git cache
|
||||
|
||||
step-restore-out-cache: &step-restore-out-cache
|
||||
restore_cache:
|
||||
paths:
|
||||
- ./src/out/Default
|
||||
keys:
|
||||
- v10-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
name: Restoring out cache
|
||||
|
||||
step-set-git-cache-path: &step-set-git-cache-path
|
||||
run:
|
||||
name: Set GIT_CACHE_PATH to make gclient to use the cache
|
||||
@@ -919,13 +911,6 @@ step-save-git-cache: &step-save-git-cache
|
||||
key: v1-git-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }}
|
||||
name: Persisting git cache
|
||||
|
||||
step-save-out-cache: &step-save-out-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- ./src/out/Default
|
||||
key: v10-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
name: Persisting out cache
|
||||
|
||||
step-run-electron-only-hooks: &step-run-electron-only-hooks
|
||||
run:
|
||||
name: Run Electron Only Hooks
|
||||
@@ -955,13 +940,16 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c
|
||||
rm -rf third_party/electron_node/deps/openssl
|
||||
rm -rf third_party/electron_node/deps/v8
|
||||
rm -rf chrome/test/data/xr/webvr_info
|
||||
rm -rf src/third_party/angle/third_party/VK-GL-CTS/src
|
||||
rm -rf src/third_party/swift-toolchain
|
||||
rm -rf src/third_party/swiftshader/tests/regres/testlists
|
||||
|
||||
# Save the src cache based on the deps hash
|
||||
step-save-src-cache: &step-save-src-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- /var/portal
|
||||
key: v14-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
key: v16-src-cache-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
name: Persisting src cache
|
||||
step-make-src-cache-marker: &step-make-src-cache-marker
|
||||
run:
|
||||
@@ -971,7 +959,7 @@ step-save-src-cache-marker: &step-save-src-cache-marker
|
||||
save_cache:
|
||||
paths:
|
||||
- .src-cache-marker
|
||||
key: v14-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
key: v16-src-cache-marker-{{ checksum "/var/portal/src/electron/.depshash" }}
|
||||
|
||||
step-maybe-early-exit-no-doc-change: &step-maybe-early-exit-no-doc-change
|
||||
run:
|
||||
@@ -1297,9 +1285,6 @@ commands:
|
||||
build:
|
||||
type: boolean
|
||||
default: true
|
||||
use-out-cache:
|
||||
type: boolean
|
||||
default: true
|
||||
restore-src-cache:
|
||||
type: boolean
|
||||
default: true
|
||||
@@ -1422,10 +1407,6 @@ commands:
|
||||
- *step-delete-git-directories
|
||||
|
||||
# Electron app
|
||||
- when:
|
||||
condition: << parameters.use-out-cache >>
|
||||
steps:
|
||||
- *step-restore-out-cache
|
||||
- *step-gn-gen-default
|
||||
- *step-electron-build
|
||||
- *step-maybe-electron-dist-strip
|
||||
@@ -1468,22 +1449,6 @@ commands:
|
||||
condition: << parameters.build >>
|
||||
steps:
|
||||
- move_and_store_all_artifacts
|
||||
- run:
|
||||
name: Remove the big things on macOS, this seems to be better on average
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
mkdir -p src/out/Default
|
||||
cd src/out/Default
|
||||
find . -type f -size +50M -delete
|
||||
mkdir -p gen/electron
|
||||
cd gen/electron
|
||||
# These files do not seem to like being in a cache, let us remove them
|
||||
find . -type f -name '*_pkg_info' -delete
|
||||
fi
|
||||
- when:
|
||||
condition: << parameters.use-out-cache >>
|
||||
steps:
|
||||
- *step-save-out-cache
|
||||
|
||||
- *step-maybe-notify-slack-failure
|
||||
|
||||
@@ -1637,7 +1602,6 @@ jobs:
|
||||
persist: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
use-out-cache: false
|
||||
|
||||
linux-x64-testing-asan:
|
||||
executor:
|
||||
@@ -1654,7 +1618,6 @@ jobs:
|
||||
- electron-build:
|
||||
persist: true
|
||||
checkout: true
|
||||
use-out-cache: false
|
||||
build-nonproprietary-ffmpeg: false
|
||||
|
||||
linux-x64-testing-no-run-as-node:
|
||||
@@ -1671,7 +1634,6 @@ jobs:
|
||||
- electron-build:
|
||||
persist: false
|
||||
checkout: true
|
||||
use-out-cache: false
|
||||
|
||||
linux-x64-testing-gn-check:
|
||||
executor:
|
||||
@@ -1722,7 +1684,6 @@ jobs:
|
||||
persist: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
use-out-cache: false
|
||||
|
||||
linux-arm-publish:
|
||||
executor:
|
||||
@@ -1765,7 +1726,6 @@ jobs:
|
||||
persist: true
|
||||
checkout: false
|
||||
checkout-and-assume-cache: true
|
||||
use-out-cache: false
|
||||
|
||||
linux-arm64-testing-gn-check:
|
||||
executor:
|
||||
|
||||
74
BUILD.gn
74
BUILD.gn
@@ -107,6 +107,14 @@ branding = read_file("shell/app/BRANDING.json", "json")
|
||||
electron_project_name = branding.project_name
|
||||
electron_product_name = branding.product_name
|
||||
electron_mac_bundle_id = branding.mac_bundle_id
|
||||
electron_version = exec_script("script/print-version.py",
|
||||
[],
|
||||
"trim string",
|
||||
[
|
||||
".git/packed-refs",
|
||||
".git/HEAD",
|
||||
"script/lib/get-version.js",
|
||||
])
|
||||
|
||||
if (is_mas_build) {
|
||||
assert(is_mac,
|
||||
@@ -202,6 +210,15 @@ webpack_build("electron_isolated_renderer_bundle") {
|
||||
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") {
|
||||
deps = [
|
||||
":electron_asar_bundle",
|
||||
@@ -209,6 +226,7 @@ action("electron_js2c") {
|
||||
":electron_isolated_renderer_bundle",
|
||||
":electron_renderer_bundle",
|
||||
":electron_sandboxed_renderer_bundle",
|
||||
":electron_utility_bundle",
|
||||
":electron_worker_bundle",
|
||||
]
|
||||
|
||||
@@ -218,6 +236,7 @@ action("electron_js2c") {
|
||||
"$target_gen_dir/js2c/isolated_bundle.js",
|
||||
"$target_gen_dir/js2c/renderer_init.js",
|
||||
"$target_gen_dir/js2c/sandbox_bundle.js",
|
||||
"$target_gen_dir/js2c/utility_init.js",
|
||||
"$target_gen_dir/js2c/worker_init.js",
|
||||
]
|
||||
|
||||
@@ -302,12 +321,9 @@ npm_action("electron_version_args") {
|
||||
|
||||
outputs = [ "$target_gen_dir/electron_version.args" ]
|
||||
|
||||
args = rebase_path(outputs)
|
||||
args = rebase_path(outputs) + [ "$electron_version" ]
|
||||
|
||||
inputs = [
|
||||
"ELECTRON_VERSION",
|
||||
"script/generate-version-json.js",
|
||||
]
|
||||
inputs = [ "script/generate-version-json.js" ]
|
||||
}
|
||||
|
||||
templated_file("electron_version_header") {
|
||||
@@ -319,6 +335,39 @@ templated_file("electron_version_header") {
|
||||
args_files = get_target_outputs(":electron_version_args")
|
||||
}
|
||||
|
||||
templated_file("electron_win_rc") {
|
||||
deps = [ ":electron_version_args" ]
|
||||
|
||||
template = "build/templates/electron_rc.tmpl"
|
||||
output = "$target_gen_dir/win-resources/electron.rc"
|
||||
|
||||
args_files = get_target_outputs(":electron_version_args")
|
||||
}
|
||||
|
||||
copy("electron_win_resource_files") {
|
||||
sources = [
|
||||
"shell/browser/resources/win/electron.ico",
|
||||
"shell/browser/resources/win/resource.h",
|
||||
]
|
||||
outputs = [ "$target_gen_dir/win-resources/{{source_file_part}}" ]
|
||||
}
|
||||
|
||||
templated_file("electron_version_file") {
|
||||
deps = [ ":electron_version_args" ]
|
||||
|
||||
template = "build/templates/version_string.tmpl"
|
||||
output = "$root_build_dir/version"
|
||||
|
||||
args_files = get_target_outputs(":electron_version_args")
|
||||
}
|
||||
|
||||
group("electron_win32_resources") {
|
||||
public_deps = [
|
||||
":electron_win_rc",
|
||||
":electron_win_resource_files",
|
||||
]
|
||||
}
|
||||
|
||||
action("electron_fuses") {
|
||||
script = "build/fuses/build.py"
|
||||
|
||||
@@ -368,6 +417,7 @@ source_set("electron_lib") {
|
||||
"chromium_src:chrome",
|
||||
"chromium_src:chrome_spellchecker",
|
||||
"shell/common/api:mojo",
|
||||
"shell/services/node/public/mojom",
|
||||
"//base:base_static",
|
||||
"//base/allocator:buildflags",
|
||||
"//chrome:strings",
|
||||
@@ -615,6 +665,8 @@ source_set("electron_lib") {
|
||||
sources += [
|
||||
"shell/common/plugin_info.cc",
|
||||
"shell/common/plugin_info.h",
|
||||
"shell/renderer/electron_renderer_pepper_host_factory.cc",
|
||||
"shell/renderer/electron_renderer_pepper_host_factory.h",
|
||||
"shell/renderer/pepper_helper.cc",
|
||||
"shell/renderer/pepper_helper.h",
|
||||
]
|
||||
@@ -753,7 +805,6 @@ if (is_mac) {
|
||||
electron_helper_name = "$electron_product_name Helper"
|
||||
electron_login_helper_name = "$electron_product_name Login Helper"
|
||||
electron_framework_version = "A"
|
||||
electron_version = read_file("ELECTRON_VERSION", "trim string")
|
||||
|
||||
mac_xib_bundle_data("electron_xibs") {
|
||||
sources = [ "shell/common/resources/mac/MainMenu.xib" ]
|
||||
@@ -1194,6 +1245,7 @@ if (is_mac) {
|
||||
":default_app_asar",
|
||||
":electron_app_manifest",
|
||||
":electron_lib",
|
||||
":electron_win32_resources",
|
||||
":packed_resources",
|
||||
"//components/crash/core/app",
|
||||
"//content:sandbox_helper_win",
|
||||
@@ -1227,8 +1279,7 @@ if (is_mac) {
|
||||
|
||||
if (is_win) {
|
||||
sources += [
|
||||
# TODO: we should be generating our .rc files more like how chrome does
|
||||
"shell/browser/resources/win/electron.rc",
|
||||
"$target_gen_dir/win-resources/electron.rc",
|
||||
"shell/browser/resources/win/resource.h",
|
||||
]
|
||||
|
||||
@@ -1410,15 +1461,10 @@ group("licenses") {
|
||||
]
|
||||
}
|
||||
|
||||
copy("electron_version") {
|
||||
sources = [ "ELECTRON_VERSION" ]
|
||||
outputs = [ "$root_build_dir/version" ]
|
||||
}
|
||||
|
||||
dist_zip("electron_dist_zip") {
|
||||
data_deps = [
|
||||
":electron_app",
|
||||
":electron_version",
|
||||
":electron_version_file",
|
||||
":licenses",
|
||||
]
|
||||
if (is_linux) {
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'108.0.5359.10',
|
||||
'108.0.5359.40',
|
||||
'node_version':
|
||||
'v16.17.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
22.0.0-alpha.8
|
||||
@@ -12,7 +12,7 @@ using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) an
|
||||
[Chromium](https://www.chromium.org) and is used by the [Atom
|
||||
editor](https://github.com/atom/atom) and many other [apps](https://electronjs.org/apps).
|
||||
|
||||
Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important
|
||||
Follow [@electronjs](https://twitter.com/electronjs) on Twitter for important
|
||||
announcements.
|
||||
|
||||
This project adheres to the Contributor Covenant
|
||||
|
||||
14
appveyor.yml
14
appveyor.yml
@@ -199,14 +199,14 @@ for:
|
||||
}
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -eq 'release') {
|
||||
python electron\script\zip-symbols.py
|
||||
python3 electron\script\zip-symbols.py
|
||||
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
|
||||
} else {
|
||||
# It's useful to have pdb files when debugging testing builds that are
|
||||
# built on CI.
|
||||
7z a pdb.zip out\Default\*.pdb
|
||||
}
|
||||
- python electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
|
||||
- python3 electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
|
||||
|
||||
deploy_script:
|
||||
- cd electron
|
||||
@@ -214,10 +214,10 @@ for:
|
||||
if (Test-Path Env:\ELECTRON_RELEASE) {
|
||||
if (Test-Path Env:\UPLOAD_TO_STORAGE) {
|
||||
Write-Output "Uploading Electron release distribution to azure"
|
||||
& python script\release\uploaders\upload.py --verbose --upload_to_storage
|
||||
& python3 script\release\uploaders\upload.py --verbose --upload_to_storage
|
||||
} else {
|
||||
Write-Output "Uploading Electron release distribution to github releases"
|
||||
& python script\release\uploaders\upload.py --verbose
|
||||
& python3 script\release\uploaders\upload.py --verbose
|
||||
}
|
||||
} elseif (Test-Path Env:\TEST_WOA) {
|
||||
node script/release/ci-release-build.js --job=electron-woa-testing --ci=GHA --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
|
||||
@@ -303,11 +303,11 @@ for:
|
||||
- echo Running main test suite & node script/yarn test -- --trace-uncaught --runners=main --enable-logging=file --log-file=%cd%\electron.log
|
||||
- echo Running native test suite & node script/yarn test -- --trace-uncaught --runners=native --enable-logging=file --log-file=%cd%\electron.log
|
||||
- cd ..
|
||||
- echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
- echo Verifying non proprietary ffmpeg & python3 electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
- echo "About to verify mksnapshot"
|
||||
- echo Verifying mksnapshot & python electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd%
|
||||
- echo Verifying mksnapshot & python3 electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd%
|
||||
- echo "Done verifying mksnapshot"
|
||||
- echo Verifying chromedriver & python electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd%
|
||||
- echo Verifying chromedriver & python3 electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd%
|
||||
- echo "Done verifying chromedriver"
|
||||
|
||||
on_finish:
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 22,0,0,8
|
||||
PRODUCTVERSION 22,0,0,8
|
||||
FILEVERSION $major,$minor,$patch,$prerelease_number
|
||||
PRODUCTVERSION $major,$minor,$patch,$prerelease_number
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "22.0.0"
|
||||
VALUE "FileVersion", "$major.$minor.$patch"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "22.0.0"
|
||||
VALUE "ProductVersion", "$major.$minor.$patch"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
1
build/templates/version_string.tmpl
Normal file
1
build/templates/version_string.tmpl
Normal file
@@ -0,0 +1 @@
|
||||
$full_version
|
||||
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
|
||||
});
|
||||
@@ -232,6 +232,8 @@ static_library("chrome") {
|
||||
"//chrome/browser/printing/printing_service.h",
|
||||
"//components/printing/browser/print_to_pdf/pdf_print_job.cc",
|
||||
"//components/printing/browser/print_to_pdf/pdf_print_job.h",
|
||||
"//components/printing/browser/print_to_pdf/pdf_print_result.cc",
|
||||
"//components/printing/browser/print_to_pdf/pdf_print_result.h",
|
||||
"//components/printing/browser/print_to_pdf/pdf_print_utils.cc",
|
||||
"//components/printing/browser/print_to_pdf/pdf_print_utils.h",
|
||||
]
|
||||
|
||||
@@ -717,6 +717,8 @@ To set the locale, you'll want to use a command line switch at app startup, whic
|
||||
|
||||
**Note:** This API must be called after the `ready` event is emitted.
|
||||
|
||||
**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages).
|
||||
|
||||
### `app.getLocaleCountryCode()`
|
||||
|
||||
Returns `string` - User operating system's locale two-letter [ISO 3166](https://www.iso.org/iso-3166-country-codes.html) country code. The value is taken from native OS APIs.
|
||||
@@ -725,10 +727,42 @@ Returns `string` - User operating system's locale two-letter [ISO 3166](https://
|
||||
|
||||
### `app.getSystemLocale()`
|
||||
|
||||
Returns `string` - The current system locale. On Windows and Linux, it is fetched using Chromium's `i18n` library. On macOS, the `NSLocale` object is used instead.
|
||||
Returns `string` - The current system locale. On Windows and Linux, it is fetched using Chromium's `i18n` library. On macOS, `[NSLocale currentLocale]` is used instead. To get the user's current system language, which is not always the same as the locale, it is better to use [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages).
|
||||
|
||||
Different operating systems also use the regional data differently:
|
||||
|
||||
* Windows 11 uses the regional format for numbers, dates, and times.
|
||||
* macOS Monterey uses the region for formatting numbers, dates, times, and for selecting the currency symbol to use.
|
||||
|
||||
Therefore, this API can be used for purposes such as choosing a format for rendering dates and times in a calendar app, especially when the developer wants the format to be consistent with the OS.
|
||||
|
||||
**Note:** This API must be called after the `ready` event is emitted.
|
||||
|
||||
**Note:** To see example return values of this API compared to other locale and language APIs, see [`app.getPreferredSystemLanguages()`](#appgetpreferredsystemlanguages).
|
||||
|
||||
### `app.getPreferredSystemLanguages()`
|
||||
|
||||
Returns `string[]` - The user's preferred system languages from most preferred to least preferred, including the country codes if applicable. A user can modify and add to this list on Windows or macOS through the Language and Region settings.
|
||||
|
||||
The API uses `GlobalizationPreferences` (with a fallback to `GetSystemPreferredUILanguages`) on Windows, `\[NSLocale preferredLanguages\]` on macOS, and `g_get_language_names` on Linux.
|
||||
|
||||
This API can be used for purposes such as deciding what language to present the application in.
|
||||
|
||||
Here are some examples of return values of the various language and locale APIs with different configurations:
|
||||
|
||||
* For Windows, where the application locale is German, the regional format is Finnish (Finland), and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese (China), Finnish, and Spanish (Latin America):
|
||||
* `app.getLocale()` returns `'de'`
|
||||
* `app.getSystemLocale()` returns `'fi-FI'`
|
||||
* `app.getPreferredSystemLanguages()` returns `['fr-CA', 'en-US', 'zh-Hans-CN', 'fi', 'es-419']`
|
||||
* On macOS, where the application locale is German, the region is Finland, and the preferred system languages from most to least preferred are French (Canada), English (US), Simplified Chinese, and Spanish (Latin America):
|
||||
* `app.getLocale()` returns `'de'`
|
||||
* `app.getSystemLocale()` returns `'fr-FI'`
|
||||
* `app.getPreferredSystemLanguages()` returns `['fr-CA', 'en-US', 'zh-Hans-FI', 'es-419']`
|
||||
|
||||
Both the available languages and regions and the possible return values differ between the two operating systems.
|
||||
|
||||
As can be seen with the example above, on Windows, it is possible that a preferred system language has no country code, and that one of the preferred system languages corresponds with the language used for the regional format. On macOS, the region serves more as a default country code: the user doesn't need to have Finnish as a preferred language to use Finland as the region,and the country code `FI` is used as the country code for preferred system languages that do not have associated countries in the language name.
|
||||
|
||||
### `app.addRecentDocument(path)` _macOS_ _Windows_
|
||||
|
||||
* `path` string
|
||||
@@ -1203,7 +1237,7 @@ For `infoType` equal to `basic`:
|
||||
}
|
||||
```
|
||||
|
||||
Using `basic` should be preferred if only basic information like `vendorId` or `driverId` is needed.
|
||||
Using `basic` should be preferred if only basic information like `vendorId` or `deviceId` is needed.
|
||||
|
||||
### `app.setBadgeCount([count])` _Linux_ _macOS_
|
||||
|
||||
|
||||
@@ -96,14 +96,6 @@ Algorithm][SCA], just like [`window.postMessage`][], so prototype chains will no
|
||||
included. Sending Functions, Promises, Symbols, WeakMaps, or WeakSets will
|
||||
throw an exception.
|
||||
|
||||
> **NOTE:** Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects will throw an exception.
|
||||
>
|
||||
> Since the main process does not have support for DOM objects such as
|
||||
> `ImageBitmap`, `File`, `DOMMatrix` and so on, such objects cannot be sent over
|
||||
> Electron's IPC to the main process, as the main process would have no way to decode
|
||||
> them. Attempting to send such objects over IPC will result in an error.
|
||||
|
||||
The main process should listen for `channel` with
|
||||
[`ipcMain.handle()`](./ipc-main.md#ipcmainhandlechannel-listener).
|
||||
|
||||
@@ -126,6 +118,21 @@ If you need to transfer a [`MessagePort`][] to the main process, use [`ipcRender
|
||||
|
||||
If you do not need a response to the message, consider using [`ipcRenderer.send`](#ipcrenderersendchannel-args).
|
||||
|
||||
> **Note**
|
||||
> Sending non-standard JavaScript types such as DOM objects or
|
||||
> special Electron objects will throw an exception.
|
||||
>
|
||||
> Since the main process does not have support for DOM objects such as
|
||||
> `ImageBitmap`, `File`, `DOMMatrix` and so on, such objects cannot be sent over
|
||||
> Electron's IPC to the main process, as the main process would have no way to decode
|
||||
> them. Attempting to send such objects over IPC will result in an error.
|
||||
|
||||
> **Note**
|
||||
> If the handler in the main process throws an error,
|
||||
> the promise returned by `invoke` will reject.
|
||||
> However, the `Error` object in the renderer process
|
||||
> will not be the same as the one thrown in the main process.
|
||||
|
||||
### `ipcRenderer.sendSync(channel, ...args)`
|
||||
|
||||
* `channel` string
|
||||
|
||||
@@ -14,7 +14,7 @@ See [`Menu`](menu.md) for examples.
|
||||
* `menuItem` MenuItem
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) | undefined - This will not be defined if no window is open.
|
||||
* `event` [KeyboardEvent](structures/keyboard-event.md)
|
||||
* `role` string (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `toggleSpellChecker`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, 'showSubstitutions', 'toggleSmartQuotes', 'toggleSmartDashes', 'toggleTextReplacement', `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `shareMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
* `role` string (optional) - Can be `undo`, `redo`, `cut`, `copy`, `paste`, `pasteAndMatchStyle`, `delete`, `selectAll`, `reload`, `forceReload`, `toggleDevTools`, `resetZoom`, `zoomIn`, `zoomOut`, `toggleSpellChecker`, `togglefullscreen`, `window`, `minimize`, `close`, `help`, `about`, `services`, `hide`, `hideOthers`, `unhide`, `quit`, `showSubstitutions`, `toggleSmartQuotes`, `toggleSmartDashes`, `toggleTextReplacement`, `startSpeaking`, `stopSpeaking`, `zoom`, `front`, `appMenu`, `fileMenu`, `editMenu`, `viewMenu`, `shareMenu`, `recentDocuments`, `toggleTabBar`, `selectNextTab`, `selectPreviousTab`, `mergeAllWindows`, `clearRecentDocuments`, `moveTabToNewWindow` or `windowMenu` - Define the action of the menu item, when specified the
|
||||
`click` property will be ignored. See [roles](#roles).
|
||||
* `type` string (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or
|
||||
`radio`.
|
||||
|
||||
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
|
||||
* `renderer` - A renderer process
|
||||
* `worker` - In a web worker
|
||||
* `utility` - In a node process launched as a service
|
||||
|
||||
### `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.
|
||||
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
|
||||
|
||||
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
|
||||
@@ -28,7 +28,7 @@ const { session } = require('electron')
|
||||
|
||||
// Modify the user agent for all requests to the following urls.
|
||||
const filter = {
|
||||
urls: ['https://*.github.com/*', '*://electron.github.io']
|
||||
urls: ['https://*.github.com/*', '*://electron.github.io/*']
|
||||
}
|
||||
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
||||
|
||||
@@ -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
|
||||
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 is Google's open source JavaScript engine. It is written in C++ and is
|
||||
@@ -231,4 +240,5 @@ embedded content.
|
||||
[renderer]: #renderer-process
|
||||
[userland]: #userland
|
||||
[using native node modules]: tutorial/using-native-node-modules.md
|
||||
[UtilityProcess]: api/utility-process.md
|
||||
[v8]: #v8
|
||||
|
||||
@@ -52,15 +52,17 @@ ways to get your application signed and notarized.
|
||||
If you're using Electron's favorite build tool, getting your application signed
|
||||
and notarized requires a few additions to your configuration. [Forge](https://electronforge.io) is a
|
||||
collection of the official Electron tools, using [`electron-packager`],
|
||||
[`electron-osx-sign`], and [`electron-notarize`] under the hood.
|
||||
[`@electron/osx-sign`], and [`@electron/notarize`] under the hood.
|
||||
|
||||
Detailed instructions on how to configure your application can be found in the [Electron Forge Code Signing Tutorial](https://www.electronforge.io/guides/code-signing/code-signing-macos).
|
||||
Detailed instructions on how to configure your application can be found in the
|
||||
[Signing macOS Apps](https://www.electronforge.io/guides/code-signing/code-signing-macos) guide in
|
||||
the Electron Forge docs.
|
||||
|
||||
### Using Electron Packager
|
||||
|
||||
If you're not using an integrated build pipeline like Forge, you
|
||||
are likely using [`electron-packager`], which includes [`electron-osx-sign`] and
|
||||
[`electron-notarize`].
|
||||
are likely using [`electron-packager`], which includes [`@electron/osx-sign`] and
|
||||
[`@electron/notarize`].
|
||||
|
||||
If you're using Packager's API, you can pass [in configuration that both signs
|
||||
and notarizes your application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
|
||||
@@ -70,13 +72,7 @@ const packager = require('electron-packager')
|
||||
|
||||
packager({
|
||||
dir: '/path/to/my/app',
|
||||
osxSign: {
|
||||
identity: 'Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)',
|
||||
'hardened-runtime': true,
|
||||
entitlements: 'entitlements.plist',
|
||||
'entitlements-inherit': 'entitlements.plist',
|
||||
'signature-flags': 'library'
|
||||
},
|
||||
osxSign: {},
|
||||
osxNotarize: {
|
||||
appleId: 'felix@felix.fun',
|
||||
appleIdPassword: 'my-apple-id-password'
|
||||
@@ -84,26 +80,6 @@ packager({
|
||||
})
|
||||
```
|
||||
|
||||
The `entitlements.plist` file referenced here needs the following macOS-specific entitlements
|
||||
to assure the Apple security mechanisms that your app is doing these things
|
||||
without meaning any harm:
|
||||
|
||||
```xml title="entitlements.plist"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.debugger</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
Up until Electron 12, the `com.apple.security.cs.allow-unsigned-executable-memory` entitlement was required
|
||||
as well. However, it should not be used anymore if it can be avoided.
|
||||
|
||||
### Signing Mac App Store applications
|
||||
|
||||
See the [Mac App Store Guide].
|
||||
@@ -213,15 +189,14 @@ can find [its documentation here](https://www.electron.build/code-signing).
|
||||
See the [Windows Store Guide].
|
||||
|
||||
[apple developer program]: https://developer.apple.com/programs/
|
||||
[`electron-builder`]: https://github.com/electron-userland/electron-builder
|
||||
[`electron-forge`]: https://github.com/electron-userland/electron-forge
|
||||
[`electron-osx-sign`]: https://github.com/electron-userland/electron-osx-sign
|
||||
[`electron-forge`]: https://github.com/electron/forge
|
||||
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
|
||||
[`electron-packager`]: https://github.com/electron/electron-packager
|
||||
[`electron-notarize`]: https://github.com/electron/electron-notarize
|
||||
[`@electron/notarize`]: https://github.com/electron/notarize
|
||||
[`electron-winstaller`]: https://github.com/electron/windows-installer
|
||||
[`electron-wix-msi`]: https://github.com/felixrieseberg/electron-wix-msi
|
||||
[`electron-wix-msi`]: https://github.com/electron-userland/electron-wix-msi
|
||||
[xcode]: https://developer.apple.com/xcode
|
||||
[signing certificates]: https://github.com/electron/electron-osx-sign/wiki/1.-Getting-Started#certificates
|
||||
[signing certificates]: https://developer.apple.com/support/certificates/
|
||||
[mac app store guide]: ./mac-app-store-submission-guide.md
|
||||
[windows store guide]: ./windows-store-guide.md
|
||||
[maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows
|
||||
|
||||
@@ -7,29 +7,29 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
## Timeline
|
||||
|
||||
| Electron | Alpha | Beta | Stable | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | M108 | TBD | ✅ |
|
||||
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | M106 | v16.17 | ✅ |
|
||||
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | M104 | v16.15 | ✅ |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | M102 | v16.14 | ✅ |
|
||||
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | M100 | v16.13 | 🚫 |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | M98 | v16.13 | 🚫 |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | M96 | v16.9 | 🚫 |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | v16.5 | 🚫 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | v14.17 | 🚫 |
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | M91 | v14.16 | 🚫 |
|
||||
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | M89 | v14.16 | 🚫 |
|
||||
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | M87 | v12.18 | 🚫 |
|
||||
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | M85 | v12.16 | 🚫 |
|
||||
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | M83 | v12.14 | 🚫 |
|
||||
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | M80 | v12.13 | 🚫 |
|
||||
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | M78 | v12.8 | 🚫 |
|
||||
| 6.0.0 | -- | 2019-May-01 | 2019-Jul-30 | M76 | v12.4 | 🚫 |
|
||||
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-24 | M73 | v12.0 | 🚫 |
|
||||
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | M69 | v10.11 | 🚫 |
|
||||
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | M66 | v10.2 | 🚫 |
|
||||
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | M61 | v8.9 | 🚫 |
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | TBD | M108 | TBD | ✅ |
|
||||
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | TBD | M106 | v16.17 | ✅ |
|
||||
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | TBD | M104 | v16.15 | ✅ |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | TBD | M102 | v16.14 | ✅ |
|
||||
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | 2022-Sep-27 | M100 | v16.13 | 🚫 |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | 2022-Aug-02 | M98 | v16.13 | 🚫 |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | 2022-May-24 | M96 | v16.9 | 🚫 |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | 2022-May-24 | M94 | v16.5 | 🚫 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | 2022-Mar-29 | M93 | v14.17 | 🚫 |
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | 2022-Feb-01 | M91 | v14.16 | 🚫 |
|
||||
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | 2021-Nov-16 | M89 | v14.16 | 🚫 |
|
||||
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | 2021-Aug-31 | M87 | v12.18 | 🚫 |
|
||||
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | 2021-May-25 | M85 | v12.16 | 🚫 |
|
||||
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | 2021-Mar-02 | M83 | v12.14 | 🚫 |
|
||||
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | 2020-Nov-17 | M80 | v12.13 | 🚫 |
|
||||
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | 2020-Aug-25 | M78 | v12.8 | 🚫 |
|
||||
| 6.0.0 | -- | 2019-Apr-25 | 2019-Jul-30 | 2020-May-19 | M76 | v12.14.0 | 🚫 |
|
||||
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-23 | 2020-Feb-04 | M73 | v12.0 | 🚫 |
|
||||
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | 2019-Oct-22 | M69 | v10.11 | 🚫 |
|
||||
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | 2019-Jul-30 | M66 | v10.2 | 🚫 |
|
||||
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | 2019-Apr-23 | M61 | v8.9 | 🚫 |
|
||||
|
||||
**Notes:**
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ This guide provides information on:
|
||||
To sign Electron apps, the following tools must be installed first:
|
||||
|
||||
* Xcode 11 or above.
|
||||
* The [electron-osx-sign][electron-osx-sign] npm module.
|
||||
* The [@electron/osx-sign] npm module.
|
||||
|
||||
You also have to register an Apple Developer account and join the
|
||||
[Apple Developer Program][developer-program].
|
||||
@@ -103,7 +103,7 @@ Apps submitted to the Mac App Store must run under Apple's
|
||||
the App Sandbox. The standard darwin build of Electron will fail to launch
|
||||
when run under App Sandbox.
|
||||
|
||||
When signing the app with `electron-osx-sign`, it will automatically add the
|
||||
When signing the app with `@electron/osx-sign`, it will automatically add the
|
||||
necessary entitlements to your app's entitlements, but if you are using custom
|
||||
entitlements, you must ensure App Sandbox capacity is added:
|
||||
|
||||
@@ -120,7 +120,7 @@ entitlements, you must ensure App Sandbox capacity is added:
|
||||
|
||||
#### Extra steps without `electron-osx-sign`
|
||||
|
||||
If you are signing your app without using `electron-osx-sign`, you must ensure
|
||||
If you are signing your app without using `@electron/osx-sign`, you must ensure
|
||||
the app bundle's entitlements have at least following keys:
|
||||
|
||||
```xml
|
||||
@@ -170,22 +170,22 @@ your Apple Developer account's Team ID as its value:
|
||||
</plist>
|
||||
```
|
||||
|
||||
When using `electron-osx-sign` the `ElectronTeamID` key will be added
|
||||
When using `@electron/osx-sign` the `ElectronTeamID` key will be added
|
||||
automatically by extracting the Team ID from the certificate's name. You may
|
||||
need to manually add this key if `electron-osx-sign` could not find the correct
|
||||
need to manually add this key if `@electron/osx-sign` could not find the correct
|
||||
Team ID.
|
||||
|
||||
### Sign apps for development
|
||||
|
||||
To sign an app that can run on your development machine, you must sign it with
|
||||
the "Apple Development" certificate and pass the provisioning profile to
|
||||
`electron-osx-sign`.
|
||||
`@electron/osx-sign`.
|
||||
|
||||
```bash
|
||||
electron-osx-sign YourApp.app --identity='Apple Development' --provisioning-profile=/path/to/yourapp.provisionprofile
|
||||
```
|
||||
|
||||
If you are signing without `electron-osx-sign`, you must place the provisioning
|
||||
If you are signing without `@electron/osx-sign`, you must place the provisioning
|
||||
profile to `YourApp.app/Contents/embedded.provisionprofile`.
|
||||
|
||||
The signed app can only run on the machines that registered by the provisioning
|
||||
@@ -213,7 +213,7 @@ use App Sandbox.
|
||||
electron-osx-sign YourApp.app --identity='Developer ID Application' --no-gatekeeper-assess
|
||||
```
|
||||
|
||||
By passing `--no-gatekeeper-assess`, the `electron-osx-sign` will skip the macOS
|
||||
By passing `--no-gatekeeper-assess`, `@electron/osx-sign` will skip the macOS
|
||||
GateKeeper check as your app usually has not been notarized yet by this step.
|
||||
|
||||
<!-- TODO(zcbenz): Add a chapter about App Notarization -->
|
||||
@@ -232,7 +232,7 @@ how to meet the Mac App Store requirements.
|
||||
|
||||
### Upload
|
||||
|
||||
The Application Loader should be used to upload the signed app to iTunes
|
||||
[Apple Transporter][apple-transporter] should be used to upload the signed app to App Store
|
||||
Connect for processing, making sure you have [created a record][create-record]
|
||||
before uploading.
|
||||
|
||||
@@ -341,11 +341,12 @@ Electron uses following cryptographic algorithms:
|
||||
* RIPEMD - [ISO/IEC 10118-3](https://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2FIEC%2010118-3:2004)
|
||||
|
||||
[developer-program]: https://developer.apple.com/support/compare-memberships/
|
||||
[electron-osx-sign]: https://github.com/electron/electron-osx-sign
|
||||
[@electron/osx-sign]: https://github.com/electron/electron-osx-sign
|
||||
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
|
||||
[app-notarization]: https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
|
||||
[submitting-your-app]: https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/SubmittingYourApp/SubmittingYourApp.html
|
||||
[create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html
|
||||
[create-record]: https://help.apple.com/app-store-connect/#/dev2cd126805
|
||||
[apple-transporter]: https://help.apple.com/itc/transporteruserguide/en.lproj/static.html
|
||||
[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
|
||||
[export-compliance]: https://help.apple.com/app-store-connect/#/devc3f64248f
|
||||
[user-selected]: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW6
|
||||
|
||||
@@ -214,8 +214,25 @@ This feature is incredibly useful for two main purposes:
|
||||
URL, you can add custom properties onto the renderer's `window` global that can
|
||||
be used for desktop-only logic on the web client's side.
|
||||
|
||||
## The utility process
|
||||
|
||||
Each Electron app can spawn multiple child processes from the main process using
|
||||
the [UtilityProcess][] API. The utility process runs in a Node.js environment,
|
||||
meaning it has the ability to `require` modules and use all of Node.js APIs.
|
||||
The utility process can be used to host for example: untrusted services,
|
||||
CPU intensive tasks or crash prone components which would have previously
|
||||
been hosted in the main process or process spawned with Node.js [`child_process.fork`][] API.
|
||||
The primary difference between the utility process and process spawned by Node.js
|
||||
child_process module is that the utility process can establish a communication
|
||||
channel with a renderer process using [`MessagePort`][]s. An Electron app can
|
||||
always prefer the [UtilityProcess][] API over Node.js [`child_process.fork`][] API when
|
||||
there is need to fork a child process from the main process.
|
||||
|
||||
[window-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window
|
||||
[`MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
|
||||
[`child_process.fork`]: https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html#child_processforkmodulepath-args-options
|
||||
[context-isolation]: ./context-isolation.md
|
||||
[context-bridge]: ../api/context-bridge.md
|
||||
[ipcrenderer]: ../api/ipc-renderer.md
|
||||
[UtilityProcess]: ../api/utility-process.md
|
||||
[tutorial]: ./tutorial-1-prerequisites.md
|
||||
|
||||
@@ -26,6 +26,15 @@ work on Windows, macOS, and Linux with a single JavaScript codebase.
|
||||
This tutorial will guide you through the process of developing a desktop
|
||||
application with Electron and distributing it to end users.
|
||||
|
||||
## Goals
|
||||
|
||||
This tutorial starts by guiding you through the process of piecing together
|
||||
a minimal Electron application from scratch, then teaches you how to
|
||||
package and distribute it to users using Electron Forge.
|
||||
|
||||
If you prefer to get a project started with a single-command boilerplate, we recommend you start
|
||||
with Electron Forge's [`create-electron-app`](https://www.electronforge.io/) command.
|
||||
|
||||
## Assumptions
|
||||
|
||||
Electron is a native wrapper layer for web apps and is run in a Node.js environment.
|
||||
|
||||
@@ -70,10 +70,9 @@ the [Electron Forge CLI documentation].
|
||||
:::
|
||||
|
||||
You should also notice that your package.json now has a few more packages installed
|
||||
under your `devDependencies`, and contains an added `config.forge` field with an array
|
||||
of makers configured. **Makers** are Forge plugins that create distributables from
|
||||
your source code. You should see multiple makers in the pre-populated configuration,
|
||||
one for each target platform.
|
||||
under `devDependencies`, and a new `forge.config.js` file that exports a configuration
|
||||
object. You should see multiple makers (packages that generate distributable app bundles) in the
|
||||
pre-populated configuration, one for each target platform.
|
||||
|
||||
### Creating a distributable
|
||||
|
||||
@@ -111,13 +110,14 @@ Electron Forge can be configured to create distributables in different OS-specif
|
||||
|
||||
:::
|
||||
|
||||
:::tip Creating and Adding Application Icons
|
||||
:::tip Creating and adding application icons
|
||||
|
||||
Setting custom application icons requires a few additions to your config. Check out [Forge's icon tutorial] for more information.
|
||||
Setting custom application icons requires a few additions to your config.
|
||||
Check out [Forge's icon tutorial] for more information.
|
||||
|
||||
:::
|
||||
|
||||
:::note Packaging without Electron Forge
|
||||
:::info Packaging without Electron Forge
|
||||
|
||||
If you want to manually package your code, or if you're just interested understanding the
|
||||
mechanics behind packaging an Electron app, check out the full [Application Packaging]
|
||||
@@ -136,64 +136,51 @@ Code signing is a security technology that you use to certify that a desktop app
|
||||
created by a known source. Windows and macOS have their own OS-specific code signing
|
||||
systems that will make it difficult for users to download or launch unsigned applications.
|
||||
|
||||
If you already have code signing certificates for Windows and macOS, you can set your
|
||||
credentials in your Forge configuration. Otherwise, please refer to the full
|
||||
[Code Signing] documentation to learn how to purchase a certificate and for more information
|
||||
on the desktop app code signing process.
|
||||
|
||||
On macOS, code signing is done at the app packaging level. On Windows, distributable installers
|
||||
are signed instead.
|
||||
are signed instead. If you already have code signing certificates for Windows and macOS, you can set
|
||||
your credentials in your Forge configuration.
|
||||
|
||||
:::info
|
||||
|
||||
For more information on code signing, check out the
|
||||
[Signing macOS Apps](https://www.electronforge.io/guides/code-signing) guide in the Forge docs.
|
||||
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="macos" label="macOS" default>
|
||||
|
||||
```json title='package.json' {6-18}
|
||||
{
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
//...
|
||||
"packagerConfig": {
|
||||
"osxSign": {
|
||||
"identity": "Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)",
|
||||
"hardened-runtime": true,
|
||||
"entitlements": "entitlements.plist",
|
||||
"entitlements-inherit": "entitlements.plist",
|
||||
"signature-flags": "library"
|
||||
},
|
||||
"osxNotarize": {
|
||||
"appleId": "felix@felix.fun",
|
||||
"appleIdPassword": "this-is-a-secret"
|
||||
}
|
||||
}
|
||||
//...
|
||||
```js title='forge.config.js'
|
||||
module.exports = {
|
||||
packagerConfig: {
|
||||
osxSign: {},
|
||||
//...
|
||||
osxNotarize: {
|
||||
tool: 'notarytool',
|
||||
appleId: process.env.APPLE_ID,
|
||||
appleIdPassword: process.env.APPLE_PASSWORD,
|
||||
teamId: process.env.APPLE_TEAM_ID,
|
||||
}
|
||||
//...
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="windows" label="Windows">
|
||||
|
||||
```json title='package.json' {6-14}
|
||||
{
|
||||
```js title='forge.config.js'
|
||||
module.exports = {
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
//...
|
||||
"makers": [
|
||||
{
|
||||
"name": "@electron-forge/maker-squirrel",
|
||||
"config": {
|
||||
"certificateFile": "./cert.pfx",
|
||||
"certificatePassword": "this-is-a-secret"
|
||||
}
|
||||
}
|
||||
]
|
||||
//...
|
||||
}
|
||||
}
|
||||
makers: [
|
||||
{
|
||||
name: '@electron-forge/maker-squirrel',
|
||||
config: {
|
||||
certificateFile: './cert.pfx',
|
||||
certificatePassword: process.env.CERTIFICATE_PASSWORD,
|
||||
},
|
||||
},
|
||||
],
|
||||
//...
|
||||
}
|
||||
```
|
||||
@@ -214,13 +201,12 @@ information.
|
||||
|
||||
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
|
||||
[application packaging]: ./application-distribution.md
|
||||
[code signing]: ./code-signing.md
|
||||
[`electron-packager`]: https://github.com/electron/electron-packager
|
||||
[`electron-winstaller`]: https://github.com/electron/windows-installer
|
||||
[electron forge]: https://www.electronforge.io
|
||||
[electron forge cli documentation]: https://www.electronforge.io/cli#commands
|
||||
[makers]: https://www.electronforge.io/config/makers
|
||||
[Forge's icon tutorial]: https://www.electronforge.io/guides/create-and-add-icons
|
||||
[forge's icon tutorial]: https://www.electronforge.io/guides/create-and-add-icons
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
|
||||
@@ -78,27 +78,21 @@ Once you have it installed, you need to set it up in your Forge
|
||||
configuration. A full list of options is documented in the Forge's
|
||||
[`PublisherGitHubConfig`] API docs.
|
||||
|
||||
```json title='package.json' {6-16}
|
||||
{
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
"publishers": [
|
||||
{
|
||||
"name": "@electron-forge/publisher-github",
|
||||
"config": {
|
||||
"repository": {
|
||||
"owner": "github-user-name",
|
||||
"name": "github-repo-name"
|
||||
},
|
||||
"prerelease": false,
|
||||
"draft": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
//...
|
||||
```js title='forge.config.js'
|
||||
module.exports = {
|
||||
publishers: [
|
||||
{
|
||||
name: '@electron-forge/publisher-github',
|
||||
config: {
|
||||
repository: {
|
||||
owner: 'github-user-name',
|
||||
name: 'github-repo-name',
|
||||
},
|
||||
prerelease: false,
|
||||
draft: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ auto_filenames = {
|
||||
"docs/api/net-log.md",
|
||||
"docs/api/net.md",
|
||||
"docs/api/notification.md",
|
||||
"docs/api/parent-port.md",
|
||||
"docs/api/power-monitor.md",
|
||||
"docs/api/power-save-blocker.md",
|
||||
"docs/api/process.md",
|
||||
@@ -62,6 +63,7 @@ auto_filenames = {
|
||||
"docs/api/touch-bar-spacer.md",
|
||||
"docs/api/touch-bar.md",
|
||||
"docs/api/tray.md",
|
||||
"docs/api/utility-process.md",
|
||||
"docs/api/web-contents.md",
|
||||
"docs/api/web-frame-main.md",
|
||||
"docs/api/web-frame.md",
|
||||
@@ -220,6 +222,7 @@ auto_filenames = {
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/touch-bar.ts",
|
||||
"lib/browser/api/tray.ts",
|
||||
"lib/browser/api/utility-process.ts",
|
||||
"lib/browser/api/view.ts",
|
||||
"lib/browser/api/views/image-view.ts",
|
||||
"lib/browser/api/web-contents-view.ts",
|
||||
@@ -331,4 +334,20 @@ auto_filenames = {
|
||||
"typings/internal-ambient.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",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -313,6 +313,8 @@ filenames = {
|
||||
"shell/browser/api/electron_api_tray.h",
|
||||
"shell/browser/api/electron_api_url_loader.cc",
|
||||
"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.h",
|
||||
"shell/browser/api/electron_api_web_contents.cc",
|
||||
@@ -672,14 +674,16 @@ filenames = {
|
||||
"shell/renderer/electron_render_frame_observer.h",
|
||||
"shell/renderer/electron_renderer_client.cc",
|
||||
"shell/renderer/electron_renderer_client.h",
|
||||
"shell/renderer/electron_renderer_pepper_host_factory.cc",
|
||||
"shell/renderer/electron_renderer_pepper_host_factory.h",
|
||||
"shell/renderer/electron_sandboxed_renderer_client.cc",
|
||||
"shell/renderer/electron_sandboxed_renderer_client.h",
|
||||
"shell/renderer/renderer_client_base.cc",
|
||||
"shell/renderer/renderer_client_base.h",
|
||||
"shell/renderer/web_worker_observer.cc",
|
||||
"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.h",
|
||||
]
|
||||
|
||||
@@ -31,6 +31,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'systemPreferences', loader: () => require('./system-preferences') },
|
||||
{ name: 'TouchBar', loader: () => require('./touch-bar') },
|
||||
{ name: 'Tray', loader: () => require('./tray') },
|
||||
{ name: 'utilityProcess', loader: () => require('./utility-process') },
|
||||
{ name: 'View', loader: () => require('./view') },
|
||||
{ name: 'webContents', loader: () => require('./web-contents') },
|
||||
{ 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);
|
||||
}
|
||||
@@ -450,12 +450,14 @@ WebContents.prototype.loadURL = function (url, options) {
|
||||
const removeListeners = () => {
|
||||
this.removeListener('did-finish-load', finishListener);
|
||||
this.removeListener('did-fail-load', failListener);
|
||||
this.removeListener('did-navigate-in-page', finishListener);
|
||||
this.removeListener('did-start-navigation', navigationListener);
|
||||
this.removeListener('did-stop-loading', stopLoadingListener);
|
||||
this.removeListener('destroyed', stopLoadingListener);
|
||||
};
|
||||
this.on('did-finish-load', finishListener);
|
||||
this.on('did-fail-load', failListener);
|
||||
this.on('did-navigate-in-page', finishListener);
|
||||
this.on('did-start-navigation', navigationListener);
|
||||
this.on('did-stop-loading', stopLoadingListener);
|
||||
this.on('destroyed', stopLoadingListener);
|
||||
|
||||
@@ -33,20 +33,29 @@ function wrap <T extends AnyFn> (func: T, wrapper: (fn: AnyFn) => T) {
|
||||
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);
|
||||
|
||||
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
|
||||
global.clearImmediate = timers.clearImmediate;
|
||||
|
||||
// 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
|
||||
// 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.setInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
|
||||
// Only override the global setTimeout/setInterval impls in the browser process
|
||||
if (process.type === 'browser') {
|
||||
// Update the global version of the timer apis to use the above wrapper
|
||||
// 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.setInterval = timers.setInterval;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,10 @@ export class SrcAttribute extends WebViewAttribute {
|
||||
opts.userAgent = useragent;
|
||||
}
|
||||
|
||||
(this.webViewImpl.webviewNode as Electron.WebviewTag).loadURL(this.getValue(), opts);
|
||||
(this.webViewImpl.webviewNode as Electron.WebviewTag).loadURL(this.getValue(), opts)
|
||||
.catch(err => {
|
||||
console.error('Unexpected error while loading URL', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "22.0.0-alpha.8",
|
||||
"version": "0.0.0-development",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
"@azure/storage-blob": "^12.9.0",
|
||||
"@electron/docs-parser": "^0.12.4",
|
||||
"@electron/typescript-definitions": "^8.9.6",
|
||||
"@electron/docs-parser": "^1.0.0",
|
||||
"@electron/typescript-definitions": "^8.10.0",
|
||||
"@octokit/auth-app": "^2.10.0",
|
||||
"@octokit/rest": "^18.0.3",
|
||||
"@primer/octicons": "^10.0.0",
|
||||
@@ -92,7 +92,7 @@
|
||||
"lint:docs-relative-links": "python3 ./script/check-relative-doc-links.py",
|
||||
"lint:markdownlint": "markdownlint \"*.md\" \"docs/**/*.md\"",
|
||||
"lint:js-in-markdown": "standard-markdown docs",
|
||||
"create-api-json": "electron-docs-parser --dir=./",
|
||||
"create-api-json": "node script/create-api-json.js",
|
||||
"create-typescript-definitions": "npm run create-api-json && electron-typescript-definitions --api=electron-api.json && node spec/ts-smoke/runner.js",
|
||||
"gn-typescript-definitions": "npm run create-typescript-definitions && shx cp electron.d.ts",
|
||||
"pre-flight": "pre-flight",
|
||||
|
||||
@@ -115,6 +115,8 @@ add_electron_deps_to_license_credits_file.patch
|
||||
fix_crash_loading_non-standard_schemes_in_iframes.patch
|
||||
fix_return_v8_value_from_localframe_requestexecutescript.patch
|
||||
create_browser_v8_snapshot_file_name_fuse.patch
|
||||
feat_configure_launch_options_for_service_process.patch
|
||||
fix_on-screen-keyboard_hides_on_input_blur_in_webview.patch
|
||||
preconnect_manager.patch
|
||||
fix_remove_caption-removing_style_call.patch
|
||||
build_allow_electron_to_use_exec_script.patch
|
||||
|
||||
@@ -53,7 +53,7 @@ index 18c34ce9965912caa58457fc28be2b4f6edffb78..d1fc002ff98ea25ff4fec7bacd44140c
|
||||
|
||||
} // namespace gtk
|
||||
diff --git a/ui/gtk/window_frame_provider_gtk.cc b/ui/gtk/window_frame_provider_gtk.cc
|
||||
index 6aff3b989aff9488b85ca46a62a52a1c7cfb3215..751247c2608ca5143044f6395baae5917c909f46 100644
|
||||
index c7857a3e316554e6b5f46c023a1a8084a3263074..5ad7d4ffa7e9c12ec4640a845a4c763420c23ec2 100644
|
||||
--- a/ui/gtk/window_frame_provider_gtk.cc
|
||||
+++ b/ui/gtk/window_frame_provider_gtk.cc
|
||||
@@ -38,16 +38,18 @@ std::string GetThemeName() {
|
||||
@@ -111,8 +111,8 @@ index 6aff3b989aff9488b85ca46a62a52a1c7cfb3215..751247c2608ca5143044f6395baae591
|
||||
|
||||
WindowFrameProviderGtk::~WindowFrameProviderGtk() = default;
|
||||
|
||||
@@ -264,7 +266,7 @@ void WindowFrameProviderGtk::PaintWindowFrame(gfx::Canvas* canvas,
|
||||
top_area_height_dip * scale - asset.frame_thickness_px.top();
|
||||
@@ -272,7 +274,7 @@ void WindowFrameProviderGtk::PaintWindowFrame(
|
||||
top_area_height_dip * scale - effective_frame_thickness_px.top();
|
||||
|
||||
auto header = PaintHeaderbar({client_bounds_px.width(), top_area_height_px},
|
||||
- HeaderContext(solid_frame_, focused), scale);
|
||||
@@ -120,7 +120,7 @@ index 6aff3b989aff9488b85ca46a62a52a1c7cfb3215..751247c2608ca5143044f6395baae591
|
||||
image = gfx::ImageSkia::CreateFrom1xBitmap(header);
|
||||
// In GTK4, the headerbar gets clipped by the window.
|
||||
if (GtkCheckVersion(4)) {
|
||||
@@ -296,7 +298,7 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
|
||||
@@ -304,7 +306,7 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
|
||||
|
||||
gfx::Rect frame_bounds_dip(kMaxFrameSizeDip, kMaxFrameSizeDip,
|
||||
2 * kMaxFrameSizeDip, 2 * kMaxFrameSizeDip);
|
||||
@@ -129,7 +129,7 @@ index 6aff3b989aff9488b85ca46a62a52a1c7cfb3215..751247c2608ca5143044f6395baae591
|
||||
frame_bounds_dip.Inset(-GtkStyleContextGetPadding(focused_context));
|
||||
frame_bounds_dip.Inset(-GtkStyleContextGetBorder(focused_context));
|
||||
gfx::Size bitmap_size(BitmapSizePx(asset), BitmapSizePx(asset));
|
||||
@@ -304,7 +306,7 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
|
||||
@@ -312,7 +314,7 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
|
||||
PaintBitmap(bitmap_size, frame_bounds_dip, focused_context, scale);
|
||||
asset.unfocused_bitmap =
|
||||
PaintBitmap(bitmap_size, frame_bounds_dip,
|
||||
@@ -139,7 +139,7 @@ index 6aff3b989aff9488b85ca46a62a52a1c7cfb3215..751247c2608ca5143044f6395baae591
|
||||
// In GTK4, there's no way to obtain the frame thickness from CSS values
|
||||
// directly, so we must determine it experimentally based on the drawn
|
||||
diff --git a/ui/gtk/window_frame_provider_gtk.h b/ui/gtk/window_frame_provider_gtk.h
|
||||
index 867896de7fa036b7c8be5adf5a7731c97e942d69..2c38d399f95cc2c8851aded5993e3019941defc0 100644
|
||||
index d8cb2c6aab333cc55ad1daa70ac91b0569d33a7c..558aa3979301f79df789a29ba3ad1cf134bd6494 100644
|
||||
--- a/ui/gtk/window_frame_provider_gtk.h
|
||||
+++ b/ui/gtk/window_frame_provider_gtk.h
|
||||
@@ -14,7 +14,7 @@ namespace gtk {
|
||||
@@ -151,7 +151,7 @@ index 867896de7fa036b7c8be5adf5a7731c97e942d69..2c38d399f95cc2c8851aded5993e3019
|
||||
|
||||
WindowFrameProviderGtk(const WindowFrameProviderGtk&) = delete;
|
||||
WindowFrameProviderGtk& operator=(const WindowFrameProviderGtk&) = delete;
|
||||
@@ -69,6 +69,9 @@ class WindowFrameProviderGtk : public ui::WindowFrameProvider {
|
||||
@@ -70,6 +70,9 @@ class WindowFrameProviderGtk : public ui::WindowFrameProvider {
|
||||
|
||||
// Cached bitmaps and metrics. The scale is rounded to percent.
|
||||
base::flat_map<int, Asset> assets_;
|
||||
|
||||
@@ -3,28 +3,28 @@ From: Jeremy Apthorp <nornagon@nornagon.net>
|
||||
Date: Mon, 26 Aug 2019 12:02:51 -0700
|
||||
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
|
||||
index dd5ccfc0bdc2e071999d1bf864dc065dd1311407..7464e84f6e610749dce5c3a46afce262f29020cc 100644
|
||||
index dd5ccfc0bdc2e071999d1bf864dc065dd1311407..cfadd28fca9f80bf57578db78d5472c4f75414e1 100644
|
||||
--- a/content/browser/child_process_launcher_helper_linux.cc
|
||||
+++ b/content/browser/child_process_launcher_helper_linux.cc
|
||||
@@ -54,6 +54,18 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
|
||||
if (GetProcessType() == switches::kRendererProcess) {
|
||||
const int sandbox_fd = SandboxHostLinux::GetInstance()->GetChildSocket();
|
||||
@@ -56,6 +56,18 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
|
||||
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) {
|
||||
options->fds_to_remap.emplace_back(remapped_fd.second.get(),
|
||||
remapped_fd.first);
|
||||
|
||||
@@ -15,7 +15,7 @@ Refs changes in:
|
||||
This patch reverts the changes to fix associated crashes in Electron.
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc
|
||||
index b7c1c9e1db6e7366912447cd4c2e3d4ac6f131e6..65689678cafabd61b273ef8350f4c11394384945 100644
|
||||
index 8b6abb315d33ce752884510beca325833b893bce..b197d38944c0560c7f9cb25b706eb8a5876c59d3 100644
|
||||
--- a/third_party/blink/renderer/core/frame/frame.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/frame.cc
|
||||
@@ -124,14 +124,6 @@ bool Frame::Detach(FrameDetachType type) {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@salesforce.com>
|
||||
Date: Fri, 21 Oct 2022 16:29:06 -0700
|
||||
Subject: build: allow electron to use exec_script
|
||||
|
||||
This is similar to the //build usecase so we're OK adding ourselves here
|
||||
|
||||
diff --git a/.gn b/.gn
|
||||
index 55f5ee19f13e49dfd0aa2300d980d813474c95ef..483e0f0598bfed76b4a4283be3cfa1fdb9eafe5a 100644
|
||||
--- a/.gn
|
||||
+++ b/.gn
|
||||
@@ -169,4 +169,6 @@ exec_script_whitelist =
|
||||
|
||||
"//tools/grit/grit_rule.gni",
|
||||
"//tools/gritsettings/BUILD.gn",
|
||||
+
|
||||
+ "//electron/BUILD.gn"
|
||||
]
|
||||
@@ -33,10 +33,10 @@ index b12b4e65be8ad29054f5fdd98600888726b1d068..d67683efeabed3209eac553ff050ec2e
|
||||
"//base",
|
||||
"//build:branding_buildflags",
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index 68415e75360cec6de27d95ba01b30935c491023e..b5885d348ed5a3ed18bb524cbbde51ed185a8658 100644
|
||||
index 6af6b725815f67cee31bf03382ea69c53c2897d8..63807e436ef7011a284357e237ada68508870d87 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -4683,7 +4683,7 @@ static_library("browser") {
|
||||
@@ -4687,7 +4687,7 @@ static_library("browser") {
|
||||
|
||||
# On Windows, the hashes are embedded in //chrome:chrome_initial rather
|
||||
# than here in :chrome_dll.
|
||||
@@ -46,10 +46,10 @@ index 68415e75360cec6de27d95ba01b30935c491023e..b5885d348ed5a3ed18bb524cbbde51ed
|
||||
sources += [ "certificate_viewer_stub.cc" ]
|
||||
}
|
||||
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
|
||||
index 15b713cb25724fc2a0b743725837720492739ed2..f7649e3bc908cd9a04294d63c405f98b6363b05d 100644
|
||||
index 1c660e23fbfbe97d489bb7b0e463ef40197851be..60a43d97d47924e4d95ab27feb0a365e52878bfc 100644
|
||||
--- a/chrome/test/BUILD.gn
|
||||
+++ b/chrome/test/BUILD.gn
|
||||
@@ -6156,7 +6156,6 @@ test("unit_tests") {
|
||||
@@ -6157,7 +6157,6 @@ test("unit_tests") {
|
||||
|
||||
deps += [
|
||||
"//chrome:other_version",
|
||||
@@ -57,7 +57,7 @@ index 15b713cb25724fc2a0b743725837720492739ed2..f7649e3bc908cd9a04294d63c405f98b
|
||||
"//chrome//services/util_win:unit_tests",
|
||||
"//chrome/app:chrome_dll_resources",
|
||||
"//chrome/app:crash_reporter_client_win_unit_tests",
|
||||
@@ -6181,6 +6180,10 @@ test("unit_tests") {
|
||||
@@ -6182,6 +6181,10 @@ test("unit_tests") {
|
||||
"//ui/resources",
|
||||
]
|
||||
|
||||
@@ -68,7 +68,7 @@ index 15b713cb25724fc2a0b743725837720492739ed2..f7649e3bc908cd9a04294d63c405f98b
|
||||
ldflags = [
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-error-l1-1-0.dll",
|
||||
"/DELAYLOAD:api-ms-win-core-winrt-l1-1-0.dll",
|
||||
@@ -7107,7 +7110,7 @@ test("unit_tests") {
|
||||
@@ -7109,7 +7112,7 @@ test("unit_tests") {
|
||||
}
|
||||
|
||||
deps += [
|
||||
@@ -77,7 +77,7 @@ index 15b713cb25724fc2a0b743725837720492739ed2..f7649e3bc908cd9a04294d63c405f98b
|
||||
"//chrome/browser/autofill_assistant/password_change/vector_icons:vector_icons",
|
||||
"//chrome/browser/enterprise/connectors/analysis:features",
|
||||
"//chrome/browser/media/router:test_support",
|
||||
@@ -7230,6 +7233,10 @@ test("unit_tests") {
|
||||
@@ -7232,6 +7235,10 @@ test("unit_tests") {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@ to deal with color spaces. That is being tracked at
|
||||
https://crbug.com/634542 and https://crbug.com/711107.
|
||||
|
||||
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
|
||||
index e4db8136e957313da28b7ef2bcff4ec91eee5deb..bd4fbda15d8d73d92ec8684a66a4aaeea53990d1 100644
|
||||
index d129d26e2ee18a30aa44c7f9c2ba3af2d12dca6a..06867e89b6a0a73dc7d8f4060b366a8b43dd5525 100644
|
||||
--- a/cc/trees/layer_tree_host_impl.cc
|
||||
+++ b/cc/trees/layer_tree_host_impl.cc
|
||||
@@ -1882,6 +1882,10 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
|
||||
@@ -1890,6 +1890,10 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
|
||||
TargetColorParams LayerTreeHostImpl::GetTargetColorParams(
|
||||
gfx::ContentColorUsage content_color_usage) const {
|
||||
TargetColorParams params;
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: disable_hidden.patch
|
||||
Electron uses this to disable background throttling for hidden windows.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
index 0133090585389abafc843d7f7258d3d2281a5b54..a81933bcbb926659ba9dc8c775ce022a0f7bdafb 100644
|
||||
index ff4e181fae1849772b699bf83e0f7e9556dbd71e..84b3bcbfb4f52098efad6f2b8ab8e8445d8fe13a 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
@@ -819,6 +819,9 @@ void RenderWidgetHostImpl::WasHidden() {
|
||||
@@ -824,6 +824,9 @@ void RenderWidgetHostImpl::WasHidden() {
|
||||
if (is_hidden_)
|
||||
return;
|
||||
|
||||
@@ -20,12 +20,12 @@ index 0133090585389abafc843d7f7258d3d2281a5b54..a81933bcbb926659ba9dc8c775ce022a
|
||||
blink::mojom::PointerLockResult::kWrongDocument);
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
|
||||
index 688c6b226d83996e29f5aebc542163fe6b53e77a..a57beb8ba60416db6bc8d99eea0c450d3eb7a44b 100644
|
||||
index 6ea55a22496a5186513963d06fa700cb82758177..bbcff3ff49234e2f6b9fcdaa1409678189d91b67 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_impl.h
|
||||
+++ b/content/browser/renderer_host/render_widget_host_impl.h
|
||||
@@ -889,6 +889,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl
|
||||
|
||||
SiteInstanceGroup* GetSiteInstanceGroup();
|
||||
@@ -894,6 +894,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl
|
||||
cc::BrowserControlsState current,
|
||||
bool animate);
|
||||
|
||||
+ // Electron: Prevents the widget from getting hidden.
|
||||
+ bool disable_hidden_ = false;
|
||||
|
||||
@@ -13,10 +13,10 @@ uses internally for things like menus and devtools.
|
||||
We can remove this patch once it has in some shape been upstreamed.
|
||||
|
||||
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc
|
||||
index bf77c08ecf45bc9da36b99442b7ce14df3f8a4fc..cc2e46107f645dfc7c0265c93aeca037e6094559 100644
|
||||
index c4049d94aaa00774777f3788c7093cb19101fcb5..92866fc8fe2375f9703ce77be980646bf61cf8ac 100644
|
||||
--- a/ui/native_theme/native_theme.cc
|
||||
+++ b/ui/native_theme/native_theme.cc
|
||||
@@ -132,6 +132,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors,
|
||||
@@ -144,6 +144,8 @@ NativeTheme::NativeTheme(bool should_use_dark_colors,
|
||||
NativeTheme::~NativeTheme() = default;
|
||||
|
||||
bool NativeTheme::ShouldUseDarkColors() const {
|
||||
|
||||
@@ -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 1bb75bb14aa1afd9ebebf343b1a9436cd3f790f1..9260fa0eba56eca9c24a16880b07efb5481c15a1 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();
|
||||
@@ -358,9 +378,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);
|
||||
|
||||
@@ -17,10 +17,10 @@ policy->CanCommitOriginAndUrl.
|
||||
Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/3856266.
|
||||
|
||||
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
|
||||
index 14dfb45da60c0552be10c8998ec126db1e7be202..bf89fb878e2bddf98558e6d6e9db8b00d525baf2 100644
|
||||
index 28605cda32c8a499271ff358bbdbaa2261012c88..1496adf92d0e0c7868869263eba5c7786bed6bd8 100644
|
||||
--- a/content/browser/renderer_host/navigation_request.cc
|
||||
+++ b/content/browser/renderer_host/navigation_request.cc
|
||||
@@ -6716,10 +6716,11 @@ std::pair<url::Origin, std::string> NavigationRequest::
|
||||
@@ -6724,10 +6724,11 @@ std::pair<url::Origin, std::string> NavigationRequest::
|
||||
if (IsForMhtmlSubframe())
|
||||
return origin_with_debug_info;
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ index 89b9323c08cfed0d3ea3a0ec1beaa0bdfabe343e..d000b7f43f393d297a3715ea4279537b
|
||||
|
||||
} // namespace
|
||||
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
|
||||
index eb178455e92ae0c5f6d5033ffc1990b711466a83..9b4f909501f6cbc50a7d88cef679add9e6c454d2 100644
|
||||
index 1b91b71bae8201d7cae5850a8c810bd179a36ab1..7f4bc1ded505067ba83001b389ab5813bad17dc4 100644
|
||||
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
|
||||
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
|
||||
@@ -566,10 +566,12 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) {
|
||||
|
||||
@@ -796,10 +796,10 @@ index 51a5497b809cda82b223138f43507172a9f066ae..71227492fd87e577518f3bf55ded37b0
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
// Set options for print preset from source PDF document.
|
||||
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
|
||||
index 5a032fac280466c9e1498c4d629acbe0b0bb1ec6..3274b0331aaa077603e8ad10ca9fe4b3cebc3161 100644
|
||||
index 18328bedfdd0dc735126f7541ee7ed3e736d94b0..e5e28db9a970b49ac5624c59cbaeed9aff5e52d8 100644
|
||||
--- a/content/browser/BUILD.gn
|
||||
+++ b/content/browser/BUILD.gn
|
||||
@@ -2819,8 +2819,9 @@ source_set("browser") {
|
||||
@@ -2821,8 +2821,9 @@ source_set("browser") {
|
||||
"//ppapi/shared_impl",
|
||||
]
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@ index 5f1eea557b549a6db081ff925ba9995b0591d9fa..62c5ef52ec774623f2cfc7c269973a26
|
||||
// RenderWidgetHost on the primary main frame, and false otherwise.
|
||||
virtual bool IsWidgetForPrimaryMainFrame(RenderWidgetHostImpl*);
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
index a81933bcbb926659ba9dc8c775ce022a0f7bdafb..bc0d1fab2160fa476c875a6a5e549c878c855795 100644
|
||||
index 84b3bcbfb4f52098efad6f2b8ab8e8445d8fe13a..6ed422b4bfc5019a935634cad5488b2927461486 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
|
||||
@@ -2093,6 +2093,8 @@ void RenderWidgetHostImpl::FilterDropData(DropData* drop_data) {
|
||||
@@ -2098,6 +2098,8 @@ void RenderWidgetHostImpl::FilterDropData(DropData* drop_data) {
|
||||
void RenderWidgetHostImpl::SetCursor(const ui::Cursor& cursor) {
|
||||
if (view_)
|
||||
view_->UpdateCursor(WebCursor(cursor));
|
||||
|
||||
@@ -6,7 +6,7 @@ Subject: render_widget_host_view_base.patch
|
||||
... something to do with OSR? and maybe <webview> as well? terrifying.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
|
||||
index b0c9044e9978ff38ecd730541e512bbe73e45a27..aecd5ef1019188a593f23436b5392f57805620b0 100644
|
||||
index e5d57b61367227762c1e4cab6a3d7c7ffc4c74a5..b1217d282b8c70a3afb2c5af130aed9c0d78de2f 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
|
||||
@@ -706,6 +706,13 @@ bool RenderWidgetHostViewBase::ScreenRectIsUnstableFor(
|
||||
|
||||
@@ -67,7 +67,7 @@ index bb82952036825aa6b19449956984cb3c90874af1..9550aee35d129c5cdd71de67150dfb70
|
||||
const WebSecurityOrigin& script_origin) {
|
||||
return false;
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
|
||||
index 8bf05b2789af3f7d2f6ecda9b7a6eb4abb84dbd6..b739ed97b190bce06b6c0f2bc12a1869fa590e39 100644
|
||||
index 9056d1bb293dfb8c40c7e3d3d94c238cd514a2aa..fc576e5ad08b93204e1ebfb5486d0ad15733f317 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
|
||||
@@ -747,6 +747,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() {
|
||||
|
||||
@@ -51,3 +51,4 @@ fixup_for_wc_98-compat-extra-semi.patch
|
||||
drop_deserializerequest_move_constructor_for_c_20_compat.patch
|
||||
fix_parallel_test-v8-stats.patch
|
||||
fix_expose_the_built-in_electron_module_via_the_esm_loader.patch
|
||||
chore_enable_c_17_for_native_modules.patch
|
||||
|
||||
54
patches/node/chore_enable_c_17_for_native_modules.patch
Normal file
54
patches/node/chore_enable_c_17_for_native_modules.patch
Normal file
@@ -0,0 +1,54 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Wed, 16 Nov 2022 13:18:23 +0900
|
||||
Subject: chore: enable c++17 for native modules
|
||||
|
||||
V8 headers shipped since 10.4 use C++17 featuers, update the compile flags
|
||||
correspondinly for native addons. C++ version in this file should be updated
|
||||
following the version bump in upstream.
|
||||
|
||||
Next update: crbug.com/1284275
|
||||
|
||||
diff --git a/common.gypi b/common.gypi
|
||||
index 8441a5270212af7e4643e6b4ee100a22f8e6f51c..37908fc70c6e95970ef7bd4ee83799710397a450 100644
|
||||
--- a/common.gypi
|
||||
+++ b/common.gypi
|
||||
@@ -306,7 +306,10 @@
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
- 'AdditionalOptions': ['/Zc:__cplusplus'],
|
||||
+ 'AdditionalOptions': [
|
||||
+ '/Zc:__cplusplus',
|
||||
+ '-std:c++17',
|
||||
+ ],
|
||||
'BufferSecurityCheck': 'true',
|
||||
'target_conditions': [
|
||||
['_toolset=="target"', {
|
||||
@@ -438,7 +441,7 @@
|
||||
}],
|
||||
[ 'OS in "linux freebsd openbsd solaris android aix cloudabi"', {
|
||||
'cflags': [ '-Wall', '-Wextra', '-Wno-unused-parameter', ],
|
||||
- 'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++14' ],
|
||||
+ 'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++17' ],
|
||||
'defines': [ '__STDC_FORMAT_MACROS' ],
|
||||
'ldflags': [ '-rdynamic' ],
|
||||
'target_conditions': [
|
||||
@@ -578,7 +581,7 @@
|
||||
['clang==1', {
|
||||
'xcode_settings': {
|
||||
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
|
||||
- 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++14', # -std=gnu++14
|
||||
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++17', # -std=gnu++17
|
||||
'CLANG_CXX_LIBRARY': 'libc++',
|
||||
},
|
||||
}],
|
||||
@@ -651,7 +654,7 @@
|
||||
'-qASM',
|
||||
],
|
||||
'cflags_cc': [
|
||||
- '-qxclang=-std=c++14',
|
||||
+ '-qxclang=-std=c++17',
|
||||
],
|
||||
'ldflags': [
|
||||
'-q64',
|
||||
@@ -3,3 +3,4 @@ fix_ensure_that_self_is_retained_until_the_racsignal_is_complete.patch
|
||||
fix_use_kseccschecknestedcode_kseccsstrictvalidate_in_the_sec.patch
|
||||
feat_add_new_squirrel_mac_bundle_installation_method_behind_flag.patch
|
||||
refactor_use_posix_spawn_instead_of_nstask_so_we_can_disclaim_the.patch
|
||||
fix_abort_installation_attempt_at_the_final_mile_if_the_app_is.patch
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@salesforce.com>
|
||||
Date: Tue, 25 Oct 2022 13:09:55 -0700
|
||||
Subject: fix: abort installation attempt at the final mile if the app is
|
||||
running
|
||||
|
||||
There is a race condition between ShipIt launching and it performing an atomic rename to "install" the app bundle. This fixes the race by checking the list of running apps immediately before performing the rename.
|
||||
|
||||
diff --git a/Squirrel/SQRLInstaller.h b/Squirrel/SQRLInstaller.h
|
||||
index 2de1c384aae200f41de429cc35313e4c2ba9d0de..35a0c99129478f09526667d73c9544c3bfe14706 100644
|
||||
--- a/Squirrel/SQRLInstaller.h
|
||||
+++ b/Squirrel/SQRLInstaller.h
|
||||
@@ -37,6 +37,9 @@ extern const NSInteger SQRLInstallerErrorMovingAcrossVolumes;
|
||||
// There was an error changing the file permissions of the update.
|
||||
extern const NSInteger SQRLInstallerErrorChangingPermissions;
|
||||
|
||||
+// There was a running instance of the app just prior to the update attempt
|
||||
+extern const NSInteger SQRLInstallerErrorAppStillRunning;
|
||||
+
|
||||
@class RACCommand;
|
||||
|
||||
// Performs the installation of an update, saving its intermediate state to user
|
||||
diff --git a/Squirrel/SQRLInstaller.m b/Squirrel/SQRLInstaller.m
|
||||
index c1f328fa8c3689218ef260347cb8f9d30b789efe..f502df2f88424ea902a061adfeb30358daf212e4 100644
|
||||
--- a/Squirrel/SQRLInstaller.m
|
||||
+++ b/Squirrel/SQRLInstaller.m
|
||||
@@ -36,6 +36,7 @@
|
||||
const NSInteger SQRLInstallerErrorInvalidState = -6;
|
||||
const NSInteger SQRLInstallerErrorMovingAcrossVolumes = -7;
|
||||
const NSInteger SQRLInstallerErrorChangingPermissions = -8;
|
||||
+const NSInteger SQRLInstallerErrorAppStillRunning = -9;
|
||||
|
||||
NSString * const SQRLShipItInstallationAttemptsKey = @"SQRLShipItInstallationAttempts";
|
||||
NSString * const SQRLInstallerOwnedBundleKey = @"SQRLInstallerOwnedBundle";
|
||||
@@ -286,6 +287,19 @@ - (RACSignal *)installRequest:(SQRLShipItRequest *)request {
|
||||
return [[[[self
|
||||
renameIfNeeded:request updateBundleURL:updateBundleURL]
|
||||
flattenMap:^(SQRLShipItRequest *request) {
|
||||
+ // Final validation that the application is not running again;
|
||||
+ NSArray *apps = [[NSRunningApplication runningApplicationsWithBundleIdentifier:request.bundleIdentifier] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSRunningApplication *app, NSDictionary *bindings) {
|
||||
+ return [[[app bundleURL] URLByStandardizingPath] isEqual:request.targetBundleURL];
|
||||
+ }]];
|
||||
+ if ([apps count] != 0) {
|
||||
+ NSLog(@"Aborting update attempt because there are %lu running instances of the target app", [apps count]);
|
||||
+ NSDictionary *errorInfo = @{
|
||||
+ NSLocalizedDescriptionKey: NSLocalizedString(@"App Still Running Error", nil),
|
||||
+ NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"All instances of the target application should be quit during the update process", nil),
|
||||
+ };
|
||||
+ return [RACSignal error:[NSError errorWithDomain:SQRLInstallerErrorDomain code:SQRLInstallerErrorAppStillRunning userInfo:errorInfo]];
|
||||
+ }
|
||||
+
|
||||
return [[self acquireTargetBundleURLForRequest:request] concat:[RACSignal return:request]];
|
||||
}]
|
||||
flattenMap:^(SQRLShipItRequest *request) {
|
||||
diff --git a/Squirrel/ShipIt-main.m b/Squirrel/ShipIt-main.m
|
||||
index 2c515ffdd67052a08ee8155c0e46b57e9721a0e5..5f3e29642012d04fc506b730a4e87fba861df250 100644
|
||||
--- a/Squirrel/ShipIt-main.m
|
||||
+++ b/Squirrel/ShipIt-main.m
|
||||
@@ -201,8 +201,14 @@ static void installRequest(RACSignal *readRequestSignal, NSString *applicationId
|
||||
return action;
|
||||
}]
|
||||
subscribeError:^(NSError *error) {
|
||||
- NSLog(@"Installation error: %@", error);
|
||||
- exit(EXIT_FAILURE);
|
||||
+ if ([[error domain] isEqual:SQRLInstallerErrorDomain] && [error code] == SQRLInstallerErrorAppStillRunning) {
|
||||
+ NSLog(@"Installation cancelled: %@", error);
|
||||
+ clearInstallationAttempts(applicationIdentifier);
|
||||
+ exit(EXIT_SUCCESS);
|
||||
+ } else {
|
||||
+ NSLog(@"Installation error: %@", error);
|
||||
+ exit(EXIT_FAILURE);
|
||||
+ }
|
||||
} completed:^{
|
||||
exit(EXIT_SUCCESS);
|
||||
}];
|
||||
@@ -9,10 +9,10 @@ necessary for native modules to load.
|
||||
Also, some fixes relating to mksnapshot on ARM.
|
||||
|
||||
diff --git a/BUILD.gn b/BUILD.gn
|
||||
index 29eb0dc060063e0f68b16d5809e39ce2ebc8156e..f3a5b9f5d1e1dcb881497d793f982a8c38091bb4 100644
|
||||
index 61187af3053a02e62cc75ae0c6e368dd541af877..a596586c695f4a4d8daea63881c2334414b6ef5c 100644
|
||||
--- a/BUILD.gn
|
||||
+++ b/BUILD.gn
|
||||
@@ -634,7 +634,7 @@ config("internal_config") {
|
||||
@@ -636,7 +636,7 @@ config("internal_config") {
|
||||
":cppgc_header_features",
|
||||
]
|
||||
|
||||
@@ -21,7 +21,7 @@ index 29eb0dc060063e0f68b16d5809e39ce2ebc8156e..f3a5b9f5d1e1dcb881497d793f982a8c
|
||||
defines += [ "BUILDING_V8_SHARED" ]
|
||||
}
|
||||
|
||||
@@ -6073,7 +6073,7 @@ if (current_toolchain == v8_generator_toolchain) {
|
||||
@@ -6075,7 +6075,7 @@ if (current_toolchain == v8_generator_toolchain) {
|
||||
"src/interpreter/bytecodes.h",
|
||||
]
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ This patch can be safely removed if, when it is removed, `node.lib` does not
|
||||
contain any standard C++ library exports (e.g. `std::ostringstream`).
|
||||
|
||||
diff --git a/BUILD.gn b/BUILD.gn
|
||||
index 12e322d66485ad174eb6cc7220296d0f8eb4655d..f481aaa18d18034b7f5ddd834820c2d065e39a83 100644
|
||||
index 61752da47dd3393ae25a949a92eda4b8aaca8f74..c3393a116390d5321da578110c9d3ab227126351 100644
|
||||
--- a/BUILD.gn
|
||||
+++ b/BUILD.gn
|
||||
@@ -634,6 +634,10 @@ config("internal_config") {
|
||||
@@ -636,6 +636,10 @@ config("internal_config") {
|
||||
":cppgc_header_features",
|
||||
]
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: expose_mksnapshot.patch
|
||||
Needed in order to target mksnapshot for mksnapshot zip.
|
||||
|
||||
diff --git a/BUILD.gn b/BUILD.gn
|
||||
index f3a5b9f5d1e1dcb881497d793f982a8c38091bb4..12e322d66485ad174eb6cc7220296d0f8eb4655d 100644
|
||||
index a596586c695f4a4d8daea63881c2334414b6ef5c..61752da47dd3393ae25a949a92eda4b8aaca8f74 100644
|
||||
--- a/BUILD.gn
|
||||
+++ b/BUILD.gn
|
||||
@@ -6085,7 +6085,6 @@ if (current_toolchain == v8_generator_toolchain) {
|
||||
@@ -6087,7 +6087,6 @@ if (current_toolchain == v8_generator_toolchain) {
|
||||
|
||||
if (current_toolchain == v8_snapshot_toolchain) {
|
||||
v8_executable("mksnapshot") {
|
||||
|
||||
17
script/create-api-json.js
Normal file
17
script/create-api-json.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const { parseDocs } = require('@electron/docs-parser');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { getElectronVersion } = require('./lib/get-version');
|
||||
|
||||
parseDocs({
|
||||
baseDirectory: path.resolve(__dirname, '..'),
|
||||
packageMode: 'single',
|
||||
useReadme: false,
|
||||
moduleVersion: getElectronVersion()
|
||||
}).then((api) => {
|
||||
return fs.promises.writeFile(path.resolve(__dirname, '..', 'electron-api.json'), JSON.stringify(api, null, 2));
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -40,6 +40,10 @@ const main = async () => {
|
||||
{
|
||||
name: 'asar_bundle_deps',
|
||||
config: 'webpack.config.asar.js'
|
||||
},
|
||||
{
|
||||
name: 'utility_bundle_deps',
|
||||
config: 'webpack.config.utility.js'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ const path = require('path');
|
||||
const semver = require('semver');
|
||||
|
||||
const outputPath = process.argv[2];
|
||||
|
||||
const currentVersion = fs.readFileSync(path.resolve(__dirname, '../ELECTRON_VERSION'), 'utf8').trim();
|
||||
const currentVersion = process.argv[3];
|
||||
|
||||
const parsed = semver.parse(currentVersion);
|
||||
|
||||
@@ -20,9 +19,11 @@ const {
|
||||
} = parsed;
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify({
|
||||
full_version: currentVersion,
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
prerelease,
|
||||
prerelease_number: prerelease ? parsed.prerelease[parsed.prerelease.length - 1] : '0',
|
||||
has_prerelease: prerelease === '' ? 0 : 1
|
||||
}, null, 2));
|
||||
|
||||
22
script/lib/get-version.js
Normal file
22
script/lib/get-version.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const { spawnSync } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
module.exports.getElectronVersion = () => {
|
||||
// Find the nearest tag to the current HEAD
|
||||
// This is equivilant to our old logic of "use a value in package.json" for the following reasons
|
||||
//
|
||||
// 1. Whenever we updated the package.json we ALSO pushed a tag with the same version
|
||||
// 2. Whenever we _reverted_ a bump all we actually did was push a commit that deleted the tag and changed the version number back
|
||||
//
|
||||
// The only difference in the "git describe" technique is that technically a commit can "change" it's version
|
||||
// number if a tag is created / removed retroactively. i.e. the first time a commit is pushed it will be 1.2.3
|
||||
// and after the tag is made rebuilding the same commit will result in it being 1.2.4
|
||||
const output = spawnSync('git', ['describe', '--tags', '--abbrev=0'], {
|
||||
cwd: path.resolve(__dirname, '..', '..')
|
||||
});
|
||||
if (output.status !== 0) {
|
||||
console.error(output.stderr);
|
||||
throw new Error('Failed to get current electron version');
|
||||
}
|
||||
return output.stdout.toString().trim().replace(/^v/g, '');
|
||||
};
|
||||
@@ -15,7 +15,9 @@ except ImportError:
|
||||
from urllib2 import urlopen
|
||||
import zipfile
|
||||
|
||||
from lib.config import is_verbose_mode
|
||||
# from lib.config import is_verbose_mode
|
||||
def is_verbose_mode():
|
||||
return False
|
||||
|
||||
ELECTRON_DIR = os.path.abspath(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
@@ -149,11 +151,17 @@ def get_electron_branding():
|
||||
with open(branding_file_path) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
cached_electron_version = None
|
||||
def get_electron_version():
|
||||
SOURCE_ROOT = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
|
||||
version_file = os.path.join(SOURCE_ROOT, 'ELECTRON_VERSION')
|
||||
with open(version_file) as f:
|
||||
return 'v' + f.read().strip()
|
||||
global cached_electron_version
|
||||
if cached_electron_version is None:
|
||||
cached_electron_version = str.strip(execute([
|
||||
'node',
|
||||
'-p',
|
||||
'require("./script/lib/get-version").getElectronVersion()'
|
||||
], cwd=ELECTRON_DIR).decode())
|
||||
return cached_electron_version
|
||||
|
||||
def store_artifact(prefix, key_prefix, files):
|
||||
# Azure Storage
|
||||
|
||||
@@ -67,6 +67,8 @@ async function main () {
|
||||
console.error(`Found ${missing.length} missing disabled specs: \n${missing.join('\n')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const options = args.default ? defaultOptions : getCustomOptions();
|
||||
|
||||
3
script/print-version.py
Normal file
3
script/print-version.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from lib.util import get_electron_version
|
||||
|
||||
print(get_electron_version())
|
||||
@@ -6,6 +6,7 @@ const got = require('got');
|
||||
const semver = require('semver');
|
||||
|
||||
const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils');
|
||||
const { getElectronVersion } = require('../lib/get-version');
|
||||
const rootPackageJson = require('../../package.json');
|
||||
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
@@ -34,7 +35,6 @@ const files = [
|
||||
|
||||
const jsonFields = [
|
||||
'name',
|
||||
'version',
|
||||
'repository',
|
||||
'description',
|
||||
'license',
|
||||
@@ -44,6 +44,9 @@ const jsonFields = [
|
||||
|
||||
let npmTag = '';
|
||||
|
||||
const currentElectronVersion = getElectronVersion();
|
||||
const isNightlyElectronVersion = currentElectronVersion.includes('nightly');
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
temp.mkdir('electron-npm', (err, dirPath) => {
|
||||
if (err) {
|
||||
@@ -68,6 +71,7 @@ new Promise((resolve, reject) => {
|
||||
jsonFields.forEach((fieldName) => {
|
||||
packageJson[fieldName] = rootPackageJson[fieldName];
|
||||
});
|
||||
packageJson.version = currentElectronVersion;
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'package.json'),
|
||||
JSON.stringify(packageJson, null, 2)
|
||||
@@ -75,27 +79,27 @@ new Promise((resolve, reject) => {
|
||||
|
||||
return octokit.repos.listReleases({
|
||||
owner: 'electron',
|
||||
repo: rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
|
||||
repo: isNightlyElectronVersion ? 'nightlies' : 'electron'
|
||||
});
|
||||
})
|
||||
.then((releases) => {
|
||||
// download electron.d.ts from release
|
||||
const release = releases.data.find(
|
||||
(release) => release.tag_name === `v${rootPackageJson.version}`
|
||||
(release) => release.tag_name === `v${currentElectronVersion}`
|
||||
);
|
||||
if (!release) {
|
||||
throw new Error(`cannot find release with tag v${rootPackageJson.version}`);
|
||||
throw new Error(`cannot find release with tag v${currentElectronVersion}`);
|
||||
}
|
||||
return release;
|
||||
})
|
||||
.then(async (release) => {
|
||||
const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts');
|
||||
if (!tsdAsset) {
|
||||
throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`);
|
||||
throw new Error(`cannot find electron.d.ts from v${currentElectronVersion} release assets`);
|
||||
}
|
||||
|
||||
const typingsContent = await getAssetContents(
|
||||
rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron',
|
||||
isNightlyElectronVersion ? 'nightlies' : 'electron',
|
||||
tsdAsset.id
|
||||
);
|
||||
|
||||
@@ -106,11 +110,11 @@ new Promise((resolve, reject) => {
|
||||
.then(async (release) => {
|
||||
const checksumsAsset = release.assets.find((asset) => asset.name === 'SHASUMS256.txt');
|
||||
if (!checksumsAsset) {
|
||||
throw new Error(`cannot find SHASUMS256.txt from v${rootPackageJson.version} release assets`);
|
||||
throw new Error(`cannot find SHASUMS256.txt from v${currentElectronVersion} release assets`);
|
||||
}
|
||||
|
||||
const checksumsContent = await getAssetContents(
|
||||
rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron',
|
||||
isNightlyElectronVersion ? 'nightlies' : 'electron',
|
||||
checksumsAsset.id
|
||||
);
|
||||
|
||||
@@ -127,7 +131,7 @@ new Promise((resolve, reject) => {
|
||||
.then(async (release) => {
|
||||
const currentBranch = await getCurrentBranch();
|
||||
|
||||
if (release.tag_name.indexOf('nightly') > 0) {
|
||||
if (isNightlyElectronVersion) {
|
||||
// TODO(main-migration): Simplify once main branch is renamed.
|
||||
if (currentBranch === 'master' || currentBranch === 'main') {
|
||||
// Nightlies get published to their own module, so they should be tagged as latest
|
||||
@@ -164,7 +168,7 @@ new Promise((resolve, reject) => {
|
||||
.then(() => childProcess.execSync('npm pack', { cwd: tempDir }))
|
||||
.then(() => {
|
||||
// test that the package can install electron prebuilt from github release
|
||||
const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`);
|
||||
const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${currentElectronVersion}.tgz`);
|
||||
return new Promise((resolve, reject) => {
|
||||
const result = childProcess.spawnSync('npm', ['install', tarballPath, '--force', '--silent'], {
|
||||
env: { ...process.env, electron_config_cache: tempDir },
|
||||
@@ -190,7 +194,7 @@ new Promise((resolve, reject) => {
|
||||
});
|
||||
})
|
||||
.then((tarballPath) => {
|
||||
const existingVersionJSON = childProcess.execSync(`npm view electron@${rootPackageJson.version} --json`).toString('utf-8');
|
||||
const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8');
|
||||
// It's possible this is a re-run and we already have published the package, if not we just publish like normal
|
||||
if (!existingVersionJSON) {
|
||||
childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
@@ -198,22 +202,21 @@ new Promise((resolve, reject) => {
|
||||
})
|
||||
.then(() => {
|
||||
const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString());
|
||||
const localVersion = rootPackageJson.version;
|
||||
const parsedLocalVersion = semver.parse(localVersion);
|
||||
const parsedLocalVersion = semver.parse(currentElectronVersion);
|
||||
if (rootPackageJson.name === 'electron') {
|
||||
// We should only customly add dist tags for non-nightly releases where the package name is still
|
||||
// "electron"
|
||||
if (parsedLocalVersion.prerelease.length === 0 &&
|
||||
semver.gt(localVersion, currentTags.latest)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
semver.gt(currentElectronVersion, currentTags.latest)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${currentElectronVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
}
|
||||
if (parsedLocalVersion.prerelease[0] === 'beta' &&
|
||||
semver.gt(localVersion, currentTags.beta)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
semver.gt(currentElectronVersion, currentTags.beta)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${currentElectronVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
}
|
||||
if (parsedLocalVersion.prerelease[0] === 'alpha' &&
|
||||
semver.gt(localVersion, currentTags.alpha)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${localVersion} alpha --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
semver.gt(currentElectronVersion, currentTags.alpha)) {
|
||||
childProcess.execSync(`npm dist-tag add electron@${currentElectronVersion} alpha --otp=${process.env.ELECTRON_NPM_OTP}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,26 +18,6 @@ require('colors');
|
||||
const pass = '✓'.green;
|
||||
const fail = '✗'.red;
|
||||
|
||||
function getLastBumpCommit (tag) {
|
||||
const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString();
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
async function revertBumpCommit (tag) {
|
||||
const branch = await getCurrentBranch();
|
||||
const commitToRevert = getLastBumpCommit(tag).hash;
|
||||
await GitProcess.exec(['pull', '--rebase']);
|
||||
await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR);
|
||||
const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR);
|
||||
if (pushDetails.exitCode === 0) {
|
||||
console.log(`${pass} successfully reverted release commit.`);
|
||||
} else {
|
||||
const error = GitProcess.parseError(pushDetails.stderr);
|
||||
console.error(`${fail} could not push release commit: `, error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteDraft (releaseId, targetRepo) {
|
||||
try {
|
||||
const result = await octokit.repos.getRelease({
|
||||
@@ -80,9 +60,6 @@ async function cleanReleaseArtifacts () {
|
||||
const releaseId = args.releaseID.length > 0 ? args.releaseID : null;
|
||||
const isNightly = args.tag.includes('nightly');
|
||||
|
||||
// try to revert commit regardless of tag and draft deletion status
|
||||
await revertBumpCommit(args.tag);
|
||||
|
||||
if (releaseId) {
|
||||
if (isNightly) {
|
||||
await deleteDraft(releaseId, 'nightlies');
|
||||
|
||||
@@ -12,8 +12,6 @@ const args = require('minimist')(process.argv.slice(2), {
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
const got = require('got');
|
||||
const pkg = require('../../package.json');
|
||||
const pkgVersion = `v${pkg.version}`;
|
||||
const path = require('path');
|
||||
const temp = require('temp').track();
|
||||
const { URL } = require('url');
|
||||
@@ -25,8 +23,11 @@ const pass = '✓'.green;
|
||||
const fail = '✗'.red;
|
||||
|
||||
const { ELECTRON_DIR } = require('../lib/utils');
|
||||
const { getElectronVersion } = require('../lib/get-version');
|
||||
const getUrlHash = require('./get-url-hash');
|
||||
|
||||
const pkgVersion = `v${getElectronVersion()}`;
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.ELECTRON_GITHUB_TOKEN
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ from lib.util import get_electron_branding, execute, get_electron_version, \
|
||||
SRC_DIR, ELECTRON_DIR, TS_NODE
|
||||
|
||||
|
||||
ELECTRON_VERSION = get_electron_version()
|
||||
ELECTRON_VERSION = 'v' + get_electron_version()
|
||||
|
||||
PROJECT_NAME = get_electron_branding()['project_name']
|
||||
PRODUCT_NAME = get_electron_branding()['product_name']
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { GitProcess } = require('dugite');
|
||||
const { promises: fs } = require('fs');
|
||||
const semver = require('semver');
|
||||
const path = require('path');
|
||||
const minimist = require('minimist');
|
||||
|
||||
const { ELECTRON_DIR } = require('../lib/utils');
|
||||
const { getElectronVersion } = require('../lib/get-version');
|
||||
const versionUtils = require('./version-utils');
|
||||
const supported = path.resolve(ELECTRON_DIR, 'docs', 'tutorial', 'support.md');
|
||||
|
||||
const writeFile = fs.writeFile;
|
||||
const readFile = fs.readFile;
|
||||
|
||||
function parseCommandLine () {
|
||||
let help;
|
||||
@@ -37,7 +30,7 @@ function parseCommandLine () {
|
||||
// run the script
|
||||
async function main () {
|
||||
const opts = parseCommandLine();
|
||||
const currentVersion = await versionUtils.getElectronVersion();
|
||||
const currentVersion = getElectronVersion();
|
||||
const version = await nextVersion(opts.bump, currentVersion);
|
||||
|
||||
const parsed = semver.parse(version);
|
||||
@@ -54,20 +47,6 @@ async function main () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (shouldUpdateSupported(opts.bump, currentVersion, version)) {
|
||||
await updateSupported(version, supported);
|
||||
}
|
||||
|
||||
// update all version-related files
|
||||
await Promise.all([
|
||||
updateVersion(version),
|
||||
updatePackageJSON(version),
|
||||
updateWinRC(components)
|
||||
]);
|
||||
|
||||
// commit all updated version-related files
|
||||
await commitVersionBump(version);
|
||||
|
||||
console.log(`Bumped to version: ${version}`);
|
||||
}
|
||||
|
||||
@@ -118,10 +97,6 @@ async function nextVersion (bumpType, version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
function shouldUpdateSupported (bump, current, version) {
|
||||
return isMajorStable(bump, current) || isMajorNightly(version, current);
|
||||
}
|
||||
|
||||
function isMajorStable (bump, currentVersion) {
|
||||
if (versionUtils.isBeta(currentVersion) && (bump === 'stable')) return true;
|
||||
return false;
|
||||
@@ -134,59 +109,6 @@ function isMajorNightly (version, currentVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// update VERSION file with latest release info
|
||||
async function updateVersion (version) {
|
||||
const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION');
|
||||
await writeFile(versionPath, version, 'utf8');
|
||||
}
|
||||
|
||||
// update package metadata files with new version
|
||||
async function updatePackageJSON (version) {
|
||||
const filePath = path.resolve(ELECTRON_DIR, 'package.json');
|
||||
const file = require(filePath);
|
||||
file.version = version;
|
||||
await writeFile(filePath, JSON.stringify(file, null, 2));
|
||||
}
|
||||
|
||||
// push bump commit to release branch
|
||||
async function commitVersionBump (version) {
|
||||
const gitArgs = ['commit', '-a', '-m', `Bump v${version}`, '-n'];
|
||||
await GitProcess.exec(gitArgs, ELECTRON_DIR);
|
||||
}
|
||||
|
||||
// updates electron.rc file with new semver values
|
||||
async function updateWinRC (components) {
|
||||
const filePath = path.resolve(ELECTRON_DIR, 'shell', 'browser', 'resources', 'win', 'electron.rc');
|
||||
const data = await readFile(filePath, 'utf8');
|
||||
const arr = data.split('\n');
|
||||
arr.forEach((line, idx) => {
|
||||
if (line.includes('FILEVERSION')) {
|
||||
arr[idx] = ` FILEVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`;
|
||||
arr[idx + 1] = ` PRODUCTVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`;
|
||||
} else if (line.includes('FileVersion')) {
|
||||
arr[idx] = ` VALUE "FileVersion", "${versionUtils.makeVersion(components, '.')}"`;
|
||||
arr[idx + 5] = ` VALUE "ProductVersion", "${versionUtils.makeVersion(components, '.')}"`;
|
||||
}
|
||||
});
|
||||
await writeFile(filePath, arr.join('\n'));
|
||||
}
|
||||
|
||||
// updates support.md file with new semver values (stable only)
|
||||
async function updateSupported (version, filePath) {
|
||||
const v = parseInt(version);
|
||||
const newVersions = [`* ${v}.x.y`, `* ${v - 1}.x.y`, `* ${v - 2}.x.y`, `* ${v - 3}.x.y`];
|
||||
const contents = await readFile(filePath, 'utf8');
|
||||
const previousVersions = contents.split('\n').filter((elem) => {
|
||||
return (/[^\n]*\.x\.y[^\n]*/).test(elem);
|
||||
}, []);
|
||||
|
||||
const newContents = previousVersions.reduce((contents, current, i) => {
|
||||
return contents.replace(current, newVersions[i]);
|
||||
}, contents);
|
||||
|
||||
await writeFile(filePath, newContents, 'utf8');
|
||||
}
|
||||
|
||||
if (process.mainModule === module) {
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
@@ -194,4 +116,4 @@ if (process.mainModule === module) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { nextVersion, shouldUpdateSupported, updateSupported };
|
||||
module.exports = { nextVersion };
|
||||
|
||||
@@ -68,12 +68,6 @@ async function nextBeta (v) {
|
||||
return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease');
|
||||
}
|
||||
|
||||
async function getElectronVersion () {
|
||||
const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION');
|
||||
const version = await readFile(versionPath, 'utf8');
|
||||
return version.trim();
|
||||
}
|
||||
|
||||
async function nextNightly (v) {
|
||||
let next = semver.valid(semver.coerce(v));
|
||||
const pre = `nightly.${getCurrentDate()}`;
|
||||
@@ -114,7 +108,6 @@ module.exports = {
|
||||
nextAlpha,
|
||||
nextBeta,
|
||||
makeVersion,
|
||||
getElectronVersion,
|
||||
nextNightly,
|
||||
preType
|
||||
};
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
{
|
||||
"bullseye_amd64": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "f515568ba23311d4ae5cb40354d012d4e52f756f",
|
||||
"Sha1Sum": "f631cf53b56adcf0d6231c9dfe72affb25516e85",
|
||||
"SysrootDir": "debian_bullseye_amd64-sysroot",
|
||||
"Tarball": "debian_bullseye_amd64_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_arm": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "19d5f186b437c9445feb69aee1bdd74635373c83",
|
||||
"Sha1Sum": "0820cf8eadc2a2e396bacce35c27a7024ace62b1",
|
||||
"SysrootDir": "debian_bullseye_arm-sysroot",
|
||||
"Tarball": "debian_bullseye_arm_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_arm64": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "ff12c709d8ac2687651479b6bd616893e21457f1",
|
||||
"Sha1Sum": "315c3a474cfcd020bc80fc8fb12bac828b542789",
|
||||
"SysrootDir": "debian_bullseye_arm64-sysroot",
|
||||
"Tarball": "debian_bullseye_arm64_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_armel": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "bd4c83f4f6912ddf95d389c339e2a6700e4d7daa",
|
||||
"Sha1Sum": "55a8d4d9edafdbf9dd0d458665c3c287dd3559dc",
|
||||
"SysrootDir": "debian_bullseye_armel-sysroot",
|
||||
"Tarball": "debian_bullseye_armel_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_i386": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "154f0bcaa42e0eafbaa50d6963dc48ea5f239d3c",
|
||||
"Sha1Sum": "e26ddcc685b44db49c6fa5ed700961c539cf7e40",
|
||||
"SysrootDir": "debian_bullseye_i386-sysroot",
|
||||
"Tarball": "debian_bullseye_i386_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_mips": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "dff1c425913276099a08e57878297f4b21b0f9fc",
|
||||
"Sha1Sum": "fc007fd293519d5c31d1dbf9961f32559603bde7",
|
||||
"SysrootDir": "debian_bullseye_mips-sysroot",
|
||||
"Tarball": "debian_bullseye_mips_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_mips64el": {
|
||||
"Key": "20220331T153654Z-0",
|
||||
"Sha1Sum": "09f5f77d6a2ed6843ffbc66c7eafcaa417964d8a",
|
||||
"Sha1Sum": "d73dcddfc3cc7b0c5aa1a24ccda54809ba13983b",
|
||||
"SysrootDir": "debian_bullseye_mips64el-sysroot",
|
||||
"Tarball": "debian_bullseye_mips64el_sysroot.tar.xz"
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "shell/app/command_line_args.h"
|
||||
#include "shell/browser/api/electron_api_menu.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/gpuinfo_manager.h"
|
||||
#include "shell/browser/browser_process_impl.h"
|
||||
@@ -66,6 +67,7 @@
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
#include "shell/common/language_util.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
@@ -922,6 +924,12 @@ void App::BrowserChildProcessCrashedOrKilled(
|
||||
if (!data.name.empty()) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1796,6 +1804,7 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||
.SetMethod("setAppLogsPath", &App::SetAppLogsPath)
|
||||
.SetMethod("setDesktopName", &App::SetDesktopName)
|
||||
.SetMethod("getLocale", &App::GetLocale)
|
||||
.SetMethod("getPreferredSystemLanguages", &GetPreferredLanguages)
|
||||
.SetMethod("getSystemLocale", &App::GetSystemLocale)
|
||||
.SetMethod("getLocaleCountryCode", &App::GetLocaleCountryCode)
|
||||
#if BUILDFLAG(USE_NSS_CERTS)
|
||||
|
||||
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_
|
||||
@@ -172,6 +172,7 @@
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
#include "chrome/browser/printing/print_view_manager_base.h"
|
||||
#include "components/printing/browser/print_manager_utils.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
|
||||
#include "printing/backend/print_backend.h" // nogncheck
|
||||
#include "printing/mojom/print.mojom.h" // nogncheck
|
||||
@@ -2900,12 +2901,12 @@ v8::Local<v8::Promise> WebContents::PrintToPDF(const base::Value& settings) {
|
||||
|
||||
void WebContents::OnPDFCreated(
|
||||
gin_helper::Promise<v8::Local<v8::Value>> promise,
|
||||
PrintViewManagerElectron::PrintResult print_result,
|
||||
print_to_pdf::PdfPrintResult print_result,
|
||||
scoped_refptr<base::RefCountedMemory> data) {
|
||||
if (print_result != PrintViewManagerElectron::PrintResult::kPrintSuccess) {
|
||||
if (print_result != print_to_pdf::PdfPrintResult::kPrintSuccess) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Failed to generate PDF: " +
|
||||
PrintViewManagerElectron::PrintResultToString(print_result));
|
||||
print_to_pdf::PdfPrintResultToString(print_result));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "ui/gfx/image/image.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
|
||||
#include "shell/browser/printing/print_view_manager_electron.h"
|
||||
#endif
|
||||
|
||||
@@ -227,7 +228,7 @@ class WebContents : public ExclusiveAccessContext,
|
||||
// Print current page as PDF.
|
||||
v8::Local<v8::Promise> PrintToPDF(const base::Value& settings);
|
||||
void OnPDFCreated(gin_helper::Promise<v8::Local<v8::Value>> promise,
|
||||
PrintViewManagerElectron::PrintResult print_result,
|
||||
print_to_pdf::PdfPrintResult print_result,
|
||||
scoped_refptr<base::RefCountedMemory> data);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -447,6 +447,13 @@ void Browser::DockSetIcon(v8::Isolate* isolate, v8::Local<v8::Value> icon) {
|
||||
image = native_image->image();
|
||||
}
|
||||
|
||||
// This is needed when this fn is called before the browser
|
||||
// process is ready, since supported scales are normally set
|
||||
// by ui::ResourceBundle::InitSharedInstance
|
||||
// during browser process startup.
|
||||
if (!is_ready())
|
||||
gfx::ImageSkia::SetSupportedScales({1.0f});
|
||||
|
||||
[[AtomApplication sharedApplication]
|
||||
setApplicationIconImage:image.AsNSImage()];
|
||||
}
|
||||
|
||||
@@ -25,13 +25,17 @@
|
||||
#include "components/os_crypt/key_storage_config_linux.h"
|
||||
#include "components/os_crypt/os_crypt.h"
|
||||
#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/child_process_data.h"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/device_service.h"
|
||||
#include "content/public/browser/first_party_sets_handler.h"
|
||||
#include "content/public/browser/web_ui_controller_factory.h"
|
||||
#include "content/public/common/content_features.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/process_type.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
#include "electron/fuses.h"
|
||||
@@ -40,6 +44,7 @@
|
||||
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
|
||||
#include "shell/app/electron_main_delegate.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_process_impl.h"
|
||||
#include "shell/browser/electron_browser_client.h"
|
||||
@@ -279,12 +284,15 @@ void ElectronBrowserMainParts::PostEarlyInitialization() {
|
||||
// Add Electron extended APIs.
|
||||
electron_bindings_->BindTo(js_env_->isolate(), env->process_object());
|
||||
|
||||
// Load everything.
|
||||
node_bindings_->LoadEnvironment(env);
|
||||
// Create explicit microtasks runner.
|
||||
js_env_->CreateMicrotasksRunner();
|
||||
|
||||
// Wrap the uv loop with global env.
|
||||
node_bindings_->set_uv_env(env);
|
||||
|
||||
// Load everything.
|
||||
node_bindings_->LoadEnvironment(env);
|
||||
|
||||
// We already initialized the feature list in PreEarlyInitialization(), but
|
||||
// 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
|
||||
@@ -521,7 +529,6 @@ int ElectronBrowserMainParts::PreMainMessageLoopRun() {
|
||||
|
||||
void ElectronBrowserMainParts::WillRunMainMessageLoop(
|
||||
std::unique_ptr<base::RunLoop>& run_loop) {
|
||||
js_env_->OnMessageLoopCreated();
|
||||
exit_code_ = content::RESULT_CODE_NORMAL_EXIT;
|
||||
Browser::Get()->SetMainMessageLoopQuitClosure(
|
||||
run_loop->QuitWhenIdleClosure());
|
||||
@@ -583,10 +590,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
|
||||
// invoke Node/V8 APIs inside them.
|
||||
node_env_->env()->set_trace_sync_io(false);
|
||||
js_env_->OnMessageLoopDestroying();
|
||||
js_env_->DestroyMicrotasksRunner();
|
||||
node::Stop(node_env_->env());
|
||||
node_env_.reset();
|
||||
|
||||
|
||||
@@ -38,6 +38,17 @@ class EventEmitterMixin {
|
||||
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...);
|
||||
template <typename... Args>
|
||||
bool EmitCustomEvent(base::StringPiece name,
|
||||
|
||||
@@ -287,13 +287,13 @@ v8::Isolate* JavascriptEnvironment::GetIsolate() {
|
||||
return g_isolate;
|
||||
}
|
||||
|
||||
void JavascriptEnvironment::OnMessageLoopCreated() {
|
||||
void JavascriptEnvironment::CreateMicrotasksRunner() {
|
||||
DCHECK(!microtasks_runner_);
|
||||
microtasks_runner_ = std::make_unique<MicrotasksRunner>(isolate());
|
||||
base::CurrentThread::Get()->AddTaskObserver(microtasks_runner_.get());
|
||||
}
|
||||
|
||||
void JavascriptEnvironment::OnMessageLoopDestroying() {
|
||||
void JavascriptEnvironment::DestroyMicrotasksRunner() {
|
||||
DCHECK(microtasks_runner_);
|
||||
{
|
||||
v8::HandleScope scope(isolate_);
|
||||
|
||||
@@ -29,8 +29,8 @@ class JavascriptEnvironment {
|
||||
JavascriptEnvironment(const JavascriptEnvironment&) = delete;
|
||||
JavascriptEnvironment& operator=(const JavascriptEnvironment&) = delete;
|
||||
|
||||
void OnMessageLoopCreated();
|
||||
void OnMessageLoopDestroying();
|
||||
void CreateMicrotasksRunner();
|
||||
void DestroyMicrotasksRunner();
|
||||
|
||||
node::MultiIsolatePlatform* platform() const { return platform_; }
|
||||
v8::Isolate* isolate() const { return isolate_; }
|
||||
|
||||
@@ -60,124 +60,36 @@ void PrintViewManagerElectron::BindPrintManagerHost(
|
||||
print_manager->BindReceiver(std::move(receiver), rfh);
|
||||
}
|
||||
|
||||
// static
|
||||
std::string PrintViewManagerElectron::PrintResultToString(PrintResult result) {
|
||||
switch (result) {
|
||||
case kPrintSuccess:
|
||||
return std::string(); // no error message
|
||||
case kPrintFailure:
|
||||
return "Printing failed";
|
||||
case kInvalidPrinterSettings:
|
||||
return "Show invalid printer settings error";
|
||||
case kInvalidMemoryHandle:
|
||||
return "Invalid memory handle";
|
||||
case kMetafileMapError:
|
||||
return "Map to shared memory error";
|
||||
case kMetafileInvalidHeader:
|
||||
return "Invalid metafile header";
|
||||
case kMetafileGetDataError:
|
||||
return "Get data from metafile error";
|
||||
case kSimultaneousPrintActive:
|
||||
return "The previous printing job hasn't finished";
|
||||
case kPageRangeSyntaxError:
|
||||
return "Page range syntax error";
|
||||
case kPageRangeInvalidRange:
|
||||
return "Page range is invalid (start > end)";
|
||||
case kPageCountExceeded:
|
||||
return "Page range exceeds page count";
|
||||
case kPrintingInProgress:
|
||||
return "Page is already being printed";
|
||||
default:
|
||||
NOTREACHED();
|
||||
return "Unknown PrintResult";
|
||||
}
|
||||
void PrintViewManagerElectron::DidPrintToPdf(
|
||||
int cookie,
|
||||
PrintToPdfCallback callback,
|
||||
print_to_pdf::PdfPrintResult result,
|
||||
scoped_refptr<base::RefCountedMemory> memory) {
|
||||
base::Erase(pdf_jobs_, cookie);
|
||||
std::move(callback).Run(result, memory);
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::PrintToPdf(
|
||||
content::RenderFrameHost* rfh,
|
||||
const std::string& page_ranges,
|
||||
printing::mojom::PrintPagesParamsPtr print_pages_params,
|
||||
PrintToPDFCallback callback) {
|
||||
DCHECK(callback);
|
||||
PrintToPdfCallback callback) {
|
||||
// Store cookie in order to track job uniqueness and differentiate
|
||||
// between regular and headless print jobs.
|
||||
int cookie = print_pages_params->params->document_cookie;
|
||||
pdf_jobs_.emplace_back(cookie);
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback).Run(kSimultaneousPrintActive,
|
||||
base::MakeRefCounted<base::RefCountedString>());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rfh->IsRenderFrameLive()) {
|
||||
std::move(callback).Run(kPrintFailure,
|
||||
base::MakeRefCounted<base::RefCountedString>());
|
||||
return;
|
||||
}
|
||||
|
||||
absl::variant<printing::PageRanges, print_to_pdf::PdfPrintResult>
|
||||
parsed_ranges = print_to_pdf::TextPageRangesToPageRanges(page_ranges);
|
||||
if (absl::holds_alternative<print_to_pdf::PdfPrintResult>(parsed_ranges)) {
|
||||
DCHECK_NE(absl::get<print_to_pdf::PdfPrintResult>(parsed_ranges),
|
||||
print_to_pdf::PdfPrintResult::kPrintSuccess);
|
||||
std::move(callback).Run(
|
||||
static_cast<PrintResult>(
|
||||
absl::get<print_to_pdf::PdfPrintResult>(parsed_ranges)),
|
||||
base::MakeRefCounted<base::RefCountedString>());
|
||||
return;
|
||||
}
|
||||
|
||||
printing_rfh_ = rfh;
|
||||
print_pages_params->pages = absl::get<printing::PageRanges>(parsed_ranges);
|
||||
headless_jobs_.emplace_back(print_pages_params->params->document_cookie);
|
||||
callback_ = std::move(callback);
|
||||
|
||||
// There is no need for a weak pointer here since the mojo proxy is held
|
||||
// in the base class. If we're gone, mojo will discard the callback.
|
||||
GetPrintRenderFrame(rfh)->PrintWithParams(
|
||||
print_to_pdf::PdfPrintJob::StartJob(
|
||||
web_contents(), rfh, GetPrintRenderFrame(rfh), page_ranges,
|
||||
std::move(print_pages_params),
|
||||
base::BindOnce(&PrintViewManagerElectron::OnDidPrintWithParams,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::OnDidPrintWithParams(
|
||||
printing::mojom::PrintWithParamsResultPtr result) {
|
||||
if (result->is_failure_reason()) {
|
||||
switch (result->get_failure_reason()) {
|
||||
case printing::mojom::PrintFailureReason::kGeneralFailure:
|
||||
FailJob(kPrintFailure);
|
||||
return;
|
||||
case printing::mojom::PrintFailureReason::kInvalidPageRange:
|
||||
FailJob(kPageCountExceeded);
|
||||
return;
|
||||
case printing::mojom::PrintFailureReason::kPrintingInProgress:
|
||||
FailJob(kPrintingInProgress);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printing::mojom::DidPrintDocumentParamsPtr& params = result->get_params();
|
||||
|
||||
auto& content = *params->content;
|
||||
if (!content.metafile_data_region.IsValid()) {
|
||||
FailJob(kInvalidMemoryHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
base::ReadOnlySharedMemoryMapping map = content.metafile_data_region.Map();
|
||||
if (!map.IsValid()) {
|
||||
FailJob(kMetafileMapError);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string data =
|
||||
std::string(static_cast<const char*>(map.memory()), map.size());
|
||||
std::move(callback_).Run(kPrintSuccess,
|
||||
base::RefCountedString::TakeString(&data));
|
||||
base::Erase(headless_jobs_, params->document_cookie);
|
||||
Reset();
|
||||
base::BindOnce(&PrintViewManagerElectron::DidPrintToPdf,
|
||||
weak_factory_.GetWeakPtr(), cookie, std::move(callback)));
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::GetDefaultPrintSettings(
|
||||
GetDefaultPrintSettingsCallback callback) {
|
||||
if (printing_rfh_) {
|
||||
// This isn't ideal, but we're not able to access the document cookie here.
|
||||
if (pdf_jobs_.size() > 0) {
|
||||
LOG(ERROR) << "Scripted print is not supported";
|
||||
std::move(callback).Run(printing::mojom::PrintParams::New());
|
||||
} else {
|
||||
@@ -188,9 +100,8 @@ void PrintViewManagerElectron::GetDefaultPrintSettings(
|
||||
void PrintViewManagerElectron::ScriptedPrint(
|
||||
printing::mojom::ScriptedPrintParamsPtr params,
|
||||
ScriptedPrintCallback callback) {
|
||||
auto entry =
|
||||
std::find(headless_jobs_.begin(), headless_jobs_.end(), params->cookie);
|
||||
if (entry == headless_jobs_.end()) {
|
||||
auto entry = std::find(pdf_jobs_.begin(), pdf_jobs_.end(), params->cookie);
|
||||
if (entry == pdf_jobs_.end()) {
|
||||
PrintViewManagerBase::ScriptedPrint(std::move(params), std::move(callback));
|
||||
return;
|
||||
}
|
||||
@@ -201,22 +112,13 @@ void PrintViewManagerElectron::ScriptedPrint(
|
||||
std::move(callback).Run(std::move(default_param), /*cancelled*/ false);
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::ShowInvalidPrinterSettingsError() {
|
||||
if (headless_jobs_.size() == 0) {
|
||||
PrintViewManagerBase::ShowInvalidPrinterSettingsError();
|
||||
return;
|
||||
}
|
||||
|
||||
FailJob(kInvalidPrinterSettings);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
void PrintViewManagerElectron::UpdatePrintSettings(
|
||||
int32_t cookie,
|
||||
base::Value::Dict job_settings,
|
||||
UpdatePrintSettingsCallback callback) {
|
||||
auto entry = std::find(headless_jobs_.begin(), headless_jobs_.end(), cookie);
|
||||
if (entry == headless_jobs_.end()) {
|
||||
auto entry = std::find(pdf_jobs_.begin(), pdf_jobs_.end(), cookie);
|
||||
if (entry == pdf_jobs_.end()) {
|
||||
PrintViewManagerBase::UpdatePrintSettings(cookie, std::move(job_settings),
|
||||
std::move(callback));
|
||||
return;
|
||||
@@ -247,40 +149,14 @@ void PrintViewManagerElectron::CheckForCancel(int32_t preview_ui_id,
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
|
||||
void PrintViewManagerElectron::RenderFrameDeleted(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
PrintViewManagerBase::RenderFrameDeleted(render_frame_host);
|
||||
|
||||
if (printing_rfh_ != render_frame_host)
|
||||
return;
|
||||
|
||||
FailJob(kPrintFailure);
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::DidGetPrintedPagesCount(int32_t cookie,
|
||||
uint32_t number_pages) {
|
||||
auto entry = std::find(headless_jobs_.begin(), headless_jobs_.end(), cookie);
|
||||
if (entry == headless_jobs_.end()) {
|
||||
auto entry = std::find(pdf_jobs_.begin(), pdf_jobs_.end(), cookie);
|
||||
if (entry == pdf_jobs_.end()) {
|
||||
PrintViewManagerBase::DidGetPrintedPagesCount(cookie, number_pages);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::Reset() {
|
||||
printing_rfh_ = nullptr;
|
||||
callback_.Reset();
|
||||
data_.clear();
|
||||
}
|
||||
|
||||
void PrintViewManagerElectron::FailJob(PrintResult result) {
|
||||
DCHECK_NE(result, kPrintSuccess);
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(result,
|
||||
base::MakeRefCounted<base::RefCountedString>());
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(PrintViewManagerElectron);
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/printing/print_view_manager_base.h"
|
||||
#include "components/printing/browser/print_to_pdf/pdf_print_job.h"
|
||||
#include "components/printing/common/print.mojom.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
@@ -21,29 +22,12 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
using PrintToPdfCallback = print_to_pdf::PdfPrintJob::PrintToPdfCallback;
|
||||
|
||||
class PrintViewManagerElectron
|
||||
: public printing::PrintViewManagerBase,
|
||||
public content::WebContentsUserData<PrintViewManagerElectron> {
|
||||
public:
|
||||
enum PrintResult {
|
||||
kPrintSuccess,
|
||||
kPrintFailure,
|
||||
kInvalidPrinterSettings,
|
||||
kInvalidMemoryHandle,
|
||||
kMetafileMapError,
|
||||
kMetafileInvalidHeader,
|
||||
kMetafileGetDataError,
|
||||
kSimultaneousPrintActive,
|
||||
kPageRangeSyntaxError,
|
||||
kPageRangeInvalidRange,
|
||||
kPageCountExceeded,
|
||||
kPrintingInProgress
|
||||
};
|
||||
|
||||
using PrintToPDFCallback =
|
||||
base::OnceCallback<void(PrintResult,
|
||||
scoped_refptr<base::RefCountedMemory>)>;
|
||||
|
||||
~PrintViewManagerElectron() override;
|
||||
|
||||
PrintViewManagerElectron(const PrintViewManagerElectron&) = delete;
|
||||
@@ -54,30 +38,26 @@ class PrintViewManagerElectron
|
||||
receiver,
|
||||
content::RenderFrameHost* rfh);
|
||||
|
||||
static std::string PrintResultToString(PrintResult result);
|
||||
|
||||
void DidPrintToPdf(int cookie,
|
||||
PrintToPdfCallback callback,
|
||||
print_to_pdf::PdfPrintResult result,
|
||||
scoped_refptr<base::RefCountedMemory> memory);
|
||||
void PrintToPdf(content::RenderFrameHost* rfh,
|
||||
const std::string& page_ranges,
|
||||
printing::mojom::PrintPagesParamsPtr print_page_params,
|
||||
PrintToPDFCallback callback);
|
||||
PrintToPdfCallback callback);
|
||||
|
||||
private:
|
||||
friend class content::WebContentsUserData<PrintViewManagerElectron>;
|
||||
|
||||
explicit PrintViewManagerElectron(content::WebContents* web_contents);
|
||||
|
||||
void OnDidPrintWithParams(printing::mojom::PrintWithParamsResultPtr result);
|
||||
|
||||
// WebContentsObserver overrides (via PrintManager):
|
||||
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
|
||||
|
||||
// printing::mojom::PrintManagerHost:
|
||||
void DidGetPrintedPagesCount(int32_t cookie, uint32_t number_pages) override;
|
||||
void GetDefaultPrintSettings(
|
||||
GetDefaultPrintSettingsCallback callback) override;
|
||||
void ScriptedPrint(printing::mojom::ScriptedPrintParamsPtr params,
|
||||
ScriptedPrintCallback callback) override;
|
||||
void ShowInvalidPrinterSettingsError() override;
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
void UpdatePrintSettings(int32_t cookie,
|
||||
base::Value::Dict job_settings,
|
||||
@@ -91,14 +71,9 @@ class PrintViewManagerElectron
|
||||
int32_t request_id,
|
||||
CheckForCancelCallback callback) override;
|
||||
#endif
|
||||
std::vector<int32_t> pdf_jobs_;
|
||||
|
||||
void FailJob(PrintResult result);
|
||||
void Reset();
|
||||
|
||||
raw_ptr<content::RenderFrameHost> printing_rfh_ = nullptr;
|
||||
PrintToPDFCallback callback_;
|
||||
std::string data_;
|
||||
std::vector<int32_t> headless_jobs_;
|
||||
base::WeakPtrFactory<PrintViewManagerElectron> weak_factory_{this};
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_DECL();
|
||||
};
|
||||
|
||||
@@ -78,6 +78,14 @@ void ElectronDesktopWindowTreeHostLinux::OnWindowStateChanged(
|
||||
UpdateWindowState(new_state);
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged(
|
||||
ui::WindowTiledEdges new_tiled_edges) {
|
||||
static_cast<ClientFrameViewLinux*>(
|
||||
native_window_view_->widget()->non_client_view()->frame_view())
|
||||
->set_tiled_edges(new_tiled_edges);
|
||||
UpdateFrameHints();
|
||||
}
|
||||
|
||||
void ElectronDesktopWindowTreeHostLinux::UpdateWindowState(
|
||||
ui::PlatformWindowState new_state) {
|
||||
if (window_state_ == new_state)
|
||||
@@ -159,7 +167,15 @@ void ElectronDesktopWindowTreeHostLinux::UpdateClientDecorationHints(
|
||||
|
||||
input_insets = view->GetInputInsets();
|
||||
}
|
||||
|
||||
const auto tiled_edges = view->tiled_edges();
|
||||
if (tiled_edges.left)
|
||||
insets.set_left(0);
|
||||
if (tiled_edges.right)
|
||||
insets.set_right(0);
|
||||
if (tiled_edges.top)
|
||||
insets.set_top(0);
|
||||
if (tiled_edges.bottom)
|
||||
insets.set_bottom(0);
|
||||
gfx::Insets scaled_insets = gfx::ScaleToCeiledInsets(insets, scale);
|
||||
window->SetDecorationInsets(&scaled_insets);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class ElectronDesktopWindowTreeHostLinux
|
||||
void OnBoundsChanged(const BoundsChange& change) override;
|
||||
void OnWindowStateChanged(ui::PlatformWindowState old_state,
|
||||
ui::PlatformWindowState new_state) override;
|
||||
void OnWindowTiledStateChanged(ui::WindowTiledEdges new_tiled_edges) override;
|
||||
|
||||
// ui::NativeThemeObserver:
|
||||
void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
|
||||
|
||||
@@ -295,7 +295,7 @@ void ClientFrameViewLinux::OnPaint(gfx::Canvas* canvas) {
|
||||
if (!frame_->IsFullscreen()) {
|
||||
frame_provider_->PaintWindowFrame(canvas, GetLocalBounds(),
|
||||
GetTitlebarBounds().bottom(),
|
||||
ShouldPaintAsActive());
|
||||
ShouldPaintAsActive(), tiled_edges());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "base/scoped_observation.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "ui/base/ui_base_types.h"
|
||||
#include "ui/linux/linux_ui.h"
|
||||
#include "ui/linux/nav_button_provider.h"
|
||||
#include "ui/linux/window_button_order_observer.h"
|
||||
@@ -39,6 +40,11 @@ class ClientFrameViewLinux : public FramelessView,
|
||||
gfx::Insets GetInputInsets() const;
|
||||
gfx::Rect GetWindowContentBounds() const;
|
||||
SkRRect GetRoundedWindowContentBounds() const;
|
||||
// Returns which edges of the frame are tiled.
|
||||
const ui::WindowTiledEdges& tiled_edges() const { return tiled_edges_; }
|
||||
void set_tiled_edges(ui::WindowTiledEdges tiled_edges) {
|
||||
tiled_edges_ = tiled_edges;
|
||||
}
|
||||
|
||||
protected:
|
||||
// ui::NativeThemeObserver:
|
||||
@@ -137,6 +143,8 @@ class ClientFrameViewLinux : public FramelessView,
|
||||
window_button_order_observer_{this};
|
||||
|
||||
base::CallbackListSubscription paint_as_active_changed_subscription_;
|
||||
|
||||
ui::WindowTiledEdges tiled_edges_;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -4,14 +4,31 @@
|
||||
|
||||
#include "shell/common/language_util.h"
|
||||
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include <glib.h>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/i18n/rtl.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
std::vector<std::string> GetPreferredLanguages() {
|
||||
// Return empty as there's no API to use. You may be able to use
|
||||
// GetApplicationLocale() of a browser process.
|
||||
return std::vector<std::string>{};
|
||||
std::vector<std::string> preferredLanguages;
|
||||
|
||||
// Based on
|
||||
// https://source.chromium.org/chromium/chromium/src/+/refs/tags/108.0.5329.0:ui/base/l10n/l10n_util.cc;l=543-554
|
||||
// GLib implements correct environment variable parsing with
|
||||
// the precedence order: LANGUAGE, LC_ALL, LC_MESSAGES and LANG.
|
||||
const char* const* languages = g_get_language_names();
|
||||
DCHECK(languages); // A valid pointer is guaranteed.
|
||||
DCHECK(*languages); // At least one entry, "C", is guaranteed.
|
||||
|
||||
for (; *languages; ++languages) {
|
||||
if (strcmp(*languages, "C") != 0) {
|
||||
preferredLanguages.push_back(base::i18n::GetCanonicalLocale(*languages));
|
||||
}
|
||||
}
|
||||
|
||||
return preferredLanguages;
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
V(electron_browser_system_preferences) \
|
||||
V(electron_browser_base_window) \
|
||||
V(electron_browser_tray) \
|
||||
V(electron_browser_utility_process) \
|
||||
V(electron_browser_view) \
|
||||
V(electron_browser_web_contents) \
|
||||
V(electron_browser_web_contents_view) \
|
||||
@@ -87,7 +88,8 @@
|
||||
V(electron_renderer_context_bridge) \
|
||||
V(electron_renderer_crash_reporter) \
|
||||
V(electron_renderer_ipc) \
|
||||
V(electron_renderer_web_frame)
|
||||
V(electron_renderer_web_frame) \
|
||||
V(electron_utility_parent_port)
|
||||
|
||||
#define ELECTRON_VIEWS_MODULES(V) V(electron_browser_image_view)
|
||||
|
||||
@@ -390,7 +392,11 @@ void NodeBindings::Initialize() {
|
||||
std::vector<std::string> argv = {"electron"};
|
||||
std::vector<std::string> exec_argv;
|
||||
std::vector<std::string> errors;
|
||||
uint64_t process_flags = node::ProcessFlags::kEnableStdioInheritance;
|
||||
uint64_t process_flags = node::ProcessFlags::kNoFlags;
|
||||
// We do not want the child processes spawned from the utility process
|
||||
// to inherit the custom stdio handles created for the parent.
|
||||
if (browser_env_ != BrowserEnvironment::kUtility)
|
||||
process_flags |= node::ProcessFlags::kEnableStdioInheritance;
|
||||
if (!fuses::IsNodeOptionsEnabled())
|
||||
process_flags |= node::ProcessFlags::kDisableNodeOptionsEnv;
|
||||
|
||||
@@ -417,16 +423,9 @@ void NodeBindings::Initialize() {
|
||||
|
||||
node::Environment* NodeBindings::CreateEnvironment(
|
||||
v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
auto& atom_args = ElectronCommandLine::argv();
|
||||
std::vector<std::string> args(atom_args.size());
|
||||
std::transform(atom_args.cbegin(), atom_args.cend(), args.begin(),
|
||||
[](auto& a) { return base::WideToUTF8(a); });
|
||||
#else
|
||||
auto args = ElectronCommandLine::argv();
|
||||
#endif
|
||||
|
||||
node::MultiIsolatePlatform* platform,
|
||||
std::vector<std::string> args,
|
||||
std::vector<std::string> exec_args) {
|
||||
// Feed node the path to initialization script.
|
||||
std::string process_type;
|
||||
switch (browser_env_) {
|
||||
@@ -439,14 +438,20 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
case BrowserEnvironment::kWorker:
|
||||
process_type = "worker";
|
||||
break;
|
||||
case BrowserEnvironment::kUtility:
|
||||
process_type = "utility";
|
||||
break;
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
gin_helper::Dictionary global(isolate, context->Global());
|
||||
// Do not set DOM globals for renderer process.
|
||||
// We must set this before the node bootstrapper which is run inside
|
||||
// CreateEnvironment
|
||||
if (browser_env_ != BrowserEnvironment::kBrowser)
|
||||
// Avoids overriding globals like setImmediate, clearImmediate
|
||||
// queueMicrotask etc during the bootstrap phase of Node.js
|
||||
// for processes that already have these defined by DOM.
|
||||
// Check //third_party/electron_node/lib/internal/bootstrap/node.js
|
||||
// for the list of overrides on globalThis.
|
||||
if (browser_env_ == BrowserEnvironment::kRenderer ||
|
||||
browser_env_ == BrowserEnvironment::kWorker)
|
||||
global.Set("_noBrowserGlobals", true);
|
||||
|
||||
if (browser_env_ == BrowserEnvironment::kBrowser) {
|
||||
@@ -464,7 +469,6 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
: search_paths));
|
||||
}
|
||||
|
||||
std::vector<std::string> exec_args;
|
||||
base::FilePath resources_path = GetResourcesPath();
|
||||
std::string init_script = "electron/js2c/" + process_type + "_init";
|
||||
|
||||
@@ -478,7 +482,8 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
node::EnvironmentFlags::kHideConsoleWindows |
|
||||
node::EnvironmentFlags::kNoGlobalSearchPaths;
|
||||
|
||||
if (browser_env_ != BrowserEnvironment::kBrowser) {
|
||||
if (browser_env_ == BrowserEnvironment::kRenderer ||
|
||||
browser_env_ == BrowserEnvironment::kWorker) {
|
||||
// Only one ESM loader can be registered per isolate -
|
||||
// in renderer processes this should be blink. We need to tell Node.js
|
||||
// not to register its handler (overriding blinks) in non-browser processes.
|
||||
@@ -514,7 +519,8 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
|
||||
// Clean up the global _noBrowserGlobals that we unironically injected into
|
||||
// the global scope
|
||||
if (browser_env_ != BrowserEnvironment::kBrowser) {
|
||||
if (browser_env_ == BrowserEnvironment::kRenderer ||
|
||||
browser_env_ == BrowserEnvironment::kWorker) {
|
||||
// We need to bootstrap the env in non-browser processes so that
|
||||
// _noBrowserGlobals is read correctly before we remove it
|
||||
global.Delete("_noBrowserGlobals");
|
||||
@@ -528,15 +534,21 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
|
||||
// We don't want to abort either in the renderer or browser processes.
|
||||
// We already listen for uncaught exceptions and handle them there.
|
||||
is.should_abort_on_uncaught_exception_callback = [](v8::Isolate*) {
|
||||
return false;
|
||||
};
|
||||
// For utility process we expect the process to behave as standard
|
||||
// Node.js runtime and abort the process with appropriate exit
|
||||
// code depending on a handler being set for `uncaughtException` event.
|
||||
if (browser_env_ != BrowserEnvironment::kUtility) {
|
||||
is.should_abort_on_uncaught_exception_callback = [](v8::Isolate*) {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// Use a custom callback here to allow us to leverage Blink's logic in the
|
||||
// renderer process.
|
||||
is.allow_wasm_code_generation_callback = AllowWasmCodeGenerationCallback;
|
||||
|
||||
if (browser_env_ == BrowserEnvironment::kBrowser) {
|
||||
if (browser_env_ == BrowserEnvironment::kBrowser ||
|
||||
browser_env_ == BrowserEnvironment::kUtility) {
|
||||
// Node.js requires that microtask checkpoints be explicitly invoked.
|
||||
is.policy = v8::MicrotasksPolicy::kExplicit;
|
||||
} else {
|
||||
@@ -585,6 +597,20 @@ node::Environment* NodeBindings::CreateEnvironment(
|
||||
return env;
|
||||
}
|
||||
|
||||
node::Environment* NodeBindings::CreateEnvironment(
|
||||
v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
auto& electron_args = ElectronCommandLine::argv();
|
||||
std::vector<std::string> args(electron_args.size());
|
||||
std::transform(electron_args.cbegin(), electron_args.cend(), args.begin(),
|
||||
[](auto& a) { return base::WideToUTF8(a); });
|
||||
#else
|
||||
auto args = ElectronCommandLine::argv();
|
||||
#endif
|
||||
return CreateEnvironment(context, platform, args, {});
|
||||
}
|
||||
|
||||
void NodeBindings::LoadEnvironment(node::Environment* env) {
|
||||
node::LoadEnvironment(env, node::StartExecutionCallback{});
|
||||
gin_helper::EmitEvent(env->isolate(), env->process_object(), "loaded");
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#ifndef ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
|
||||
#define ELECTRON_SHELL_COMMON_NODE_BINDINGS_H_
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
@@ -74,7 +76,7 @@ class UvHandle {
|
||||
|
||||
class NodeBindings {
|
||||
public:
|
||||
enum class BrowserEnvironment { kBrowser, kRenderer, kWorker };
|
||||
enum class BrowserEnvironment { kBrowser, kRenderer, kUtility, kWorker };
|
||||
|
||||
static NodeBindings* Create(BrowserEnvironment browser_env);
|
||||
static void RegisterBuiltinModules();
|
||||
@@ -86,6 +88,10 @@ class NodeBindings {
|
||||
void Initialize();
|
||||
|
||||
// Create the environment and load node.js.
|
||||
node::Environment* CreateEnvironment(v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform,
|
||||
std::vector<std::string> args,
|
||||
std::vector<std::string> exec_args);
|
||||
node::Environment* CreateEnvironment(v8::Handle<v8::Context> context,
|
||||
node::MultiIsolatePlatform* platform);
|
||||
|
||||
|
||||
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);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user