Compare commits

..

1 Commits

Author SHA1 Message Date
Charles Kerr
14fbf5086f refactor: replace std::to_string() with base::NumberToString() 2026-02-25 09:57:33 -06:00
33 changed files with 67 additions and 340 deletions

View File

@@ -2,7 +2,7 @@ version: '3'
services: services:
buildtools: buildtools:
image: ghcr.io/electron/devcontainer:eac3529546ea8f3aa356d31e345715eef342233b image: ghcr.io/electron/devcontainer:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
volumes: volumes:
- ..:/workspaces/gclient/src/electron:cached - ..:/workspaces/gclient/src/electron:cached

View File

@@ -41,7 +41,7 @@ jobs:
permissions: permissions:
contents: read contents: read
container: container:
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
options: --user root options: --user root
volumes: volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache - /mnt/cross-instance-cache:/mnt/cross-instance-cache

View File

@@ -15,7 +15,7 @@ jobs:
permissions: permissions:
contents: read contents: read
container: container:
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
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:eac3529546ea8f3aa356d31e345715eef342233b image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
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:eac3529546ea8f3aa356d31e345715eef342233b image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
options: --user root options: --user root
volumes: volumes:
- /mnt/cross-instance-cache:/mnt/cross-instance-cache - /mnt/cross-instance-cache:/mnt/cross-instance-cache

View File

@@ -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: 'eac3529546ea8f3aa356d31e345715eef342233b' default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
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=eac3529546ea8f3aa356d31e345715eef342233b" >> "$GITHUB_OUTPUT" echo "build-image-sha=a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb" >> "$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

View File

@@ -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: 'eac3529546ea8f3aa356d31e345715eef342233b' default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
upload-to-storage: upload-to-storage:
description: 'Uploads to Azure storage' description: 'Uploads to Azure storage'
required: false required: false

View File

@@ -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: 'eac3529546ea8f3aa356d31e345715eef342233b' default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
required: true required: true
upload-to-storage: upload-to-storage:
description: 'Uploads to Azure storage' description: 'Uploads to Azure storage'

View File

@@ -110,21 +110,6 @@ 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
nn-test: nn-test:
uses: ./.github/workflows/pipeline-segment-node-nan-test.yml uses: ./.github/workflows/pipeline-segment-node-nan-test.yml
permissions: permissions:

View File

@@ -30,14 +30,9 @@ 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 }}-${{ inputs.display-server }}-${{ github.ref_protected == true && github.run_id || github.ref }} group: electron-test-${{ inputs.target-platform }}-${{ inputs.target-arch }}-${{ inputs.is-asan }}-${{ 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: {}
@@ -64,7 +59,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: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }} shard: ${{ 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 }}
@@ -215,22 +210,7 @@ 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 }} ${{ case(inputs.display-server == 'wayland', 1, inputs.target-platform == 'linux', 3, 2) }}) tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ 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
@@ -265,11 +245,7 @@ jobs:
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 else
if [ "${{ inputs.display-server }}" = "wayland" ]; then 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 -- script/actions/run-tests-wayland.sh script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
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
fi
fi fi
fi fi
@@ -292,7 +268,7 @@ jobs:
if: always() && !cancelled() if: always() && !cancelled()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
with: with:
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) }} name: test_artifacts_${{ 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

View File

@@ -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: 'eac3529546ea8f3aa356d31e345715eef342233b' default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
required: true required: true
upload-to-storage: upload-to-storage:
description: 'Uploads to Azure storage' description: 'Uploads to Azure storage'

View File

@@ -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` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux. * `showHiddenFiles` - Show hidden files in dialog.
* `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` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux. * `showHiddenFiles` - Show hidden files in dialog.
* `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` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux. * `showHiddenFiles` - Show hidden files in dialog.
* `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` _macOS_ _Windows_ _Deprecated_ - Show hidden files in dialog. Deprecated on Linux. * `showHiddenFiles` - Show hidden files in dialog.
* `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.

View File

@@ -80,12 +80,6 @@ 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

View File

@@ -144,4 +144,3 @@ 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

View File

@@ -1,20 +0,0 @@
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>();

View File

@@ -1,30 +0,0 @@
#!/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

View File

@@ -1,4 +0,0 @@
spec/parse-features-string-spec.ts
spec/types-spec.ts
spec/version-bump-spec.ts
spec/api-app-spec.ts

View File

@@ -9,6 +9,7 @@
#include "base/environment.h" #include "base/environment.h"
#include "base/functional/bind.h" #include "base/functional/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h" #include "base/task/single_thread_task_runner.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
@@ -279,7 +280,8 @@ void OnDeploymentCompleted(std::unique_ptr<DeploymentCallbackData> data,
HRESULT error_code; HRESULT error_code;
hr = async_info->get_ErrorCode(&error_code); hr = async_info->get_ErrorCode(&error_code);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
error += " (" + std::to_string(static_cast<int>(error_code)) + ")"; error +=
" (" + base::NumberToString(static_cast<int>(error_code)) + ")";
} }
} }
} }
@@ -798,10 +800,10 @@ v8::Local<v8::Value> GetPackageInfo() {
ABI::Windows::ApplicationModel::PackageVersion pkg_version; ABI::Windows::ApplicationModel::PackageVersion pkg_version;
hr = package_id->get_Version(&pkg_version); hr = package_id->get_Version(&pkg_version);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
std::string version = std::to_string(pkg_version.Major) + "." + std::string version = base::NumberToString(pkg_version.Major) + "." +
std::to_string(pkg_version.Minor) + "." + base::NumberToString(pkg_version.Minor) + "." +
std::to_string(pkg_version.Build) + "." + base::NumberToString(pkg_version.Build) + "." +
std::to_string(pkg_version.Revision); base::NumberToString(pkg_version.Revision);
result.Set("version", version); result.Set("version", version);
} }
} }

