mirror of
https://github.com/electron/electron.git
synced 2026-03-19 03:02:02 -04:00
Compare commits
3 Commits
dispositio
...
nikwen/ext
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce4a25520 | ||
|
|
3741b3b00a | ||
|
|
6f5f6895e0 |
@@ -1560,6 +1560,20 @@ 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, so enable carefully (e.g., only during development).
|
||||
|
||||
Once enabled, the feature cannot be disabled again until the app is restarted.
|
||||
|
||||
This method can only be called before app is ready.
|
||||
|
||||
### `app.isInApplicationsFolder()` _macOS_
|
||||
|
||||
Returns `boolean` - Whether the application is currently running from the
|
||||
|
||||
@@ -148,3 +148,4 @@ fix_wayland_test_crash_on_teardown.patch
|
||||
fix_set_correct_app_id_on_linux.patch
|
||||
fix_pass_trigger_for_global_shortcuts_on_wayland.patch
|
||||
feat_plumb_node_integration_in_worker_through_workersettings.patch
|
||||
feat_allow_enabling_extensions_on_all_protocols.patch
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
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/browser/api/content_settings/content_settings_helpers.cc b/extensions/browser/api/content_settings/content_settings_helpers.cc
|
||||
index ea484a282d820da78e8dc1db27ad0ba6e070ac2c..6109f86f3b6ad8051473b409251bd4384d3af7e2 100644
|
||||
--- a/extensions/browser/api/content_settings/content_settings_helpers.cc
|
||||
+++ b/extensions/browser/api/content_settings/content_settings_helpers.cc
|
||||
@@ -37,7 +37,7 @@ ContentSettingsPattern ParseExtensionPattern(std::string_view pattern_str,
|
||||
std::string* error) {
|
||||
const int kAllowedSchemes =
|
||||
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
|
||||
- URLPattern::SCHEME_FILE;
|
||||
+ URLPattern::SCHEME_FILE | URLPattern::SCHEME_ELECTRON_OTHER;
|
||||
URLPattern url_pattern(kAllowedSchemes);
|
||||
URLPattern::ParseResult result = url_pattern.Parse(pattern_str);
|
||||
if (result != URLPattern::ParseResult::kSuccess) {
|
||||
diff --git a/extensions/browser/api/web_request/extension_web_request_event_router.h b/extensions/browser/api/web_request/extension_web_request_event_router.h
|
||||
index 57ed3cf54b2921df09ad84906b3da7527c6080bb..29067792f0168e4df7f7aeb8114003acf4b92061 100644
|
||||
--- a/extensions/browser/api/web_request/extension_web_request_event_router.h
|
||||
+++ b/extensions/browser/api/web_request/extension_web_request_event_router.h
|
||||
@@ -53,7 +53,8 @@ inline constexpr int kWebRequestFilterValidSchemes =
|
||||
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
|
||||
URLPattern::SCHEME_FTP | URLPattern::SCHEME_FILE |
|
||||
URLPattern::SCHEME_EXTENSION | URLPattern::SCHEME_WS |
|
||||
- URLPattern::SCHEME_WSS | URLPattern::SCHEME_UUID_IN_PACKAGE;
|
||||
+ URLPattern::SCHEME_WSS | URLPattern::SCHEME_UUID_IN_PACKAGE |
|
||||
+ URLPattern::SCHEME_ELECTRON_OTHER;
|
||||
|
||||
class WebRequestEventRouter : public KeyedService {
|
||||
public:
|
||||
diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc
|
||||
index 0e0152871689c51d4e00f39f6ad607da90e6c9be..4092c07b75c7fcaab7483dece0fe562f3d8c6906 100644
|
||||
--- a/extensions/common/extension.cc
|
||||
+++ b/extensions/common/extension.cc
|
||||
@@ -220,7 +220,7 @@ const int Extension::kValidHostPermissionSchemes =
|
||||
URLPattern::SCHEME_CHROMEUI | URLPattern::SCHEME_HTTP |
|
||||
URLPattern::SCHEME_HTTPS | URLPattern::SCHEME_FILE |
|
||||
URLPattern::SCHEME_FTP | URLPattern::SCHEME_WS | URLPattern::SCHEME_WSS |
|
||||
- URLPattern::SCHEME_UUID_IN_PACKAGE;
|
||||
+ URLPattern::SCHEME_UUID_IN_PACKAGE | URLPattern::SCHEME_ELECTRON_OTHER;
|
||||
|
||||
//
|
||||
// Extension
|
||||
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
|
||||
index d4328ca22fdeefd3dca88bfe959dfb849705b109..27278300ddd806c46f131f19256a381047941d9e 100644
|
||||
--- a/extensions/common/url_pattern.cc
|
||||
+++ b/extensions/common/url_pattern.cc
|
||||
@@ -133,8 +133,18 @@ 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) {
|
||||
+ if (enable_extensions_on_all_protocols_) {
|
||||
+ return true;
|
||||
+ }
|
||||
for (auto* valid_scheme : kValidSchemes) {
|
||||
if (scheme == valid_scheme) {
|
||||
return true;
|
||||
@@ -145,6 +155,9 @@ bool URLPattern::IsValidSchemeForExtensions(std::string_view scheme) {
|
||||
|
||||
// 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,6 +414,10 @@ bool URLPattern::IsValidScheme(std::string_view scheme) const {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (enable_extensions_on_all_protocols_) {
|
||||
+ return valid_schemes_ & URLPattern::SCHEME_ELECTRON_OTHER;
|
||||
+ }
|
||||
+
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h
|
||||
index 4d09251b0160644d86682ad3db7c41b50f360e6f..cda8558b176e807c8cf77900d22ba90e2f8ad3fb 100644
|
||||
--- a/extensions/common/url_pattern.h
|
||||
+++ b/extensions/common/url_pattern.h
|
||||
@@ -64,6 +64,9 @@ class URLPattern {
|
||||
SCHEME_DATA = 1 << 9,
|
||||
SCHEME_UUID_IN_PACKAGE = 1 << 10,
|
||||
|
||||
+ // Represents all other schemes that are not mentioned above.
|
||||
+ SCHEME_ELECTRON_OTHER = 1 << 11,
|
||||
+
|
||||
// IMPORTANT!
|
||||
// SCHEME_ALL will match every scheme, including chrome://, chrome-
|
||||
// extension://, about:, etc. Because this has lots of security
|
||||
@@ -96,6 +99,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 +256,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;
|
||||
diff --git a/extensions/common/user_script.cc b/extensions/common/user_script.cc
|
||||
index f680ef4d31d580a285abe51387e3df043d4458f1..15714b79ef49a90b13e5534e25e6492e01c00447 100644
|
||||
--- a/extensions/common/user_script.cc
|
||||
+++ b/extensions/common/user_script.cc
|
||||
@@ -69,7 +69,8 @@ enum {
|
||||
kValidUserScriptSchemes = URLPattern::SCHEME_CHROMEUI |
|
||||
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
|
||||
URLPattern::SCHEME_FILE | URLPattern::SCHEME_FTP |
|
||||
- URLPattern::SCHEME_UUID_IN_PACKAGE
|
||||
+ URLPattern::SCHEME_UUID_IN_PACKAGE |
|
||||
+ URLPattern::SCHEME_ELECTRON_OTHER
|
||||
};
|
||||
|
||||
// static
|
||||
@@ -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 2e212d3da32a6b3954b5e1b41f54d494268124e2..66766fefd08338ee1e5d2a1501b7be12ae3803e1 100644
|
||||
--- a/front_end/core/root/Runtime.ts
|
||||
+++ b/front_end/core/root/Runtime.ts
|
||||
@@ -643,6 +643,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
|
||||
* or guest mode, rather than a "normal" profile.
|
||||
*/
|
||||
isOffTheRecord: boolean,
|
||||
+ devToolsExtensionsOnAllProtocols: boolean,
|
||||
devToolsEnableOriginBoundCookies: HostConfigEnableOriginBoundCookies,
|
||||
devToolsAnimationStylesInStylesTab: HostConfigAnimationStylesInStylesTab,
|
||||
devToolsJpegXlImageFormat: HostConfigJpegXlImageFormat,
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -1950,6 +1976,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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -869,6 +870,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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1788,6 +1788,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(() => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import * as qs from 'node:querystring';
|
||||
import { ReadableStream } from 'node:stream/web';
|
||||
import * as url from 'node:url';
|
||||
|
||||
import { listen, defer } from './lib/spec-helpers';
|
||||
import { listen, defer, startRemoteControlApp } from './lib/spec-helpers';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
|
||||
@@ -173,6 +173,108 @@ describe('webRequest module', () => {
|
||||
expect((await ajax(`${defaultURL}exclude/test`)).data).to.equal('/exclude/test');
|
||||
});
|
||||
|
||||
describe('with app.enableExtensionsOnAllProtocols()', () => {
|
||||
it('will filter http URLs properly', async () => {
|
||||
const rc = await startRemoteControlApp(['--boot-eval="app.enableExtensionsOnAllProtocols();"']);
|
||||
const called = await rc.remotely(async (url: string) => {
|
||||
const { BrowserWindow, session } = require('electron/main');
|
||||
|
||||
let called = false;
|
||||
|
||||
session.defaultSession.webRequest.onBeforeRequest({ urls: ['http://*/*'] }, (_: Electron.OnBeforeRequestListenerDetails, callback: (response: Electron.CallbackResponse) => void) => {
|
||||
called = true;
|
||||
callback({ cancel: true });
|
||||
});
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
await w.webContents.executeJavaScript(`fetch("${url}").then(() => true, () => false)`);
|
||||
|
||||
global.setTimeout(() => require('electron').app.quit());
|
||||
|
||||
return called;
|
||||
}, defaultURL);
|
||||
expect(called).to.be.true();
|
||||
});
|
||||
|
||||
it('will not call webRequest.onBeforeRequest for non-custom protocol URLs that do not match the filter', async () => {
|
||||
const rc = await startRemoteControlApp(['--boot-eval="app.enableExtensionsOnAllProtocols();"']);
|
||||
const called = await rc.remotely(async (url: string) => {
|
||||
const { BrowserWindow, session } = require('electron/main');
|
||||
|
||||
let called = false;
|
||||
|
||||
session.defaultSession.webRequest.onBeforeRequest({ urls: ['https://*/*'] }, (_: Electron.OnBeforeRequestListenerDetails, callback: (response: Electron.CallbackResponse) => void) => {
|
||||
called = true;
|
||||
callback({ cancel: true });
|
||||
});
|
||||
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
await w.webContents.executeJavaScript(`fetch("${url}").then(() => true, () => false)`);
|
||||
|
||||
global.setTimeout(() => require('electron').app.quit());
|
||||
|
||||
return called;
|
||||
}, defaultURL);
|
||||
expect(called).to.be.false();
|
||||
});
|
||||
|
||||
// TODO(nikwen): Enable when https://github.com/electron/electron/pull/45915 has been merged.
|
||||
// I have tested that it works with that patch.
|
||||
it.skip('will call webRequest.onBeforeRequest for custom protocol URLs with <all_urls> filter', async () => {
|
||||
const rc = await startRemoteControlApp(['--boot-eval="app.enableExtensionsOnAllProtocols();"']);
|
||||
const { called, responseText } = await rc.remotely(async () => {
|
||||
const { net, protocol, session } = require('electron/main');
|
||||
|
||||
protocol.handle('custom', () => new Response('success'));
|
||||
|
||||
let called = false;
|
||||
|
||||
session.defaultSession.webRequest.onBeforeRequest({ urls: ['<all_urls>'] }, (_: Electron.OnBeforeRequestListenerDetails, callback: (response: Electron.CallbackResponse) => void) => {
|
||||
called = true;
|
||||
callback({ cancel: false });
|
||||
});
|
||||
|
||||
const response = await net.fetch('custom://app/test');
|
||||
const responseText = await response.text();
|
||||
|
||||
global.setTimeout(() => require('electron').app.quit());
|
||||
|
||||
return { called, responseText };
|
||||
});
|
||||
expect(responseText).to.equal('success');
|
||||
expect(called).to.be.true();
|
||||
});
|
||||
|
||||
it('will not call webRequest.onBeforeRequest for custom protocol URLs that do not match the filter', async () => {
|
||||
const rc = await startRemoteControlApp(['--boot-eval="app.enableExtensionsOnAllProtocols();"']);
|
||||
const { called, responseText } = await rc.remotely(async () => {
|
||||
const { net, protocol, session } = require('electron/main');
|
||||
|
||||
protocol.handle('custom', () => new Response('success'));
|
||||
|
||||
let called = false;
|
||||
|
||||
session.defaultSession.webRequest.onBeforeRequest({ urls: ['http://*/*'] }, (_: Electron.OnBeforeRequestListenerDetails, callback: (response: Electron.CallbackResponse) => void) => {
|
||||
called = true;
|
||||
callback({ cancel: false });
|
||||
});
|
||||
|
||||
const response = await net.fetch('custom://app/test');
|
||||
const responseText = await response.text();
|
||||
|
||||
global.setTimeout(() => require('electron').app.quit());
|
||||
|
||||
return { called, responseText };
|
||||
});
|
||||
expect(responseText).to.equal('success');
|
||||
expect(called).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('receives details object', async () => {
|
||||
ses.webRequest.onBeforeRequest((details, callback) => {
|
||||
expect(details.id).to.be.a('number');
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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/`);
|
||||
});
|
||||
Reference in New Issue
Block a user