mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
4 Commits
roller/nod
...
test-linux
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f204435c7d | ||
|
|
6a2571ee3d | ||
|
|
94aa90bb64 | ||
|
|
b9a09acff3 |
@@ -2,7 +2,7 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
buildtools:
|
buildtools:
|
||||||
image: ghcr.io/electron/devcontainer:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
image: ghcr.io/electron/devcontainer:eac3529546ea8f3aa356d31e345715eef342233b
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- ..:/workspaces/gclient/src/electron:cached
|
- ..:/workspaces/gclient/src/electron:cached
|
||||||
|
|||||||
2
.github/workflows/apply-patches.yml
vendored
2
.github/workflows/apply-patches.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||||
options: --user root
|
options: --user root
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||||
|
|||||||
6
.github/workflows/build-git-cache.yml
vendored
6
.github/workflows/build-git-cache.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||||
options: --user root
|
options: --user root
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||||
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/win-cache:/mnt/win-cache
|
- /mnt/win-cache:/mnt/win-cache
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
# This job updates the same git cache as linux, so it needs to run after the linux one.
|
# This job updates the same git cache as linux, so it needs to run after the linux one.
|
||||||
needs: build-git-cache-linux
|
needs: build-git-cache-linux
|
||||||
container:
|
container:
|
||||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||||
options: --user root
|
options: --user root
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||||
|
|||||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
build-image-sha:
|
build-image-sha:
|
||||||
type: string
|
type: string
|
||||||
description: 'SHA for electron/build image'
|
description: 'SHA for electron/build image'
|
||||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||||
required: true
|
required: true
|
||||||
skip-macos:
|
skip-macos:
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
id: set-output
|
id: set-output
|
||||||
run: |
|
run: |
|
||||||
if [ -z "${{ inputs.build-image-sha }}" ]; then
|
if [ -z "${{ inputs.build-image-sha }}" ]; then
|
||||||
echo "build-image-sha=a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb" >> "$GITHUB_OUTPUT"
|
echo "build-image-sha=eac3529546ea8f3aa356d31e345715eef342233b" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
|
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|||||||
2
.github/workflows/linux-publish.yml
vendored
2
.github/workflows/linux-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
build-image-sha:
|
build-image-sha:
|
||||||
type: string
|
type: string
|
||||||
description: 'SHA for electron/build image'
|
description: 'SHA for electron/build image'
|
||||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||||
upload-to-storage:
|
upload-to-storage:
|
||||||
description: 'Uploads to Azure storage'
|
description: 'Uploads to Azure storage'
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
2
.github/workflows/macos-publish.yml
vendored
2
.github/workflows/macos-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
build-image-sha:
|
build-image-sha:
|
||||||
type: string
|
type: string
|
||||||
description: 'SHA for electron/build image'
|
description: 'SHA for electron/build image'
|
||||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||||
required: true
|
required: true
|
||||||
upload-to-storage:
|
upload-to-storage:
|
||||||
description: 'Uploads to Azure storage'
|
description: 'Uploads to Azure storage'
|
||||||
|
|||||||
@@ -110,6 +110,33 @@ jobs:
|
|||||||
test-runs-on: ${{ inputs.test-runs-on }}
|
test-runs-on: ${{ inputs.test-runs-on }}
|
||||||
test-container: ${{ inputs.test-container }}
|
test-container: ${{ inputs.test-container }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
test-wayland:
|
||||||
|
uses: ./.github/workflows/pipeline-segment-electron-test.yml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
needs: build
|
||||||
|
if: ${{ inputs.target-platform == 'linux' && inputs.target-arch == 'x64' && !inputs.is-asan }}
|
||||||
|
with:
|
||||||
|
target-arch: ${{ inputs.target-arch }}
|
||||||
|
target-platform: ${{ inputs.target-platform }}
|
||||||
|
test-runs-on: ${{ inputs.test-runs-on }}
|
||||||
|
test-container: ${{ inputs.test-container }}
|
||||||
|
display-server: wayland
|
||||||
|
secrets: inherit
|
||||||
|
test-linux-64k:
|
||||||
|
uses: ./.github/workflows/pipeline-segment-electron-test-64k.yml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
needs: build
|
||||||
|
if: ${{ inputs.target-platform == 'linux' && inputs.target-arch == 'arm64' && !inputs.is-asan }}
|
||||||
|
with:
|
||||||
|
test-runs-on: ${{ inputs.test-runs-on }}
|
||||||
|
test-container: ${{ inputs.test-container }}
|
||||||
|
secrets: inherit
|
||||||
nn-test:
|
nn-test:
|
||||||
uses: ./.github/workflows/pipeline-segment-node-nan-test.yml
|
uses: ./.github/workflows/pipeline-segment-node-nan-test.yml
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
64
.github/workflows/pipeline-segment-electron-test-64k.yml
vendored
Normal file
64
.github/workflows/pipeline-segment-electron-test-64k.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: Pipeline Segment - Electron Test on Linux ARM64 64k
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
test-runs-on:
|
||||||
|
type: string
|
||||||
|
description: 'What host to run the tests on'
|
||||||
|
required: true
|
||||||
|
test-container:
|
||||||
|
type: string
|
||||||
|
description: 'JSON container information for aks runs-on'
|
||||||
|
required: false
|
||||||
|
default: '{"image":null}'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: electron-test-linux-64k-${{ github.ref_protected == true && github.run_id || github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.ref_protected != true }}
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
env:
|
||||||
|
ELECTRON_OUT_DIR: Default
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-linux-arm64-64k:
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
runs-on: ${{ inputs.test-runs-on }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: read
|
||||||
|
pull-requests: read
|
||||||
|
steps:
|
||||||
|
- name: Download Generated Artifacts
|
||||||
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
|
||||||
|
with:
|
||||||
|
name: generated_artifacts_linux_arm64
|
||||||
|
path: ./generated_artifacts_linux_arm64
|
||||||
|
- name: Download Src Artifacts
|
||||||
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
|
||||||
|
with:
|
||||||
|
name: src_artifacts_linux_arm64
|
||||||
|
path: ./src_artifacts_linux_arm64
|
||||||
|
- name: Restore Generated Artifacts
|
||||||
|
run: ./src/electron/script/actions/restore-artifacts.sh
|
||||||
|
- name: Unzip Dist
|
||||||
|
run: |
|
||||||
|
cd src/out/Default
|
||||||
|
unzip -:o dist.zip
|
||||||
|
|
||||||
|
- name: Run Electron Tests in QEMU 64k Container
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
MOCHA_REPORTER: mocha-multi-reporters
|
||||||
|
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
|
||||||
|
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||||
|
DISPLAY: ':99.0'
|
||||||
|
run: |
|
||||||
|
container=$(echo '${{ inputs.test-container }}' | jq -r '.image')
|
||||||
|
echo "Running tests in container: $container"
|
||||||
|
src/electron/script/run-qemu-64k.sh --container="$container" --testfiles=`pwd`/src
|
||||||
|
|
||||||
@@ -30,9 +30,14 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
display-server:
|
||||||
|
description: 'Display backend for Linux tests: x11 or wayland'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
default: x11
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: electron-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.is-asan }}-${{ github.ref_protected == true && github.run_id || github.ref }}
|
group: electron-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.is-asan }}-${{ inputs.display-server }}-${{ github.ref_protected == true && github.run_id || github.ref }}
|
||||||
cancel-in-progress: ${{ github.ref_protected != true }}
|
cancel-in-progress: ${{ github.ref_protected != true }}
|
||||||
|
|
||||||
permissions: {}
|
permissions: {}
|
||||||
@@ -59,7 +64,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
|
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
|
||||||
shard: ${{ inputs.target-platform == 'linux' && fromJSON('[1, 2, 3]') || fromJSON('[1, 2]') }}
|
shard: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: ${{ matrix.build-type }}
|
BUILD_TYPE: ${{ matrix.build-type }}
|
||||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||||
@@ -210,7 +215,22 @@ jobs:
|
|||||||
cd src/electron
|
cd src/electron
|
||||||
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
|
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
|
||||||
# Get which tests are on this shard
|
# Get which tests are on this shard
|
||||||
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ inputs.target-platform == 'linux' && 3 || 2 }})
|
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ case(inputs.display-server == 'wayland', 1, inputs.target-platform == 'linux', 3, 2) }})
|
||||||
|
if [ "${{ inputs.display-server }}" = "wayland" ]; then
|
||||||
|
allowlist_file=script/wayland-test-allowlist.txt
|
||||||
|
filtered_tests=""
|
||||||
|
for test_file in $tests_files; do
|
||||||
|
if grep -Fxq "$test_file" "$allowlist_file"; then
|
||||||
|
filtered_tests="$filtered_tests $test_file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
tests_files="${filtered_tests# }"
|
||||||
|
|
||||||
|
if [ -z "$tests_files" ]; then
|
||||||
|
echo "No tests matched Wayland filter, skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
if [ "${{ inputs.target-platform }}" != "linux" ]; then
|
if [ "${{ inputs.target-platform }}" != "linux" ]; then
|
||||||
@@ -244,9 +264,13 @@ jobs:
|
|||||||
else
|
else
|
||||||
if [ "${{ inputs.target-arch }}" = "arm" ]; then
|
if [ "${{ inputs.target-arch }}" = "arm" ]; then
|
||||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --skipYarnInstall --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --skipYarnInstall --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||||
|
else
|
||||||
|
if [ "${{ inputs.display-server }}" = "wayland" ]; then
|
||||||
|
runuser -u builduser -- script/actions/run-tests-wayland.sh script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||||
else
|
else
|
||||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -268,7 +292,7 @@ jobs:
|
|||||||
if: always() && !cancelled()
|
if: always() && !cancelled()
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
|
||||||
with:
|
with:
|
||||||
name: test_artifacts_${{ env.ARTIFACT_KEY }}_${{ matrix.shard }}
|
name: ${{ inputs.target-platform == 'linux' && format('test_artifacts_{0}_{1}_{2}', env.ARTIFACT_KEY, inputs.display-server, matrix.shard) || format('test_artifacts_{0}_{1}', env.ARTIFACT_KEY, matrix.shard) }}
|
||||||
path: src/electron/spec/artifacts
|
path: src/electron/spec/artifacts
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
- name: Wait for active SSH sessions
|
- name: Wait for active SSH sessions
|
||||||
|
|||||||
2
.github/workflows/windows-publish.yml
vendored
2
.github/workflows/windows-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
build-image-sha:
|
build-image-sha:
|
||||||
type: string
|
type: string
|
||||||
description: 'SHA for electron/build image'
|
description: 'SHA for electron/build image'
|
||||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||||
required: true
|
required: true
|
||||||
upload-to-storage:
|
upload-to-storage:
|
||||||
description: 'Uploads to Azure storage'
|
description: 'Uploads to Azure storage'
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ The `dialog` module has the following methods:
|
|||||||
* `openFile` - Allow files to be selected.
|
* `openFile` - Allow files to be selected.
|
||||||
* `openDirectory` - Allow directories to be selected.
|
* `openDirectory` - Allow directories to be selected.
|
||||||
* `multiSelections` - Allow multiple paths to be selected.
|
* `multiSelections` - Allow multiple paths to be selected.
|
||||||
* `showHiddenFiles` - Show hidden files in dialog.
|
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||||
in the dialog does not exist. This does not actually create the file at
|
in the dialog does not exist. This does not actually create the file at
|
||||||
@@ -102,7 +102,7 @@ dialog.showOpenDialogSync(mainWindow, {
|
|||||||
* `openFile` - Allow files to be selected.
|
* `openFile` - Allow files to be selected.
|
||||||
* `openDirectory` - Allow directories to be selected.
|
* `openDirectory` - Allow directories to be selected.
|
||||||
* `multiSelections` - Allow multiple paths to be selected.
|
* `multiSelections` - Allow multiple paths to be selected.
|
||||||
* `showHiddenFiles` - Show hidden files in dialog.
|
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||||
in the dialog does not exist. This does not actually create the file at
|
in the dialog does not exist. This does not actually create the file at
|
||||||
@@ -185,7 +185,7 @@ dialog.showOpenDialog(mainWindow, {
|
|||||||
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box,
|
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box,
|
||||||
defaults to `true`.
|
defaults to `true`.
|
||||||
* `properties` string[] (optional)
|
* `properties` string[] (optional)
|
||||||
* `showHiddenFiles` - Show hidden files in dialog.
|
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||||
as a directory instead of a file.
|
as a directory instead of a file.
|
||||||
@@ -215,7 +215,7 @@ The `filters` specifies an array of file types that can be displayed, see
|
|||||||
displayed in front of the filename text field.
|
displayed in front of the filename text field.
|
||||||
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box, defaults to `true`.
|
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box, defaults to `true`.
|
||||||
* `properties` string[] (optional)
|
* `properties` string[] (optional)
|
||||||
* `showHiddenFiles` - Show hidden files in dialog.
|
* `showHiddenFiles` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux.
|
||||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||||
as a directory instead of a file.
|
as a directory instead of a file.
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ your preload script and expose it using the [contextBridge](https://www.electron
|
|||||||
Debug symbols for MacOS (dSYM) now use xz compression in order to handle larger file sizes. `dsym.zip` files are now
|
Debug symbols for MacOS (dSYM) now use xz compression in order to handle larger file sizes. `dsym.zip` files are now
|
||||||
`dsym.tar.xz` files. End users using debug symbols may need to update their zip utilities.
|
`dsym.tar.xz` files. End users using debug symbols may need to update their zip utilities.
|
||||||
|
|
||||||
|
### Deprecated: `showHiddenFiles` in Dialogs on Linux
|
||||||
|
|
||||||
|
This property will still be honored on macOS and Windows, but support on Linux
|
||||||
|
will be removed in Electron 42. GTK intends for this to be a user choice rather
|
||||||
|
than an app choice and has removed the API to do this programmatically.
|
||||||
|
|
||||||
## Planned Breaking API Changes (39.0)
|
## Planned Breaking API Changes (39.0)
|
||||||
|
|
||||||
### Deprecated: `--host-rules` command line switch
|
### Deprecated: `--host-rules` command line switch
|
||||||
|
|||||||
@@ -144,3 +144,4 @@ fix_linux_tray_id.patch
|
|||||||
expose_gtk_ui_platform_field.patch
|
expose_gtk_ui_platform_field.patch
|
||||||
patch_osr_control_screen_info.patch
|
patch_osr_control_screen_info.patch
|
||||||
refactor_allow_customizing_config_in_freedesktopsecretkeyprovider.patch
|
refactor_allow_customizing_config_in_freedesktopsecretkeyprovider.patch
|
||||||
|
fix_wayland_test_crash_on_teardown.patch
|
||||||
|
|||||||
20
patches/chromium/fix_wayland_test_crash_on_teardown.patch
Normal file
20
patches/chromium/fix_wayland_test_crash_on_teardown.patch
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mitchell Cohen <mitch.cohen@me.com>
|
||||||
|
Date: Sun, 22 Feb 2026 11:38:49 -0500
|
||||||
|
Subject: fix: Wayland test crash on teardown
|
||||||
|
|
||||||
|
Allows Wayland test job to teardown the connection without crashing when trying to update the pointer
|
||||||
|
|
||||||
|
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
|
||||||
|
index 4c44eaeebe091906a1676da106faa9072819b67e..224f6abfe06794d31fc4d876c8242dab79ba075d 100644
|
||||||
|
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
|
||||||
|
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
|
||||||
|
@@ -426,7 +426,7 @@ std::vector<TouchscreenDevice> WaylandConnection::CreateTouchscreenDevices()
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaylandConnection::UpdateCursor() {
|
||||||
|
- if (auto* pointer = seat_->pointer()) {
|
||||||
|
+ if (auto* pointer = seat_ ? seat_->pointer() : nullptr) {
|
||||||
|
cursor_ = std::make_unique<WaylandCursor>(pointer, this);
|
||||||
|
cursor_->set_listener(listener_);
|
||||||
|
cursor_position_ = std::make_unique<WaylandCursorPosition>();
|
||||||
30
script/actions/run-tests-wayland.sh
Executable file
30
script/actions/run-tests-wayland.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export XDG_SESSION_TYPE=wayland
|
||||||
|
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-99}"
|
||||||
|
|
||||||
|
if [[ -z "${XDG_RUNTIME_DIR:-}" ]]; then
|
||||||
|
XDG_RUNTIME_DIR="$(mktemp -d)"
|
||||||
|
chmod 700 "$XDG_RUNTIME_DIR"
|
||||||
|
export XDG_RUNTIME_DIR
|
||||||
|
trap 'kill "$WESTON_PID" >/dev/null 2>&1 || true; rm -rf "$XDG_RUNTIME_DIR"' EXIT
|
||||||
|
else
|
||||||
|
trap 'kill "$WESTON_PID" >/dev/null 2>&1 || true' EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
weston \
|
||||||
|
--backend=headless-backend.so \
|
||||||
|
--socket="$WAYLAND_DISPLAY" \
|
||||||
|
--idle-time=0 \
|
||||||
|
>/tmp/weston-headless.log 2>&1 &
|
||||||
|
WESTON_PID=$!
|
||||||
|
|
||||||
|
for _ in {1..100}; do
|
||||||
|
if [[ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
node "$@" --ozone-platform=wayland
|
||||||
41
script/qemu-init.sh
Normal file
41
script/qemu-init.sh
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Mounting essential filesystems"
|
||||||
|
mount -t proc proc /proc
|
||||||
|
mount -t sysfs sys /sys
|
||||||
|
mkdir -p /dev/pts
|
||||||
|
mount -t devpts devpts /dev/pts
|
||||||
|
mkdir -p /dev/shm
|
||||||
|
mount -t tmpfs tmpfs /dev/shm
|
||||||
|
mount -t tmpfs tmpfs /tmp
|
||||||
|
chmod 1777 /tmp
|
||||||
|
mount -t tmpfs tmpfs /run
|
||||||
|
mkdir -p /run/dbus
|
||||||
|
mkdir -p /run/user/0
|
||||||
|
chmod 700 /run/user/0
|
||||||
|
mount -t tmpfs tmpfs /var/tmp
|
||||||
|
|
||||||
|
echo "Setting up machine-id for D-Bus"
|
||||||
|
cat /proc/sys/kernel/random/uuid | tr -d '-' > /etc/machine-id
|
||||||
|
|
||||||
|
echo "Setting system clock"
|
||||||
|
date -s "$(cat /host-time)"
|
||||||
|
|
||||||
|
echo "Setting up networking"
|
||||||
|
ip link set lo up
|
||||||
|
ip link set eth0 up
|
||||||
|
ip addr add 10.0.2.15/24 dev eth0
|
||||||
|
ip route add default via 10.0.2.2
|
||||||
|
echo "nameserver 10.0.2.3" > /etc/resolv.conf
|
||||||
|
|
||||||
|
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
export XDG_RUNTIME_DIR=/run/user/0
|
||||||
|
|
||||||
|
echo "Starting entrypoint"
|
||||||
|
echo "System: $(uname -s) $(uname -r) $(uname -m), page size: $(getconf PAGESIZE) bytes"
|
||||||
|
EXIT_CODE=$?
|
||||||
|
echo $EXIT_CODE > /exit-code
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "Powering off"
|
||||||
|
echo o > /proc/sysrq-trigger
|
||||||
89
script/run-qemu-64k.sh
Normal file
89
script/run-qemu-64k.sh
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CONTAINER=""
|
||||||
|
TESTFILES=""
|
||||||
|
ARGS=""
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--container) CONTAINER="$2"; shift 2 ;;
|
||||||
|
--testfiles) TESTFILES="$2"; shift 2 ;;
|
||||||
|
--) shift; ARGS="$*"; break ;;
|
||||||
|
*) echo "Unknown option: $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$CONTAINER" ]; then
|
||||||
|
echo "Usage: $0 --container CONTAINER [-- ARGS...]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing QEMU system emulation and tools"
|
||||||
|
sudo apt-get update && sudo apt-get install -y qemu-system-arm binutils
|
||||||
|
|
||||||
|
echo "Exporting container filesystem"
|
||||||
|
CONTAINER_ID=$(docker create --platform linux/arm64 "$CONTAINER")
|
||||||
|
ROOTFS_DIR=$(mktemp -d)
|
||||||
|
docker export "$CONTAINER_ID" | sudo tar -xf - -C "$ROOTFS_DIR"
|
||||||
|
docker rm -f "$CONTAINER_ID"
|
||||||
|
|
||||||
|
echo "Removing container image to free disk space"
|
||||||
|
docker rmi "$CONTAINER" || true
|
||||||
|
docker system prune -f || true
|
||||||
|
|
||||||
|
echo "Copying test files into root filesystem"
|
||||||
|
if [ -n "$TESTFILES" ]; then
|
||||||
|
sudo cp -r $TESTFILES "$ROOTFS_DIR/root/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Downloading Ubuntu 24.04 generic-64k kernel for ARM64"
|
||||||
|
KERNEL_URL="http://ports.ubuntu.com/ubuntu-ports/pool/main/l/linux/linux-image-unsigned-6.8.0-90-generic-64k_6.8.0-90.91_arm64.deb"
|
||||||
|
KERNEL_DIR=$(mktemp -d)
|
||||||
|
curl -fL "$KERNEL_URL" -o "$KERNEL_DIR/kernel.deb"
|
||||||
|
|
||||||
|
echo "Extracting kernel"
|
||||||
|
(cd "$KERNEL_DIR" && ar x kernel.deb && tar xf data.tar*)
|
||||||
|
VMLINUZ="$KERNEL_DIR/boot/vmlinuz-6.8.0-90-generic-64k"
|
||||||
|
if [ ! -f "$VMLINUZ" ]; then
|
||||||
|
echo "Error: Could not find kernel at $VMLINUZ"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Storing test arguments and installing init script"
|
||||||
|
echo "$ARGS" > "$ROOTFS_DIR/test-args"
|
||||||
|
date -u '+%Y-%m-%d %H:%M:%S' > "$ROOTFS_DIR/host-time"
|
||||||
|
sudo mv "$ROOTFS_DIR/root/electron/scripts/qemu-init.sh" "$ROOTFS_DIR/init"
|
||||||
|
sudo chmod +x "$ROOTFS_DIR/init"
|
||||||
|
|
||||||
|
echo "Creating disk image with root filesystem"
|
||||||
|
DISK_IMG=$(mktemp)
|
||||||
|
dd if=/dev/zero of="$DISK_IMG" bs=1M count=2048 status=none
|
||||||
|
sudo mkfs.ext4 -q -d "$ROOTFS_DIR" "$DISK_IMG"
|
||||||
|
sudo rm -rf "$ROOTFS_DIR"
|
||||||
|
|
||||||
|
echo "Starting QEMU VM with 64K page size kernel"
|
||||||
|
timeout 1800 qemu-system-aarch64 \
|
||||||
|
-M virt \
|
||||||
|
-cpu max,pauth-impdef=on \
|
||||||
|
-accel tcg,thread=multi \
|
||||||
|
-m 4096 \
|
||||||
|
-smp 2 \
|
||||||
|
-kernel "$VMLINUZ" \
|
||||||
|
-append "console=ttyAMA0 root=/dev/vda rw init=/init net.ifnames=0" \
|
||||||
|
-drive file="$DISK_IMG",format=raw,if=virtio \
|
||||||
|
-netdev user,id=net0 \
|
||||||
|
-device virtio-net-pci,netdev=net0 \
|
||||||
|
-nographic \
|
||||||
|
-no-reboot \
|
||||||
|
|| true
|
||||||
|
|
||||||
|
echo "Extracting test results from disk image"
|
||||||
|
MOUNT_DIR=$(mktemp -d)
|
||||||
|
sudo mount -o loop "$DISK_IMG" "$MOUNT_DIR"
|
||||||
|
if [ -f "$MOUNT_DIR/root/results.xml" ]; then
|
||||||
|
cp "$MOUNT_DIR/root/results.xml" "$TEST_DIR/results.xml"
|
||||||
|
fi
|
||||||
|
EXIT_CODE=$(cat "$MOUNT_DIR/exit-code" 2>/dev/null || echo 1)
|
||||||
|
sudo umount "$MOUNT_DIR"
|
||||||
|
exit $EXIT_CODE
|
||||||
4
script/wayland-test-allowlist.txt
Normal file
4
script/wayland-test-allowlist.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
spec/parse-features-string-spec.ts
|
||||||
|
spec/types-spec.ts
|
||||||
|
spec/version-bump-spec.ts
|
||||||
|
spec/api-app-spec.ts
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "base/process/launch.h"
|
#include "base/process/launch.h"
|
||||||
#include "base/process/process.h"
|
#include "base/process/process.h"
|
||||||
#include "chrome/browser/browser_process.h"
|
#include "chrome/browser/browser_process.h"
|
||||||
|
#include "content/browser/network_service_instance_impl.h" // nogncheck
|
||||||
#include "content/public/browser/child_process_host.h"
|
#include "content/public/browser/child_process_host.h"
|
||||||
#include "content/public/browser/service_process_host.h"
|
#include "content/public/browser/service_process_host.h"
|
||||||
#include "content/public/common/result_codes.h"
|
#include "content/public/common/result_codes.h"
|
||||||
@@ -72,7 +73,8 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
|||||||
base::FilePath current_working_directory,
|
base::FilePath current_working_directory,
|
||||||
bool use_plugin_helper,
|
bool use_plugin_helper,
|
||||||
bool create_network_observer,
|
bool create_network_observer,
|
||||||
bool disclaim_responsibility) {
|
bool disclaim_responsibility)
|
||||||
|
: create_network_observer_(create_network_observer) {
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
base::win::ScopedHandle stdout_write(nullptr);
|
base::win::ScopedHandle stdout_write(nullptr);
|
||||||
base::win::ScopedHandle stderr_write(nullptr);
|
base::win::ScopedHandle stderr_write(nullptr);
|
||||||
@@ -212,32 +214,15 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
|||||||
connector_->set_connection_error_handler(base::BindOnce(
|
connector_->set_connection_error_handler(base::BindOnce(
|
||||||
&UtilityProcessWrapper::CloseConnectorPort, weak_factory_.GetWeakPtr()));
|
&UtilityProcessWrapper::CloseConnectorPort, weak_factory_.GetWeakPtr()));
|
||||||
|
|
||||||
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
|
params->url_loader_factory_params = CreateURLLoaderFactoryParams();
|
||||||
network::mojom::URLLoaderFactoryParamsPtr loader_params =
|
|
||||||
network::mojom::URLLoaderFactoryParams::New();
|
|
||||||
loader_params->process_id = network::OriginatingProcess::browser();
|
|
||||||
loader_params->is_orb_enabled = false;
|
|
||||||
loader_params->is_trusted = true;
|
|
||||||
if (create_network_observer) {
|
|
||||||
url_loader_network_observer_.emplace();
|
|
||||||
loader_params->url_loader_network_observer =
|
|
||||||
url_loader_network_observer_->Bind();
|
|
||||||
}
|
|
||||||
network::mojom::NetworkContext* network_context =
|
|
||||||
g_browser_process->system_network_context_manager()->GetContext();
|
|
||||||
network_context->CreateURLLoaderFactory(
|
|
||||||
url_loader_factory.InitWithNewPipeAndPassReceiver(),
|
|
||||||
std::move(loader_params));
|
|
||||||
params->url_loader_factory = std::move(url_loader_factory);
|
|
||||||
mojo::PendingRemote<network::mojom::HostResolver> host_resolver;
|
|
||||||
network_context->CreateHostResolver(
|
|
||||||
{}, host_resolver.InitWithNewPipeAndPassReceiver());
|
|
||||||
params->host_resolver = std::move(host_resolver);
|
|
||||||
params->use_network_observer_from_url_loader_factory =
|
|
||||||
create_network_observer;
|
|
||||||
|
|
||||||
node_service_remote_->Initialize(std::move(params),
|
node_service_remote_->Initialize(std::move(params),
|
||||||
receiver_.BindNewPipeAndPassRemote());
|
receiver_.BindNewPipeAndPassRemote());
|
||||||
|
|
||||||
|
// Subscribe to Network Service process gone notifications.
|
||||||
|
network_service_gone_subscription_ =
|
||||||
|
content::RegisterNetworkServiceProcessGoneHandler(base::BindRepeating(
|
||||||
|
&UtilityProcessWrapper::CreateAndSendURLLoaderFactory,
|
||||||
|
weak_factory_.GetWeakPtr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
UtilityProcessWrapper::~UtilityProcessWrapper() {
|
UtilityProcessWrapper::~UtilityProcessWrapper() {
|
||||||
@@ -429,6 +414,44 @@ void UtilityProcessWrapper::OnV8FatalError(const std::string& location,
|
|||||||
EmitWithoutEvent("error", "FatalError", location, report);
|
EmitWithoutEvent("error", "FatalError", location, report);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UtilityProcessWrapper::CreateAndSendURLLoaderFactory(bool /* crashed */) {
|
||||||
|
if (!node_service_remote_.is_connected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
node_service_remote_->UpdateURLLoaderFactory(CreateURLLoaderFactoryParams());
|
||||||
|
}
|
||||||
|
|
||||||
|
node::mojom::URLLoaderFactoryParamsPtr
|
||||||
|
UtilityProcessWrapper::CreateURLLoaderFactoryParams() {
|
||||||
|
node::mojom::URLLoaderFactoryParamsPtr params =
|
||||||
|
node::mojom::URLLoaderFactoryParams::New();
|
||||||
|
mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
|
||||||
|
network::mojom::URLLoaderFactoryParamsPtr loader_params =
|
||||||
|
network::mojom::URLLoaderFactoryParams::New();
|
||||||
|
loader_params->process_id = network::OriginatingProcess::browser();
|
||||||
|
loader_params->is_orb_enabled = false;
|
||||||
|
loader_params->is_trusted = true;
|
||||||
|
if (create_network_observer_) {
|
||||||
|
url_loader_network_observer_.emplace();
|
||||||
|
loader_params->url_loader_network_observer =
|
||||||
|
url_loader_network_observer_->Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
network::mojom::NetworkContext* network_context =
|
||||||
|
g_browser_process->system_network_context_manager()->GetContext();
|
||||||
|
network_context->CreateURLLoaderFactory(
|
||||||
|
url_loader_factory.InitWithNewPipeAndPassReceiver(),
|
||||||
|
std::move(loader_params));
|
||||||
|
params->url_loader_factory = std::move(url_loader_factory);
|
||||||
|
mojo::PendingRemote<network::mojom::HostResolver> host_resolver;
|
||||||
|
network_context->CreateHostResolver(
|
||||||
|
{}, host_resolver.InitWithNewPipeAndPassReceiver());
|
||||||
|
params->host_resolver = std::move(host_resolver);
|
||||||
|
params->use_network_observer_from_url_loader_factory =
|
||||||
|
create_network_observer_;
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
raw_ptr<UtilityProcessWrapper> UtilityProcessWrapper::FromProcessId(
|
raw_ptr<UtilityProcessWrapper> UtilityProcessWrapper::FromProcessId(
|
||||||
base::ProcessId pid) {
|
base::ProcessId pid) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/callback_list.h"
|
||||||
#include "base/containers/id_map.h"
|
#include "base/containers/id_map.h"
|
||||||
#include "base/environment.h"
|
#include "base/environment.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
@@ -99,6 +100,11 @@ class UtilityProcessWrapper final
|
|||||||
void OnServiceProcessDisconnected(uint32_t exit_code,
|
void OnServiceProcessDisconnected(uint32_t exit_code,
|
||||||
const std::string& description);
|
const std::string& description);
|
||||||
|
|
||||||
|
// Creates and sends a new URLLoaderFactory to the utility process.
|
||||||
|
// Called after Network Service restart to update the factory.
|
||||||
|
void CreateAndSendURLLoaderFactory(bool crashed);
|
||||||
|
node::mojom::URLLoaderFactoryParamsPtr CreateURLLoaderFactoryParams();
|
||||||
|
|
||||||
base::ProcessId pid_ = base::kNullProcessId;
|
base::ProcessId pid_ = base::kNullProcessId;
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
// Non-owning handles, these will be closed when the
|
// Non-owning handles, these will be closed when the
|
||||||
@@ -111,12 +117,14 @@ class UtilityProcessWrapper final
|
|||||||
bool connector_closed_ = false;
|
bool connector_closed_ = false;
|
||||||
bool terminated_ = false;
|
bool terminated_ = false;
|
||||||
bool killed_ = false;
|
bool killed_ = false;
|
||||||
|
bool create_network_observer_ = false;
|
||||||
std::unique_ptr<mojo::Connector> connector_;
|
std::unique_ptr<mojo::Connector> connector_;
|
||||||
blink::MessagePortDescriptor host_port_;
|
blink::MessagePortDescriptor host_port_;
|
||||||
mojo::Receiver<node::mojom::NodeServiceClient> receiver_{this};
|
mojo::Receiver<node::mojom::NodeServiceClient> receiver_{this};
|
||||||
mojo::Remote<node::mojom::NodeService> node_service_remote_;
|
mojo::Remote<node::mojom::NodeService> node_service_remote_;
|
||||||
std::optional<electron::URLLoaderNetworkObserver>
|
std::optional<electron::URLLoaderNetworkObserver>
|
||||||
url_loader_network_observer_;
|
url_loader_network_observer_;
|
||||||
|
base::CallbackListSubscription network_service_gone_subscription_;
|
||||||
base::WeakPtrFactory<UtilityProcessWrapper> weak_factory_{this};
|
base::WeakPtrFactory<UtilityProcessWrapper> weak_factory_{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
|
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
|
||||||
#include "components/proxy_config/proxy_config_pref_names.h"
|
#include "components/proxy_config/proxy_config_pref_names.h"
|
||||||
#include "content/browser/blob_storage/chrome_blob_storage_context.h" // nogncheck
|
#include "content/browser/blob_storage/chrome_blob_storage_context.h" // nogncheck
|
||||||
|
#include "content/browser/network_service_instance_impl.h" // nogncheck
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "content/public/browser/cors_origin_pattern_setter.h"
|
#include "content/public/browser/cors_origin_pattern_setter.h"
|
||||||
#include "content/public/browser/host_zoom_map.h"
|
#include "content/public/browser/host_zoom_map.h"
|
||||||
@@ -407,10 +408,18 @@ ElectronBrowserContext::ElectronBrowserContext(
|
|||||||
extension_system->FinishInitialization();
|
extension_system->FinishInitialization();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Subscribe to Network Service process gone notifications to reset the
|
||||||
|
// cached URLLoaderFactory when the Network Service crashes or restarts.
|
||||||
|
network_service_gone_subscription_ =
|
||||||
|
content::RegisterNetworkServiceProcessGoneHandler(base::BindRepeating(
|
||||||
|
&ElectronBrowserContext::OnNetworkServiceProcessGone,
|
||||||
|
weak_factory_.GetWeakPtr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ElectronBrowserContext::~ElectronBrowserContext() {
|
ElectronBrowserContext::~ElectronBrowserContext() {
|
||||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||||
|
|
||||||
NotifyWillBeDestroyed();
|
NotifyWillBeDestroyed();
|
||||||
|
|
||||||
// Notify any keyed services of browser context destruction.
|
// Notify any keyed services of browser context destruction.
|
||||||
@@ -568,6 +577,12 @@ content::PreconnectManager* ElectronBrowserContext::GetPreconnectManager() {
|
|||||||
return preconnect_manager_.get();
|
return preconnect_manager_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ElectronBrowserContext::OnNetworkServiceProcessGone(bool /* crashed */) {
|
||||||
|
// Clear the cached URLLoaderFactory so the next request creates a new one
|
||||||
|
// from the new NetworkContext.
|
||||||
|
url_loader_factory_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
scoped_refptr<network::SharedURLLoaderFactory>
|
scoped_refptr<network::SharedURLLoaderFactory>
|
||||||
ElectronBrowserContext::GetURLLoaderFactory() {
|
ElectronBrowserContext::GetURLLoaderFactory() {
|
||||||
if (url_loader_factory_)
|
if (url_loader_factory_)
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/callback_list.h"
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "content/public/browser/browser_context.h"
|
#include "content/public/browser/browser_context.h"
|
||||||
#include "content/public/browser/media_stream_request.h"
|
#include "content/public/browser/media_stream_request.h"
|
||||||
#include "mojo/public/cpp/bindings/remote.h"
|
#include "mojo/public/cpp/bindings/remote.h"
|
||||||
@@ -184,6 +186,9 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||||||
// Initialize pref registry.
|
// Initialize pref registry.
|
||||||
void InitPrefs();
|
void InitPrefs();
|
||||||
|
|
||||||
|
// Called when the Network Service process crashes or restarts.
|
||||||
|
void OnNetworkServiceProcessGone(bool crashed);
|
||||||
|
|
||||||
scoped_refptr<ValueMapPrefStore> in_memory_pref_store_;
|
scoped_refptr<ValueMapPrefStore> in_memory_pref_store_;
|
||||||
std::unique_ptr<CookieChangeNotifier> cookie_change_notifier_;
|
std::unique_ptr<CookieChangeNotifier> cookie_change_notifier_;
|
||||||
std::unique_ptr<PrefService> prefs_;
|
std::unique_ptr<PrefService> prefs_;
|
||||||
@@ -207,6 +212,9 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||||||
// Shared URLLoaderFactory.
|
// Shared URLLoaderFactory.
|
||||||
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
|
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
|
||||||
|
|
||||||
|
// Subscription to Network Service process gone notifications.
|
||||||
|
base::CallbackListSubscription network_service_gone_subscription_;
|
||||||
|
|
||||||
network::mojom::SSLConfigPtr ssl_config_;
|
network::mojom::SSLConfigPtr ssl_config_;
|
||||||
mojo::Remote<network::mojom::SSLConfigClient> ssl_config_client_;
|
mojo::Remote<network::mojom::SSLConfigClient> ssl_config_client_;
|
||||||
|
|
||||||
@@ -214,6 +222,8 @@ class ElectronBrowserContext : public content::BrowserContext {
|
|||||||
|
|
||||||
// In-memory cache that holds objects that have been granted permissions.
|
// In-memory cache that holds objects that have been granted permissions.
|
||||||
DevicePermissionMap granted_devices_;
|
DevicePermissionMap granted_devices_;
|
||||||
|
|
||||||
|
base::WeakPtrFactory<ElectronBrowserContext> weak_factory_{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|||||||
@@ -5,8 +5,12 @@
|
|||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
#include "base/dcheck_is_on.h"
|
#include "base/dcheck_is_on.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "content/browser/network_service_instance_impl.h" // nogncheck
|
||||||
|
#include "content/public/browser/network_service_instance.h"
|
||||||
#include "content/public/common/content_switches.h"
|
#include "content/public/common/content_switches.h"
|
||||||
|
#include "shell/common/callback_util.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
|
#include "shell/common/gin_helper/promise.h"
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
#include "v8/include/v8.h"
|
#include "v8/include/v8.h"
|
||||||
|
|
||||||
@@ -41,6 +45,18 @@ std::string GetLoggingDestination() {
|
|||||||
return command_line->GetSwitchValueASCII(switches::kEnableLogging);
|
return command_line->GetSwitchValueASCII(switches::kEnableLogging);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Promise> SimulateNetworkServiceCrash(v8::Isolate* isolate) {
|
||||||
|
gin_helper::Promise<void> promise(isolate);
|
||||||
|
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||||
|
auto subscription = content::RegisterNetworkServiceProcessGoneHandler(
|
||||||
|
electron::AdaptCallbackForRepeating(
|
||||||
|
base::BindOnce([](gin_helper::Promise<void> promise,
|
||||||
|
bool crashed) { promise.Resolve(); },
|
||||||
|
std::move(promise))));
|
||||||
|
content::RestartNetworkService();
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
void Initialize(v8::Local<v8::Object> exports,
|
void Initialize(v8::Local<v8::Object> exports,
|
||||||
v8::Local<v8::Value> unused,
|
v8::Local<v8::Value> unused,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
@@ -49,6 +65,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
|||||||
gin_helper::Dictionary dict{isolate, exports};
|
gin_helper::Dictionary dict{isolate, exports};
|
||||||
dict.SetMethod("log", &Log);
|
dict.SetMethod("log", &Log);
|
||||||
dict.SetMethod("getLoggingDestination", &GetLoggingDestination);
|
dict.SetMethod("getLoggingDestination", &GetLoggingDestination);
|
||||||
|
dict.SetMethod("simulateNetworkServiceCrash", &SimulateNetworkServiceCrash);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -484,6 +484,7 @@ void SimpleURLLoaderWrapper::Clone(
|
|||||||
|
|
||||||
void SimpleURLLoaderWrapper::Cancel() {
|
void SimpleURLLoaderWrapper::Cancel() {
|
||||||
loader_.reset();
|
loader_.reset();
|
||||||
|
url_loader_factory_.reset();
|
||||||
pinned_wrapper_.Reset();
|
pinned_wrapper_.Reset();
|
||||||
pinned_chunk_pipe_getter_.Reset();
|
pinned_chunk_pipe_getter_.Reset();
|
||||||
// This ensures that no further callbacks will be called, so there's no need
|
// This ensures that no further callbacks will be called, so there's no need
|
||||||
@@ -750,6 +751,7 @@ void SimpleURLLoaderWrapper::OnComplete(bool success) {
|
|||||||
// we would perform cleanup of the wrapper and we should bail out below.
|
// we would perform cleanup of the wrapper and we should bail out below.
|
||||||
if (self) {
|
if (self) {
|
||||||
loader_.reset();
|
loader_.reset();
|
||||||
|
url_loader_factory_.reset();
|
||||||
pinned_wrapper_.Reset();
|
pinned_wrapper_.Reset();
|
||||||
pinned_chunk_pipe_getter_.Reset();
|
pinned_chunk_pipe_getter_.Reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,10 +122,9 @@ void NodeService::Initialize(
|
|||||||
|
|
||||||
ParentPort::GetInstance()->Initialize(std::move(params->port));
|
ParentPort::GetInstance()->Initialize(std::move(params->port));
|
||||||
|
|
||||||
URLLoaderBundle::GetInstance()->SetURLLoaderFactory(
|
if (params->url_loader_factory_params) {
|
||||||
std::move(params->url_loader_factory),
|
UpdateURLLoaderFactory(std::move(params->url_loader_factory_params));
|
||||||
mojo::Remote(std::move(params->host_resolver)),
|
}
|
||||||
params->use_network_observer_from_url_loader_factory);
|
|
||||||
|
|
||||||
js_env_ = std::make_unique<JavascriptEnvironment>(node_bindings_->uv_loop());
|
js_env_ = std::make_unique<JavascriptEnvironment>(node_bindings_->uv_loop());
|
||||||
|
|
||||||
@@ -208,4 +207,12 @@ void NodeService::Initialize(
|
|||||||
node_bindings_->StartPolling();
|
node_bindings_->StartPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeService::UpdateURLLoaderFactory(
|
||||||
|
node::mojom::URLLoaderFactoryParamsPtr params) {
|
||||||
|
URLLoaderBundle::GetInstance()->SetURLLoaderFactory(
|
||||||
|
std::move(params->url_loader_factory),
|
||||||
|
mojo::Remote(std::move(params->host_resolver)),
|
||||||
|
params->use_network_observer_from_url_loader_factory);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ class NodeService : public node::mojom::NodeService {
|
|||||||
void Initialize(node::mojom::NodeServiceParamsPtr params,
|
void Initialize(node::mojom::NodeServiceParamsPtr params,
|
||||||
mojo::PendingRemote<node::mojom::NodeServiceClient>
|
mojo::PendingRemote<node::mojom::NodeServiceClient>
|
||||||
client_pending_remote) override;
|
client_pending_remote) override;
|
||||||
|
void UpdateURLLoaderFactory(
|
||||||
|
node::mojom::URLLoaderFactoryParamsPtr params) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This needs to be initialized first so that it can be destroyed last
|
// This needs to be initialized first so that it can be destroyed last
|
||||||
|
|||||||
@@ -10,14 +10,18 @@ import "services/network/public/mojom/host_resolver.mojom";
|
|||||||
import "services/network/public/mojom/url_loader_factory.mojom";
|
import "services/network/public/mojom/url_loader_factory.mojom";
|
||||||
import "third_party/blink/public/mojom/messaging/message_port_descriptor.mojom";
|
import "third_party/blink/public/mojom/messaging/message_port_descriptor.mojom";
|
||||||
|
|
||||||
|
struct URLLoaderFactoryParams {
|
||||||
|
pending_remote<network.mojom.URLLoaderFactory> url_loader_factory;
|
||||||
|
pending_remote<network.mojom.HostResolver> host_resolver;
|
||||||
|
bool use_network_observer_from_url_loader_factory = false;
|
||||||
|
};
|
||||||
|
|
||||||
struct NodeServiceParams {
|
struct NodeServiceParams {
|
||||||
mojo_base.mojom.FilePath script;
|
mojo_base.mojom.FilePath script;
|
||||||
array<string> args;
|
array<string> args;
|
||||||
array<string> exec_args;
|
array<string> exec_args;
|
||||||
blink.mojom.MessagePortDescriptor port;
|
blink.mojom.MessagePortDescriptor port;
|
||||||
pending_remote<network.mojom.URLLoaderFactory> url_loader_factory;
|
URLLoaderFactoryParams url_loader_factory_params;
|
||||||
pending_remote<network.mojom.HostResolver> host_resolver;
|
|
||||||
bool use_network_observer_from_url_loader_factory = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface NodeServiceClient {
|
interface NodeServiceClient {
|
||||||
@@ -28,4 +32,6 @@ interface NodeServiceClient {
|
|||||||
interface NodeService {
|
interface NodeService {
|
||||||
Initialize(NodeServiceParams params,
|
Initialize(NodeServiceParams params,
|
||||||
pending_remote<NodeServiceClient> client_remote);
|
pending_remote<NodeServiceClient> client_remote);
|
||||||
|
|
||||||
|
UpdateURLLoaderFactory(URLLoaderFactoryParams params);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { setTimeout } from 'node:timers/promises';
|
|||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
|
|
||||||
import { collectStreamBody, getResponse } from './lib/net-helpers';
|
import { collectStreamBody, getResponse } from './lib/net-helpers';
|
||||||
import { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers';
|
import { ifdescribe, ifit, isWayland, listen, waitUntil } from './lib/spec-helpers';
|
||||||
import { closeWindow, closeAllWindows } from './lib/window-helpers';
|
import { closeWindow, closeAllWindows } from './lib/window-helpers';
|
||||||
|
|
||||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||||
@@ -587,7 +587,7 @@ describe('app module', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: re-enable this test on win32.
|
// FIXME: re-enable this test on win32.
|
||||||
ifit(process.platform !== 'win32')('should emit render-process-gone event when renderer crashes', async () => {
|
ifit(process.platform !== 'win32' && !isWayland)('should emit render-process-gone event when renderer crashes', async () => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
@@ -1437,7 +1437,7 @@ describe('app module', () => {
|
|||||||
|
|
||||||
describe('getApplicationNameForProtocol()', () => {
|
describe('getApplicationNameForProtocol()', () => {
|
||||||
// TODO: Linux CI doesn't have registered http & https handlers
|
// TODO: Linux CI doesn't have registered http & https handlers
|
||||||
ifit(!(process.env.CI && process.platform === 'linux'))('returns application names for common protocols', function () {
|
ifit(!(process.env.CI && process.platform === 'linux') && !isWayland)('returns application names for common protocols', function () {
|
||||||
// We can't expect particular app names here, but these protocols should
|
// We can't expect particular app names here, but these protocols should
|
||||||
// at least have _something_ registered. Except on our Linux CI
|
// at least have _something_ registered. Except on our Linux CI
|
||||||
// environment apparently.
|
// environment apparently.
|
||||||
|
|||||||
@@ -1688,4 +1688,61 @@ describe('net module', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('Network Service crash recovery', () => {
|
||||||
|
const binding = process._linkedBinding('electron_common_testing');
|
||||||
|
|
||||||
|
it('should recover net.fetch after Network Service crash (main process)', async () => {
|
||||||
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
response.end('first');
|
||||||
|
});
|
||||||
|
const firstResponse = await net.fetch(serverUrl);
|
||||||
|
expect(firstResponse.ok).to.be.true();
|
||||||
|
expect(await firstResponse.text()).to.equal('first');
|
||||||
|
|
||||||
|
await binding.simulateNetworkServiceCrash();
|
||||||
|
|
||||||
|
// Wait for StoragePartitionImpl's NetworkContext disconnect handler to
|
||||||
|
// fire and reinitialize the context in the new Network Service.
|
||||||
|
await setTimeout(500);
|
||||||
|
|
||||||
|
const secondServerUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
response.end('second');
|
||||||
|
});
|
||||||
|
const secondResponse = await net.fetch(secondServerUrl);
|
||||||
|
expect(secondResponse.ok).to.be.true();
|
||||||
|
expect(await secondResponse.text()).to.equal('second');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should recover net.fetch after Network Service crash (utility process)', async () => {
|
||||||
|
const child = utilityProcess.fork(path.join(fixturesPath, 'api', 'utility-process', 'network-restart-test.js'));
|
||||||
|
await once(child, 'spawn');
|
||||||
|
await once(child, 'message');
|
||||||
|
|
||||||
|
const firstServerUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
response.end('utility-first');
|
||||||
|
});
|
||||||
|
child.postMessage({ type: 'fetch', url: firstServerUrl });
|
||||||
|
const [firstResult] = await once(child, 'message');
|
||||||
|
expect(firstResult.ok).to.be.true();
|
||||||
|
expect(firstResult.body).to.equal('utility-first');
|
||||||
|
|
||||||
|
await binding.simulateNetworkServiceCrash();
|
||||||
|
|
||||||
|
// Needed for UpdateURLLoaderFactory IPC to propagate to the utility process
|
||||||
|
// and for any in-flight requests to settle
|
||||||
|
await setTimeout(500);
|
||||||
|
|
||||||
|
const secondServerUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
response.end('utility-second');
|
||||||
|
});
|
||||||
|
child.postMessage({ type: 'fetch', url: secondServerUrl });
|
||||||
|
const [secondResult] = await once(child, 'message');
|
||||||
|
expect(secondResult.ok).to.be.true();
|
||||||
|
expect(secondResult.body).to.equal('utility-second');
|
||||||
|
|
||||||
|
child.kill();
|
||||||
|
await once(child, 'exit');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
24
spec/fixtures/api/utility-process/network-restart-test.js
vendored
Normal file
24
spec/fixtures/api/utility-process/network-restart-test.js
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const { net } = require('electron');
|
||||||
|
|
||||||
|
process.parentPort.on('message', async (e) => {
|
||||||
|
const { type, url } = e.data;
|
||||||
|
|
||||||
|
if (type === 'fetch') {
|
||||||
|
try {
|
||||||
|
const response = await net.fetch(url);
|
||||||
|
const body = await response.text();
|
||||||
|
process.parentPort.postMessage({
|
||||||
|
ok: response.ok,
|
||||||
|
status: response.status,
|
||||||
|
body
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
process.parentPort.postMessage({
|
||||||
|
ok: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.parentPort.postMessage({ type: 'ready' });
|
||||||
@@ -25,6 +25,12 @@ const addOnly = <T>(fn: Function): T => {
|
|||||||
export const ifit = (condition: boolean) => (condition ? it : addOnly<TestFunction>(it.skip));
|
export const ifit = (condition: boolean) => (condition ? it : addOnly<TestFunction>(it.skip));
|
||||||
export const ifdescribe = (condition: boolean) => (condition ? describe : addOnly<SuiteFunction>(describe.skip));
|
export const ifdescribe = (condition: boolean) => (condition ? describe : addOnly<SuiteFunction>(describe.skip));
|
||||||
|
|
||||||
|
export const isWayland = process.platform === 'linux' && (
|
||||||
|
process.env.XDG_SESSION_TYPE === 'wayland' ||
|
||||||
|
!!process.env.WAYLAND_DISPLAY ||
|
||||||
|
process.argv.includes('--ozone-platform=wayland')
|
||||||
|
);
|
||||||
|
|
||||||
type CleanupFunction = (() => void) | (() => Promise<void>)
|
type CleanupFunction = (() => void) | (() => Promise<void>)
|
||||||
const cleanupFunctions: CleanupFunction[] = [];
|
const cleanupFunctions: CleanupFunction[] = [];
|
||||||
export async function runCleanupFunctions () {
|
export async function runCleanupFunctions () {
|
||||||
|
|||||||
Reference in New Issue
Block a user