View File

@@ -14,7 +14,6 @@
#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"
@@ -73,8 +72,7 @@ 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);
@@ -214,15 +212,32 @@ 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()));
params->url_loader_factory_params = CreateURLLoaderFactoryParams(); 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;
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() {
@@ -414,44 +429,6 @@ 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) {

View File

@@ -9,7 +9,6 @@
#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"
@@ -100,11 +99,6 @@ 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
@@ -117,14 +111,12 @@ 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};
}; };

View File

@@ -23,6 +23,7 @@
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/strings/strcat.h" #include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h" #include "base/task/current_thread.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
@@ -2659,7 +2660,7 @@ void WebContents::RestoreHistory(
thrower.ThrowError( thrower.ThrowError(
"Failed to restore navigation history: Invalid navigation entry at " "Failed to restore navigation history: Invalid navigation entry at "
"index " + "index " +
std::to_string(index) + "."); base::NumberToString(index) + ".");
return; return;
} }

View File

@@ -30,7 +30,6 @@
#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"
@@ -408,18 +407,10 @@ 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.
@@ -577,12 +568,6 @@ 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_)

View File

@@ -13,9 +13,7 @@
#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"
@@ -186,9 +184,6 @@ 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_;
@@ -212,9 +207,6 @@ 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_;
@@ -222,8 +214,6 @@ 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

View File

@@ -371,7 +371,7 @@ void HandleToastActivation(const std::wstring& invoked_args,
int action_index = -1; int action_index = -1;
if (!action_index_str.empty()) { if (!action_index_str.empty()) {
base::StringToInt(base::WideToUTF8(action_index_str), &action_index); action_index = std::stoi(action_index_str);
} }
std::string reply_text; std::string reply_text;

View File

@@ -5,12 +5,8 @@
#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"
@@ -45,18 +41,6 @@ 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,
@@ -65,7 +49,6 @@ 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

View File

@@ -484,7 +484,6 @@ 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
@@ -751,7 +750,6 @@ 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();
} }

View File

@@ -18,6 +18,7 @@
#include "base/environment.h" #include "base/environment.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h" #include "base/task/single_thread_task_runner.h"
@@ -192,7 +193,7 @@ void V8OOMErrorCallback(const char* location, const v8::OOMDetails& details) {
#if !IS_MAS_BUILD() #if !IS_MAS_BUILD()
electron::crash_keys::SetCrashKey("electron.v8-oom.is_heap_oom", electron::crash_keys::SetCrashKey("electron.v8-oom.is_heap_oom",
std::to_string(details.is_heap_oom)); base::NumberToString(details.is_heap_oom));
if (location) { if (location) {
electron::crash_keys::SetCrashKey("electron.v8-oom.location", location); electron::crash_keys::SetCrashKey("electron.v8-oom.location", location);
} }

View File

@@ -122,9 +122,10 @@ void NodeService::Initialize(
ParentPort::GetInstance()->Initialize(std::move(params->port)); ParentPort::GetInstance()->Initialize(std::move(params->port));
if (params->url_loader_factory_params) { URLLoaderBundle::GetInstance()->SetURLLoaderFactory(
UpdateURLLoaderFactory(std::move(params->url_loader_factory_params)); std::move(params->url_loader_factory),
} 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());
@@ -207,12 +208,4 @@ 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

View File

@@ -65,8 +65,6 @@ 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

View File

@@ -10,18 +10,14 @@ 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;
URLLoaderFactoryParams url_loader_factory_params; pending_remote<network.mojom.URLLoaderFactory> url_loader_factory;
pending_remote<network.mojom.HostResolver> host_resolver;
bool use_network_observer_from_url_loader_factory = false;
}; };
interface NodeServiceClient { interface NodeServiceClient {
@@ -32,6 +28,4 @@ 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);
}; };

View File

@@ -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, isWayland, listen, waitUntil } from './lib/spec-helpers'; import { ifdescribe, ifit, 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' && !isWayland)('should emit render-process-gone event when renderer crashes', async () => { ifit(process.platform !== 'win32')('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') && !isWayland)('returns application names for common protocols', function () { ifit(!(process.env.CI && process.platform === 'linux'))('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.

View File

@@ -1688,61 +1688,4 @@ 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');
});
});
}); });

View File

@@ -1,24 +0,0 @@
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' });

View File

@@ -25,12 +25,6 @@ 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 () {

View File

@@ -28,7 +28,7 @@ describe('node feature', () => {
expect(msg).to.equal('message'); expect(msg).to.equal('message');
}); });
it('Has its module search paths restricted', async () => { it('Has its module searth paths restricted', async () => {
const child = childProcess.fork(path.join(fixtures, 'module', 'module-paths.js')); const child = childProcess.fork(path.join(fixtures, 'module', 'module-paths.js'));
const [msg] = await once(child, 'message'); const [msg] = await once(child, 'message');
expect(msg.length).to.equal(2); expect(msg.length).to.equal(2);