mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
1 Commits
main
...
nikwen/ext
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
346d31f5e0 |
@@ -2,7 +2,7 @@ version: '3'
|
||||
|
||||
services:
|
||||
buildtools:
|
||||
image: ghcr.io/electron/devcontainer:eac3529546ea8f3aa356d31e345715eef342233b
|
||||
image: ghcr.io/electron/devcontainer:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
|
||||
volumes:
|
||||
- ..:/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:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /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:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
||||
volumes:
|
||||
- /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.
|
||||
needs: build-git-cache-linux
|
||||
container:
|
||||
image: ghcr.io/electron/build:eac3529546ea8f3aa356d31e345715eef342233b
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /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:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
skip-macos:
|
||||
type: boolean
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
id: set-output
|
||||
run: |
|
||||
if [ -z "${{ inputs.build-image-sha }}" ]; then
|
||||
echo "build-image-sha=eac3529546ea8f3aa356d31e345715eef342233b" >> "$GITHUB_OUTPUT"
|
||||
echo "build-image-sha=a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
2
.github/workflows/linux-publish.yml
vendored
2
.github/workflows/linux-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
required: false
|
||||
|
||||
2
.github/workflows/macos-publish.yml
vendored
2
.github/workflows/macos-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
|
||||
@@ -110,21 +110,6 @@ jobs:
|
||||
test-runs-on: ${{ inputs.test-runs-on }}
|
||||
test-container: ${{ inputs.test-container }}
|
||||
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:
|
||||
uses: ./.github/workflows/pipeline-segment-node-nan-test.yml
|
||||
permissions:
|
||||
|
||||
@@ -30,14 +30,9 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
display-server:
|
||||
description: 'Display backend for Linux tests: x11 or wayland'
|
||||
required: false
|
||||
type: string
|
||||
default: x11
|
||||
|
||||
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 }}
|
||||
|
||||
permissions: {}
|
||||
@@ -64,7 +59,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
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:
|
||||
BUILD_TYPE: ${{ matrix.build-type }}
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
@@ -215,22 +210,7 @@ jobs:
|
||||
cd src/electron
|
||||
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
|
||||
# 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) }})
|
||||
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
|
||||
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ inputs.target-platform == 'linux' && 3 || 2 }})
|
||||
|
||||
# Run tests
|
||||
if [ "${{ inputs.target-platform }}" != "linux" ]; then
|
||||
@@ -265,11 +245,7 @@ jobs:
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -292,7 +268,7 @@ jobs:
|
||||
if: always() && !cancelled()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
|
||||
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
|
||||
if-no-files-found: ignore
|
||||
- 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:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: 'eac3529546ea8f3aa356d31e345715eef342233b'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
|
||||
@@ -1556,6 +1556,19 @@ Enables full sandbox mode on the app. This means that all renderers will be laun
|
||||
|
||||
This method can only be called before app is ready.
|
||||
|
||||
### `app.enableExtensionsOnAllProtocols()`
|
||||
|
||||
Enables Chrome extensions on all protocols.
|
||||
|
||||
By default, Chrome extensions are enabled only for a select number of protocols
|
||||
such as `http`, `https`, and `file`. Calling this function will enable Chrome extensions
|
||||
on all protocols, including [custom protocols](protocol.md).
|
||||
|
||||
This can have security implications. For most apps, it is recommended to enable
|
||||
this during development only and keep it disabled in production builds.
|
||||
|
||||
This method can only be called before app is ready.
|
||||
|
||||
### `app.isInApplicationsFolder()` _macOS_
|
||||
|
||||
Returns `boolean` - Whether the application is currently running from the
|
||||
|
||||
@@ -30,7 +30,7 @@ The `dialog` module has the following methods:
|
||||
* `openFile` - Allow files to be selected.
|
||||
* `openDirectory` - Allow directories 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.
|
||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||
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.
|
||||
* `openDirectory` - Allow directories 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.
|
||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||
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,
|
||||
defaults to `true`.
|
||||
* `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.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
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.
|
||||
* `showsTagField` boolean (optional) _macOS_ - Show the tags input box, defaults to `true`.
|
||||
* `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.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a 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
|
||||
`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)
|
||||
|
||||
### Deprecated: `--host-rules` command line switch
|
||||
|
||||
@@ -144,4 +144,4 @@ fix_linux_tray_id.patch
|
||||
expose_gtk_ui_platform_field.patch
|
||||
patch_osr_control_screen_info.patch
|
||||
refactor_allow_customizing_config_in_freedesktopsecretkeyprovider.patch
|
||||
fix_wayland_test_crash_on_teardown.patch
|
||||
feat_allow_enabling_extensions_on_all_protocols.patch
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Wenzel <dev@nikwen.de>
|
||||
Date: Wed, 25 Feb 2026 16:24:03 +0100
|
||||
Subject: feat: allow enabling extensions on all protocols
|
||||
|
||||
This allows us to use Chrome extensions on custom protocols.
|
||||
|
||||
The patch can't really be upstreamed, unfortunately, because there are
|
||||
other URLPattern functions that we don't patch that Chrome needs.
|
||||
|
||||
Patching those properly would require replacing the bitmap logic in
|
||||
URLPattern with a more flexible solution. This would be a larger effort
|
||||
and Chromium might reject it for performance reasons.
|
||||
|
||||
See: https://source.chromium.org/chromium/chromium/src/+/main:extensions/common/url_pattern.h;l=53-74;drc=50dbcddad2f8e36ddfcec21d4551f389df425c37
|
||||
|
||||
This patch makes it work in the context of Electron.
|
||||
|
||||
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
|
||||
index 4054af728030306c5473f9a47e580595596768a0..38c3f5976a122e6e4b7e8512ef977242fa395d8d 100644
|
||||
--- a/extensions/common/url_pattern.cc
|
||||
+++ b/extensions/common/url_pattern.cc
|
||||
@@ -133,6 +133,13 @@ std::string_view CanonicalizeHostForMatching(std::string_view host_piece) {
|
||||
|
||||
} // namespace
|
||||
|
||||
+bool URLPattern::enable_extensions_on_all_protocols_ = false;
|
||||
+
|
||||
+// static
|
||||
+void URLPattern::EnableExtensionsOnAllProtocols() {
|
||||
+ enable_extensions_on_all_protocols_ = true;
|
||||
+}
|
||||
+
|
||||
// static
|
||||
bool URLPattern::IsValidSchemeForExtensions(std::string_view scheme) {
|
||||
for (auto* valid_scheme : kValidSchemes) {
|
||||
@@ -140,11 +147,14 @@ bool URLPattern::IsValidSchemeForExtensions(std::string_view scheme) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
- return false;
|
||||
+ return enable_extensions_on_all_protocols_;
|
||||
}
|
||||
|
||||
// static
|
||||
int URLPattern::GetValidSchemeMaskForExtensions() {
|
||||
+ if (enable_extensions_on_all_protocols_) {
|
||||
+ return SCHEME_ALL;
|
||||
+ }
|
||||
int result = 0;
|
||||
for (int valid_scheme_mask : kValidSchemeMasks) {
|
||||
result |= valid_scheme_mask;
|
||||
@@ -401,7 +411,7 @@ bool URLPattern::IsValidScheme(std::string_view scheme) const {
|
||||
}
|
||||
}
|
||||
|
||||
- return false;
|
||||
+ return enable_extensions_on_all_protocols_;
|
||||
}
|
||||
|
||||
void URLPattern::SetPath(std::string_view path) {
|
||||
diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h
|
||||
index 4d09251b0160644d86682ad3db7c41b50f360e6f..78978b82e37e080add6680300d09503acdb663db 100644
|
||||
--- a/extensions/common/url_pattern.h
|
||||
+++ b/extensions/common/url_pattern.h
|
||||
@@ -96,6 +96,8 @@ class URLPattern {
|
||||
// Returns the mask for all schemes considered valid for extensions.
|
||||
static int GetValidSchemeMaskForExtensions();
|
||||
|
||||
+ static void EnableExtensionsOnAllProtocols();
|
||||
+
|
||||
explicit URLPattern(int valid_schemes);
|
||||
|
||||
// Convenience to construct a URLPattern from a string. If the string is not
|
||||
@@ -251,6 +253,9 @@ class URLPattern {
|
||||
// Get an error string for a ParseResult.
|
||||
static const char* GetParseResultString(URLPattern::ParseResult parse_result);
|
||||
|
||||
+ protected:
|
||||
+ static bool enable_extensions_on_all_protocols_;
|
||||
+
|
||||
private:
|
||||
// Returns true if any of the `schemes` items matches our scheme.
|
||||
bool MatchesAnyScheme(const std::vector<std::string>& schemes) const;
|
||||
@@ -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>();
|
||||
@@ -1 +1,2 @@
|
||||
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
|
||||
feat_allow_enabling_extension_panels_on_all_protocols.patch
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Wenzel <dev@nikwen.de>
|
||||
Date: Wed, 25 Feb 2026 16:23:07 +0100
|
||||
Subject: feat: allow enabling extension panels on all protocols
|
||||
|
||||
This allows us to show Chrome extension panels on pages served over
|
||||
custom protocols.
|
||||
|
||||
diff --git a/front_end/core/root/Runtime.ts b/front_end/core/root/Runtime.ts
|
||||
index 19824217973f002a52478c7fa63a3faa217b0c63..6406fff61691fab0d2a8cc5344aaee743937c84e 100644
|
||||
--- a/front_end/core/root/Runtime.ts
|
||||
+++ b/front_end/core/root/Runtime.ts
|
||||
@@ -639,6 +639,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
|
||||
* or guest mode, rather than a "normal" profile.
|
||||
*/
|
||||
isOffTheRecord: boolean,
|
||||
+ devToolsExtensionsOnAllProtocols: boolean,
|
||||
devToolsEnableOriginBoundCookies: HostConfigEnableOriginBoundCookies,
|
||||
devToolsAnimationStylesInStylesTab: HostConfigAnimationStylesInStylesTab,
|
||||
thirdPartyCookieControls: HostConfigThirdPartyCookieControls,
|
||||
diff --git a/front_end/panels/common/ExtensionServer.ts b/front_end/panels/common/ExtensionServer.ts
|
||||
index 0a5ec620b135b128013d6ddbb5299f9a5813f122..1a6118b4fa1607a634720b5579a50d1b4478b60a 100644
|
||||
--- a/front_end/panels/common/ExtensionServer.ts
|
||||
+++ b/front_end/panels/common/ExtensionServer.ts
|
||||
@@ -12,6 +12,7 @@ import * as Host from '../../core/host/host.js';
|
||||
import * as i18n from '../../core/i18n/i18n.js';
|
||||
import * as Platform from '../../core/platform/platform.js';
|
||||
import * as _ProtocolClient from '../../core/protocol_client/protocol_client.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
+import * as Root from '../../core/root/root.js';
|
||||
import * as SDK from '../../core/sdk/sdk.js';
|
||||
import type * as Protocol from '../../generated/protocol.js';
|
||||
import * as Bindings from '../../models/bindings/bindings.js';
|
||||
@@ -1607,7 +1608,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (!kPermittedSchemes.includes(parsedURL.protocol)) {
|
||||
+ if (!Root.Runtime.hostConfig.devToolsExtensionsOnAllProtocols && !kPermittedSchemes.includes(parsedURL.protocol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +0,0 @@
|
||||
spec/parse-features-string-spec.ts
|
||||
spec/types-spec.ts
|
||||
spec/version-bump-spec.ts
|
||||
spec/api-app-spec.ts
|
||||
@@ -86,6 +86,10 @@
|
||||
#include "v8/include/cppgc/allocation.h"
|
||||
#include "v8/include/v8-traced-handle.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
#include "extensions/common/url_pattern.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "shell/browser/notifications/win/windows_toast_activator.h"
|
||||
@@ -1546,6 +1550,28 @@ void App::EnableSandbox(gin_helper::ErrorThrower thrower) {
|
||||
command_line->AppendSwitch(switches::kEnableSandbox);
|
||||
}
|
||||
|
||||
void App::EnableExtensionsOnAllProtocols(gin_helper::ErrorThrower thrower) {
|
||||
if (Browser::Get()->is_ready()) {
|
||||
thrower.ThrowError(
|
||||
"app.enableExtensionsOnAllProtocols() can only be called "
|
||||
"before app is ready");
|
||||
return;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
enable_extensions_on_all_protocols_ = true;
|
||||
URLPattern::EnableExtensionsOnAllProtocols();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool App::AreExtensionsEnabledOnAllProtocols() const {
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
return enable_extensions_on_all_protocols_;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> App::SetProxy(gin::Arguments* args) {
|
||||
v8::Isolate* isolate = args->isolate();
|
||||
gin_helper::Promise<void> promise(isolate);
|
||||
@@ -1949,6 +1975,8 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||
&App::IsHardwareAccelerationEnabled)
|
||||
.SetMethod("disableDomainBlockingFor3DAPIs",
|
||||
&App::DisableDomainBlockingFor3DAPIs)
|
||||
.SetMethod("enableExtensionsOnAllProtocols",
|
||||
&App::EnableExtensionsOnAllProtocols)
|
||||
.SetMethod("getFileIcon", &App::GetFileIcon)
|
||||
.SetMethod("getAppMetrics", &App::GetAppMetrics)
|
||||
.SetMethod("getGPUFeatureStatus", &App::GetGPUFeatureStatus)
|
||||
|
||||
@@ -89,6 +89,8 @@ class App final : public gin::Wrappable<App>,
|
||||
|
||||
static bool IsPackaged();
|
||||
|
||||
bool AreExtensionsEnabledOnAllProtocols() const;
|
||||
|
||||
App();
|
||||
~App() override;
|
||||
|
||||
@@ -236,6 +238,7 @@ class App final : public gin::Wrappable<App>,
|
||||
v8::Local<v8::Promise> GetGPUInfo(v8::Isolate* isolate,
|
||||
const std::string& info_type);
|
||||
void EnableSandbox(gin_helper::ErrorThrower thrower);
|
||||
void EnableExtensionsOnAllProtocols(gin_helper::ErrorThrower thrower);
|
||||
void SetUserAgentFallback(const std::string& user_agent);
|
||||
std::string GetUserAgentFallback();
|
||||
v8::Local<v8::Promise> SetProxy(gin::Arguments* args);
|
||||
@@ -293,6 +296,10 @@ class App final : public gin::Wrappable<App>,
|
||||
bool disable_domain_blocking_for_3DAPIs_ = false;
|
||||
bool watch_singleton_socket_on_ready_ = false;
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
bool enable_extensions_on_all_protocols_ = false;
|
||||
#endif
|
||||
|
||||
std::unique_ptr<content::ScopedAccessibilityMode> scoped_accessibility_mode_;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "base/process/launch.h"
|
||||
#include "base/process/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/service_process_host.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
@@ -73,8 +72,7 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
||||
base::FilePath current_working_directory,
|
||||
bool use_plugin_helper,
|
||||
bool create_network_observer,
|
||||
bool disclaim_responsibility)
|
||||
: create_network_observer_(create_network_observer) {
|
||||
bool disclaim_responsibility) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
base::win::ScopedHandle stdout_write(nullptr);
|
||||
base::win::ScopedHandle stderr_write(nullptr);
|
||||
@@ -214,15 +212,32 @@ UtilityProcessWrapper::UtilityProcessWrapper(
|
||||
connector_->set_connection_error_handler(base::BindOnce(
|
||||
&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),
|
||||
receiver_.BindNewPipeAndPassRemote());
|
||||
|
||||
// Subscribe to Network Service process gone notifications.
|
||||
network_service_gone_subscription_ =
|
||||
content::RegisterNetworkServiceProcessGoneHandler(base::BindRepeating(
|
||||
&UtilityProcessWrapper::CreateAndSendURLLoaderFactory,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
UtilityProcessWrapper::~UtilityProcessWrapper() {
|
||||
@@ -414,44 +429,6 @@ void UtilityProcessWrapper::OnV8FatalError(const std::string& location,
|
||||
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
|
||||
raw_ptr<UtilityProcessWrapper> UtilityProcessWrapper::FromProcessId(
|
||||
base::ProcessId pid) {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/callback_list.h"
|
||||
#include "base/containers/id_map.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
@@ -100,11 +99,6 @@ class UtilityProcessWrapper final
|
||||
void OnServiceProcessDisconnected(uint32_t exit_code,
|
||||
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;
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Non-owning handles, these will be closed when the
|
||||
@@ -117,14 +111,12 @@ class UtilityProcessWrapper final
|
||||
bool connector_closed_ = false;
|
||||
bool terminated_ = false;
|
||||
bool killed_ = false;
|
||||
bool create_network_observer_ = false;
|
||||
std::unique_ptr<mojo::Connector> connector_;
|
||||
blink::MessagePortDescriptor host_port_;
|
||||
mojo::Receiver<node::mojom::NodeServiceClient> receiver_{this};
|
||||
mojo::Remote<node::mojom::NodeService> node_service_remote_;
|
||||
std::optional<electron::URLLoaderNetworkObserver>
|
||||
url_loader_network_observer_;
|
||||
base::CallbackListSubscription network_service_gone_subscription_;
|
||||
base::WeakPtrFactory<UtilityProcessWrapper> weak_factory_{this};
|
||||
};
|
||||
|
||||
|
||||
@@ -617,6 +617,12 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
command_line->AppendSwitch(switches::kServiceWorkerPreload);
|
||||
}
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
if (api::App::Get()->AreExtensionsEnabledOnAllProtocols()) {
|
||||
command_line->AppendSwitch(switches::kEnableExtensionsOnAllProtocols);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
|
||||
#include "components/proxy_config/proxy_config_pref_names.h"
|
||||
#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/cors_origin_pattern_setter.h"
|
||||
#include "content/public/browser/host_zoom_map.h"
|
||||
@@ -408,18 +407,10 @@ ElectronBrowserContext::ElectronBrowserContext(
|
||||
extension_system->FinishInitialization();
|
||||
}
|
||||
#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() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
NotifyWillBeDestroyed();
|
||||
|
||||
// Notify any keyed services of browser context destruction.
|
||||
@@ -577,12 +568,6 @@ content::PreconnectManager* ElectronBrowserContext::GetPreconnectManager() {
|
||||
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>
|
||||
ElectronBrowserContext::GetURLLoaderFactory() {
|
||||
if (url_loader_factory_)
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback_list.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/media_stream_request.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
@@ -186,9 +184,6 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
// Initialize pref registry.
|
||||
void InitPrefs();
|
||||
|
||||
// Called when the Network Service process crashes or restarts.
|
||||
void OnNetworkServiceProcessGone(bool crashed);
|
||||
|
||||
scoped_refptr<ValueMapPrefStore> in_memory_pref_store_;
|
||||
std::unique_ptr<CookieChangeNotifier> cookie_change_notifier_;
|
||||
std::unique_ptr<PrefService> prefs_;
|
||||
@@ -212,9 +207,6 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
// Shared URLLoaderFactory.
|
||||
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_;
|
||||
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.
|
||||
DevicePermissionMap granted_devices_;
|
||||
|
||||
base::WeakPtrFactory<ElectronBrowserContext> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -371,7 +371,7 @@ void HandleToastActivation(const std::wstring& invoked_args,
|
||||
|
||||
int action_index = -1;
|
||||
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;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "services/network/public/cpp/simple_url_loader_stream_consumer.h"
|
||||
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
|
||||
#include "services/network/public/mojom/url_response_head.mojom.h"
|
||||
#include "shell/browser/api/electron_api_app.h"
|
||||
#include "shell/browser/api/electron_api_web_contents.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/net/asar/asar_url_loader_factory.h"
|
||||
@@ -865,6 +866,8 @@ void InspectableWebContents::GetSyncInformation(DispatchCallback callback) {
|
||||
|
||||
void InspectableWebContents::GetHostConfig(DispatchCallback callback) {
|
||||
base::DictValue response_dict;
|
||||
response_dict.Set("devToolsExtensionsOnAllProtocols",
|
||||
api::App::Get()->AreExtensionsEnabledOnAllProtocols());
|
||||
base::Value response = base::Value(std::move(response_dict));
|
||||
std::move(callback).Run(&response);
|
||||
}
|
||||
|
||||
@@ -5,12 +5,8 @@
|
||||
#include "base/command_line.h"
|
||||
#include "base/dcheck_is_on.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 "shell/common/callback_util.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
@@ -45,18 +41,6 @@ std::string GetLoggingDestination() {
|
||||
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,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
@@ -65,7 +49,6 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
gin_helper::Dictionary dict{isolate, exports};
|
||||
dict.SetMethod("log", &Log);
|
||||
dict.SetMethod("getLoggingDestination", &GetLoggingDestination);
|
||||
dict.SetMethod("simulateNetworkServiceCrash", &SimulateNetworkServiceCrash);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -484,7 +484,6 @@ void SimpleURLLoaderWrapper::Clone(
|
||||
|
||||
void SimpleURLLoaderWrapper::Cancel() {
|
||||
loader_.reset();
|
||||
url_loader_factory_.reset();
|
||||
pinned_wrapper_.Reset();
|
||||
pinned_chunk_pipe_getter_.Reset();
|
||||
// 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.
|
||||
if (self) {
|
||||
loader_.reset();
|
||||
url_loader_factory_.reset();
|
||||
pinned_wrapper_.Reset();
|
||||
pinned_chunk_pipe_getter_.Reset();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string_view>
|
||||
|
||||
#include "base/strings/cstring_view.h"
|
||||
#include "electron/buildflags/buildflags.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -270,6 +271,12 @@ inline constexpr base::cstring_view kStreamingSchemes = "streaming-schemes";
|
||||
// Register schemes as supporting V8 code cache.
|
||||
inline constexpr base::cstring_view kCodeCacheSchemes = "code-cache-schemes";
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// Enable Chrome extensions on all protocols.
|
||||
inline constexpr base::cstring_view kEnableExtensionsOnAllProtocols =
|
||||
"enable-extensions-on-all-protocols";
|
||||
#endif
|
||||
|
||||
// The browser process app model ID
|
||||
inline constexpr base::cstring_view kAppUserModelId = "app-user-model-id";
|
||||
|
||||
|
||||
@@ -162,6 +162,11 @@ RendererClientBase::RendererClientBase() {
|
||||
ParseSchemesCLISwitch(command_line, switches::kSecureSchemes);
|
||||
for (const std::string& scheme : secure_schemes_list)
|
||||
url::AddSecureScheme(scheme.data());
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
// Parse --enable-extensions-on-all-protocols
|
||||
if (command_line->HasSwitch(switches::kEnableExtensionsOnAllProtocols))
|
||||
URLPattern::EnableExtensionsOnAllProtocols();
|
||||
#endif
|
||||
// We rely on the unique process host id which is notified to the
|
||||
// renderer process via command line switch from the content layer,
|
||||
// if this switch is removed from the content layer for some reason,
|
||||
|
||||
@@ -122,9 +122,10 @@ void NodeService::Initialize(
|
||||
|
||||
ParentPort::GetInstance()->Initialize(std::move(params->port));
|
||||
|
||||
if (params->url_loader_factory_params) {
|
||||
UpdateURLLoaderFactory(std::move(params->url_loader_factory_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);
|
||||
|
||||
js_env_ = std::make_unique<JavascriptEnvironment>(node_bindings_->uv_loop());
|
||||
|
||||
@@ -207,12 +208,4 @@ void NodeService::Initialize(
|
||||
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
|
||||
|
||||
@@ -65,8 +65,6 @@ class NodeService : public node::mojom::NodeService {
|
||||
void Initialize(node::mojom::NodeServiceParamsPtr params,
|
||||
mojo::PendingRemote<node::mojom::NodeServiceClient>
|
||||
client_pending_remote) override;
|
||||
void UpdateURLLoaderFactory(
|
||||
node::mojom::URLLoaderFactoryParamsPtr params) override;
|
||||
|
||||
private:
|
||||
// This needs to be initialized first so that it can be destroyed last
|
||||
|
||||
@@ -10,18 +10,14 @@ import "services/network/public/mojom/host_resolver.mojom";
|
||||
import "services/network/public/mojom/url_loader_factory.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 {
|
||||
mojo_base.mojom.FilePath script;
|
||||
array<string> args;
|
||||
array<string> exec_args;
|
||||
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 {
|
||||
@@ -32,6 +28,4 @@ interface NodeServiceClient {
|
||||
interface NodeService {
|
||||
Initialize(NodeServiceParams params,
|
||||
pending_remote<NodeServiceClient> client_remote);
|
||||
|
||||
UpdateURLLoaderFactory(URLLoaderFactoryParams params);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { setTimeout } from 'node:timers/promises';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
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';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
@@ -587,7 +587,7 @@ describe('app module', () => {
|
||||
});
|
||||
|
||||
// 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({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
@@ -1437,7 +1437,7 @@ describe('app module', () => {
|
||||
|
||||
describe('getApplicationNameForProtocol()', () => {
|
||||
// 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
|
||||
// at least have _something_ registered. Except on our Linux CI
|
||||
// environment apparently.
|
||||
@@ -1765,6 +1765,15 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('enableExtensionsOnAllProtocols() API', () => {
|
||||
// Proper tests are in extensions-spec.ts
|
||||
it('throws when called after app is ready', () => {
|
||||
expect(() => {
|
||||
app.enableExtensionsOnAllProtocols();
|
||||
}).to.throw(/before app is ready/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('disableDomainBlockingFor3DAPIs() API', () => {
|
||||
it('throws when called after app is ready', () => {
|
||||
expect(() => {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import { app, session, webFrameMain, BrowserWindow, ipcMain, WebContents, Extens
|
||||
import { expect } from 'chai';
|
||||
import * as WebSocket from 'ws';
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import { once } from 'node:events';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import * as http from 'node:http';
|
||||
@@ -1338,4 +1339,26 @@ describe('chrome extensions', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom protocol', () => {
|
||||
async function runFixture (name: string) {
|
||||
const appProcess = spawn(process.execPath, [(path.join(fixtures, 'extensions', name, 'main.js'))]);
|
||||
|
||||
let output = '';
|
||||
appProcess.stdout.on('data', (data) => { output += data; });
|
||||
await once(appProcess.stdout, 'end');
|
||||
|
||||
return output.trim();
|
||||
};
|
||||
|
||||
it('loads DevTools extensions on custom protocols with app.enableExtensionsOnAllProtocols() and runs content and background scripts', async () => {
|
||||
const output = await runFixture('custom-protocol');
|
||||
expect(output).to.equal('Title: MESSAGE RECEIVED');
|
||||
});
|
||||
|
||||
it('loads DevTools panels on custom protocols with app.enableExtensionsOnAllProtocols()', async () => {
|
||||
const output = await runFixture('custom-protocol-panel');
|
||||
expect(output).to.equal('ELECTRON TEST PANEL created');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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' });
|
||||
7
spec/fixtures/extensions/custom-protocol-panel/extension/devtools.html
vendored
Normal file
7
spec/fixtures/extensions/custom-protocol-panel/extension/devtools.html
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="devtools.js"></script>
|
||||
</head>
|
||||
</html>
|
||||
4
spec/fixtures/extensions/custom-protocol-panel/extension/devtools.js
vendored
Normal file
4
spec/fixtures/extensions/custom-protocol-panel/extension/devtools.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/* global chrome */
|
||||
chrome.devtools.panels.create('ELECTRON TEST PANEL', '', 'panel.html');
|
||||
|
||||
console.log('ELECTRON TEST PANEL created');
|
||||
6
spec/fixtures/extensions/custom-protocol-panel/extension/manifest.json
vendored
Normal file
6
spec/fixtures/extensions/custom-protocol-panel/extension/manifest.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "custom-protocol-panel",
|
||||
"version": "1.0",
|
||||
"devtools_page": "devtools.html",
|
||||
"manifest_version": 3
|
||||
}
|
||||
4
spec/fixtures/extensions/custom-protocol-panel/extension/panel.html
vendored
Normal file
4
spec/fixtures/extensions/custom-protocol-panel/extension/panel.html
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<!doctype html>
|
||||
<body>
|
||||
DevTools panel
|
||||
</body>
|
||||
48
spec/fixtures/extensions/custom-protocol-panel/main.js
vendored
Normal file
48
spec/fixtures/extensions/custom-protocol-panel/main.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
const { app, BrowserWindow, protocol, session } = require('electron/main');
|
||||
|
||||
const { once } = require('node:events');
|
||||
const path = require('node:path');
|
||||
|
||||
const html = '<html><body><h1>EMPTY PAGE</h1></body></html>';
|
||||
const scheme = 'custom';
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme,
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
allowServiceWorkers: true,
|
||||
supportFetchAPI: true,
|
||||
bypassCSP: false,
|
||||
corsEnabled: true,
|
||||
stream: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
app.enableExtensionsOnAllProtocols();
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
const ses = session.defaultSession;
|
||||
|
||||
ses.protocol.handle(scheme, () => new Response(html, {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
}));
|
||||
|
||||
await ses.extensions.loadExtension(path.join(__dirname, 'extension'));
|
||||
|
||||
const win = new BrowserWindow();
|
||||
|
||||
win.webContents.openDevTools();
|
||||
await once(win.webContents, 'devtools-opened');
|
||||
|
||||
win.devToolsWebContents.on('console-message', ({ message }) => {
|
||||
if (message === 'ELECTRON TEST PANEL created') {
|
||||
console.log(message);
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
await win.loadURL(`${scheme}://app/`);
|
||||
});
|
||||
7
spec/fixtures/extensions/custom-protocol/extension/background.js
vendored
Normal file
7
spec/fixtures/extensions/custom-protocol/extension/background.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/* global chrome */
|
||||
chrome.runtime.onMessage.addListener((_message, sender, reply) => {
|
||||
reply({
|
||||
text: 'MESSAGE RECEIVED',
|
||||
senderTabId: sender.tab && sender.tab.id
|
||||
});
|
||||
});
|
||||
5
spec/fixtures/extensions/custom-protocol/extension/content_script.js
vendored
Normal file
5
spec/fixtures/extensions/custom-protocol/extension/content_script.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/* global chrome */
|
||||
chrome.runtime.sendMessage({ text: 'hello from content script' }, (response) => {
|
||||
if (!response || !response.text) return;
|
||||
document.title = response.text;
|
||||
});
|
||||
15
spec/fixtures/extensions/custom-protocol/extension/manifest.json
vendored
Normal file
15
spec/fixtures/extensions/custom-protocol/extension/manifest.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "custom-protocol",
|
||||
"version": "1.0",
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["content_script.js"],
|
||||
"run_at": "document_start"
|
||||
}
|
||||
],
|
||||
"manifest_version": 3
|
||||
}
|
||||
42
spec/fixtures/extensions/custom-protocol/main.js
vendored
Normal file
42
spec/fixtures/extensions/custom-protocol/main.js
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
const { app, BrowserWindow, protocol, session } = require('electron/main');
|
||||
|
||||
const path = require('node:path');
|
||||
|
||||
const html = '<html><body><h1>EMPTY PAGE</h1></body></html>';
|
||||
const scheme = 'example';
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme,
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
allowServiceWorkers: true,
|
||||
supportFetchAPI: true,
|
||||
bypassCSP: false,
|
||||
corsEnabled: true,
|
||||
stream: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
app.enableExtensionsOnAllProtocols();
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
const ses = session.defaultSession;
|
||||
|
||||
ses.protocol.handle(scheme, () => new Response(html, {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
}));
|
||||
|
||||
await ses.extensions.loadExtension(path.join(__dirname, 'extension'));
|
||||
|
||||
const win = new BrowserWindow();
|
||||
|
||||
win.on('page-title-updated', (_event, title) => {
|
||||
console.log(`Title: ${title}`);
|
||||
app.quit();
|
||||
});
|
||||
|
||||
await win.loadURL(`${scheme}://app/`);
|
||||
});
|
||||
@@ -25,12 +25,6 @@ const addOnly = <T>(fn: Function): T => {
|
||||
export const ifit = (condition: boolean) => (condition ? it : addOnly<TestFunction>(it.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>)
|
||||
const cleanupFunctions: CleanupFunction[] = [];
|
||||
export async function runCleanupFunctions () {
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('node feature', () => {
|
||||
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 [msg] = await once(child, 'message');
|
||||
expect(msg.length).to.equal(2);
|
||||
|
||||
Reference in New Issue
Block a user