mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
feat: make Chrome extensions work on custom protocols (#50588)
* chore: backport crrev.com/c/7639311 * feat: make Chrome extensions work on custom protocols
This commit is contained in:
@@ -11,3 +11,5 @@
|
||||
* `stream` boolean (optional) - Default false.
|
||||
* `codeCache` boolean (optional) - Enable V8 code cache for the scheme, only
|
||||
works when `standard` is also set to true. Default false.
|
||||
* `allowExtensions` boolean (optional) - Allow Chrome extensions to be used
|
||||
on pages served over this protocol. Default false.
|
||||
|
||||
@@ -160,3 +160,5 @@ feat_plumb_node_integration_in_worker_through_workersettings.patch
|
||||
cherry-pick-fbfb27470bf6.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
fix_out-of-bounds_read_in_diff_rulesets.patch
|
||||
extensions_return_early_from_urlpattern_isvalidscheme.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Wenzel <dev@nikwen.de>
|
||||
Date: Tue, 31 Mar 2026 00:11:27 +0200
|
||||
Subject: [Extensions] Return early from URLPattern::IsValidScheme()
|
||||
|
||||
|scheme| will match at most one entry in |kValidSchemes|. No need to
|
||||
iterate through the remaining ones.
|
||||
|
||||
Change-Id: I1f37383faccaddc775faabb797aea2851d93382f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7639311
|
||||
Commit-Queue: Andrea Orru <andreaorru@chromium.org>
|
||||
Reviewed-by: Andrea Orru <andreaorru@chromium.org>
|
||||
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1594934}
|
||||
|
||||
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
|
||||
index 8975a936d6c18c4cc53a35bf680ca2d935f29071..daf5182643b7639bb47d932ddcc3f4dbdd093197 100644
|
||||
--- a/extensions/common/url_pattern.cc
|
||||
+++ b/extensions/common/url_pattern.cc
|
||||
@@ -397,8 +397,8 @@ bool URLPattern::IsValidScheme(std::string_view scheme) const {
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < std::size(kValidSchemes); ++i) {
|
||||
- if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i])) {
|
||||
- return true;
|
||||
+ if (scheme == kValidSchemes[i]) {
|
||||
+ return valid_schemes_ & kValidSchemeMasks[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
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 custom 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 bitmask 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 34fa528a82f03891c89b3bb95bc9d2a135ee5f36..f88041554b828215a32dbb4aadcc73df40e6d8c2 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(const std::string& pattern_str,
|
||||
std::string* error) {
|
||||
const int kAllowedSchemes =
|
||||
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
|
||||
- URLPattern::SCHEME_FILE;
|
||||
+ URLPattern::SCHEME_FILE | URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS;
|
||||
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 dec452ba5aa621b385011b77155705387312f82b..da2229cb0bcd18e1b3fd76ce25cb14bcb6bbf8b2 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
|
||||
@@ -52,7 +52,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_CUSTOM_PROTOCOLS;
|
||||
|
||||
class WebRequestEventRouter : public KeyedService {
|
||||
public:
|
||||
diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc
|
||||
index c15cb579d545d0640b3e936e5ca4e32610544138..de12c2894abeacf35a32d34e788ede4c38597e24 100644
|
||||
--- a/extensions/common/extension.cc
|
||||
+++ b/extensions/common/extension.cc
|
||||
@@ -219,7 +219,8 @@ 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_CUSTOM_PROTOCOLS;
|
||||
|
||||
//
|
||||
// Extension
|
||||
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
|
||||
index daf5182643b7639bb47d932ddcc3f4dbdd093197..e4c4b453e62a916bb61ca638df11f989be1e1769 100644
|
||||
--- a/extensions/common/url_pattern.cc
|
||||
+++ b/extensions/common/url_pattern.cc
|
||||
@@ -141,6 +141,11 @@ bool URLPattern::IsValidSchemeForExtensions(std::string_view scheme) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+ for (auto& extension_scheme : url::GetExtensionSchemes()) {
|
||||
+ if (scheme == extension_scheme) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -402,6 +407,14 @@ bool URLPattern::IsValidScheme(std::string_view scheme) const {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (valid_schemes_ & URLPattern::SCHEME_ELECTRON_CUSTOM_PROTOCOLS) {
|
||||
+ for (auto& extension_scheme : url::GetExtensionSchemes()) {
|
||||
+ if (scheme == extension_scheme) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h
|
||||
index 4d09251b0160644d86682ad3db7c41b50f360e6f..8a626e14eff2d58d8218a7b0df820c6c0522b00f 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 the schemes returned by url::GetExtensionSchemes().
|
||||
+ SCHEME_ELECTRON_CUSTOM_PROTOCOLS = 1 << 11,
|
||||
+
|
||||
// IMPORTANT!
|
||||
// SCHEME_ALL will match every scheme, including chrome://, chrome-
|
||||
// extension://, about:, etc. Because this has lots of security
|
||||
diff --git a/extensions/common/user_script.cc b/extensions/common/user_script.cc
|
||||
index 2d945e2f17a93ef22f9e4ed254c07cf91bc70c9b..712a2c32ab258253524f8d03fbdedd53cf9a0dc9 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_CUSTOM_PROTOCOLS
|
||||
};
|
||||
|
||||
// static
|
||||
diff --git a/url/url_util.cc b/url/url_util.cc
|
||||
index 50b15e06956c47e94ccd801fb3ee91aeb77ae15c..cd357f5312f70c1405e040e792dc22292b197176 100644
|
||||
--- a/url/url_util.cc
|
||||
+++ b/url/url_util.cc
|
||||
@@ -134,6 +134,9 @@ struct SchemeRegistry {
|
||||
// Embedder schemes that have V8 code cache enabled in js and wasm scripts.
|
||||
std::vector<std::string> code_cache_schemes = {};
|
||||
|
||||
+ // Embedder schemes on which Chrome extensions can be used.
|
||||
+ std::vector<std::string> extension_schemes = {};
|
||||
+
|
||||
// Schemes with a predefined default custom handler.
|
||||
std::vector<SchemeWithHandler> predefined_handler_schemes;
|
||||
|
||||
@@ -679,6 +682,15 @@ const std::vector<std::string>& GetCodeCacheSchemes() {
|
||||
return GetSchemeRegistry().code_cache_schemes;
|
||||
}
|
||||
|
||||
+void AddExtensionScheme(std::string_view new_scheme) {
|
||||
+ DoAddScheme(new_scheme,
|
||||
+ &GetSchemeRegistryWithoutLocking()->extension_schemes);
|
||||
+}
|
||||
+
|
||||
+const std::vector<std::string>& GetExtensionSchemes() {
|
||||
+ return GetSchemeRegistry().extension_schemes;
|
||||
+}
|
||||
+
|
||||
void AddPredefinedHandlerScheme(std::string_view new_scheme,
|
||||
std::string_view handler) {
|
||||
DoAddSchemeWithHandler(
|
||||
diff --git a/url/url_util.h b/url/url_util.h
|
||||
index 10bf2c6e27dca530906ef7acb7ac43fa5c731d22..924ae5f2e63db6489f4365551bde7b162aedd862 100644
|
||||
--- a/url/url_util.h
|
||||
+++ b/url/url_util.h
|
||||
@@ -124,6 +124,11 @@ COMPONENT_EXPORT(URL) const std::vector<std::string>& GetEmptyDocumentSchemes();
|
||||
COMPONENT_EXPORT(URL) void AddCodeCacheScheme(std::string_view new_scheme);
|
||||
COMPONENT_EXPORT(URL) const std::vector<std::string>& GetCodeCacheSchemes();
|
||||
|
||||
+// Adds an application-defined scheme to the list of schemes on which Chrome
|
||||
+// extensions can be used.
|
||||
+COMPONENT_EXPORT(URL) void AddExtensionScheme(std::string_view new_scheme);
|
||||
+COMPONENT_EXPORT(URL) const std::vector<std::string>& GetExtensionSchemes();
|
||||
+
|
||||
// Adds a scheme with a predefined default handler.
|
||||
//
|
||||
// This pair of strings must be normalized protocol handler parameters as
|
||||
@@ -1,2 +1,3 @@
|
||||
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
|
||||
fix_prefer_browser_runtime_over_node_in_hostruntime_detection.patch
|
||||
feat_allow_enabling_extension_panels_on_custom_protocols.patch
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
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 custom 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 a66d4b57af461db0426bd4216d5134490240d697..5e1f465adde1668cbb645f5ff55cd62382a10890 100644
|
||||
--- a/front_end/core/root/Runtime.ts
|
||||
+++ b/front_end/core/root/Runtime.ts
|
||||
@@ -575,6 +575,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
|
||||
* or guest mode, rather than a "normal" profile.
|
||||
*/
|
||||
isOffTheRecord: boolean,
|
||||
+ devToolsExtensionSchemes: readonly string[],
|
||||
devToolsEnableOriginBoundCookies: HostConfigEnableOriginBoundCookies,
|
||||
devToolsAnimationStylesInStylesTab: HostConfigAnimationStylesInStylesTab,
|
||||
thirdPartyCookieControls: HostConfigThirdPartyCookieControls,
|
||||
diff --git a/front_end/panels/common/ExtensionServer.ts b/front_end/panels/common/ExtensionServer.ts
|
||||
index 5fd87f637a0141788951997742c6fe8712c29ceb..8e26d9592f50615ef28382a44f0917b18f91b6f8 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';
|
||||
@@ -1603,7 +1604,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
||||
return false;
|
||||
}
|
||||
|
||||
- if (!kPermittedSchemes.includes(parsedURL.protocol)) {
|
||||
+ if (!kPermittedSchemes.includes(parsedURL.protocol) &&
|
||||
+ !Root.Runtime.hostConfig.devToolsExtensionSchemes?.includes(parsedURL.protocol)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ struct SchemeOptions {
|
||||
bool corsEnabled = false;
|
||||
bool stream = false;
|
||||
bool codeCache = false;
|
||||
bool allowExtensions = false;
|
||||
};
|
||||
|
||||
struct CustomScheme {
|
||||
@@ -70,6 +71,7 @@ struct Converter<CustomScheme> {
|
||||
opt.Get("corsEnabled", &(out->options.corsEnabled));
|
||||
opt.Get("stream", &(out->options.stream));
|
||||
opt.Get("codeCache", &(out->options.codeCache));
|
||||
opt.Get("allowExtensions", &(out->options.allowExtensions));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -124,7 +126,7 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
||||
}
|
||||
|
||||
std::vector<std::string> secure_schemes, cspbypassing_schemes, fetch_schemes,
|
||||
service_worker_schemes, cors_schemes;
|
||||
service_worker_schemes, cors_schemes, extension_schemes;
|
||||
for (const auto& custom_scheme : custom_schemes) {
|
||||
// Register scheme to privileged list (https, wss, data, chrome-extension)
|
||||
if (custom_scheme.options.standard) {
|
||||
@@ -160,6 +162,10 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
||||
GetCodeCacheSchemes().push_back(custom_scheme.scheme);
|
||||
url::AddCodeCacheScheme(custom_scheme.scheme.c_str());
|
||||
}
|
||||
if (custom_scheme.options.allowExtensions) {
|
||||
extension_schemes.push_back(custom_scheme.scheme);
|
||||
url::AddExtensionScheme(custom_scheme.scheme.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
const auto AppendSchemesToCmdLine = [](const std::string_view switch_name,
|
||||
@@ -179,6 +185,8 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
||||
AppendSchemesToCmdLine(electron::switches::kFetchSchemes, fetch_schemes);
|
||||
AppendSchemesToCmdLine(electron::switches::kServiceWorkerSchemes,
|
||||
service_worker_schemes);
|
||||
AppendSchemesToCmdLine(electron::switches::kExtensionSchemes,
|
||||
extension_schemes);
|
||||
AppendSchemesToCmdLine(electron::switches::kStandardSchemes,
|
||||
GetStandardSchemes());
|
||||
AppendSchemesToCmdLine(electron::switches::kStreamingSchemes,
|
||||
|
||||
@@ -554,7 +554,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
if (process_type == ::switches::kUtilityProcess ||
|
||||
process_type == ::switches::kRendererProcess) {
|
||||
// Copy following switches to child process.
|
||||
static constexpr std::array<const char*, 10U> kCommonSwitchNames = {
|
||||
static constexpr std::array<const char*, 11U> kCommonSwitchNames = {
|
||||
switches::kStandardSchemes.c_str(),
|
||||
switches::kEnableSandbox.c_str(),
|
||||
switches::kSecureSchemes.c_str(),
|
||||
@@ -564,7 +564,8 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
switches::kServiceWorkerSchemes.c_str(),
|
||||
switches::kStreamingSchemes.c_str(),
|
||||
switches::kNoStdioInit.c_str(),
|
||||
switches::kCodeCacheSchemes.c_str()};
|
||||
switches::kCodeCacheSchemes.c_str(),
|
||||
switches::kExtensionSchemes.c_str()};
|
||||
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
|
||||
kCommonSwitchNames);
|
||||
if (process_type == ::switches::kUtilityProcess ||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "third_party/blink/public/common/page/page_zoom.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "url/url_util.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
@@ -868,7 +869,14 @@ void InspectableWebContents::GetSyncInformation(DispatchCallback callback) {
|
||||
}
|
||||
|
||||
void InspectableWebContents::GetHostConfig(DispatchCallback callback) {
|
||||
base::Value::Dict response_dict;
|
||||
base::DictValue response_dict;
|
||||
|
||||
base::ListValue extension_schemes;
|
||||
for (const std::string& scheme : url::GetExtensionSchemes())
|
||||
extension_schemes.Append(scheme + ":");
|
||||
response_dict.Set("devToolsExtensionSchemes",
|
||||
base::Value(std::move(extension_schemes)));
|
||||
|
||||
base::Value response = base::Value(std::move(response_dict));
|
||||
std::move(callback).Run(&response);
|
||||
}
|
||||
|
||||
@@ -270,6 +270,9 @@ inline constexpr base::cstring_view kStreamingSchemes = "streaming-schemes";
|
||||
// Register schemes as supporting V8 code cache.
|
||||
inline constexpr base::cstring_view kCodeCacheSchemes = "code-cache-schemes";
|
||||
|
||||
// Register schemes as supporting extensions.
|
||||
inline constexpr base::cstring_view kExtensionSchemes = "extension-schemes";
|
||||
|
||||
// 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());
|
||||
// Parse --extension-schemes=scheme1,scheme2
|
||||
std::vector<std::string> extension_schemes_list =
|
||||
ParseSchemesCLISwitch(command_line, switches::kExtensionSchemes);
|
||||
for (const std::string& scheme : extension_schemes_list)
|
||||
url::AddExtensionScheme(scheme.c_str());
|
||||
// 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,
|
||||
|
||||
@@ -1123,6 +1123,8 @@ describe('protocol module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// protocol.registerSchemesAsPrivileged allowExtensions tests are in extensions-spec.ts.
|
||||
|
||||
describe('handle', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
|
||||
@@ -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 allowExtensions privileges 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 allowExtensions privileges', 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>
|
||||
41
spec/fixtures/extensions/custom-protocol-panel/main.js
vendored
Normal file
41
spec/fixtures/extensions/custom-protocol-panel/main.js
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
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,
|
||||
allowExtensions: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
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
|
||||
}
|
||||
35
spec/fixtures/extensions/custom-protocol/main.js
vendored
Normal file
35
spec/fixtures/extensions/custom-protocol/main.js
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
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,
|
||||
allowExtensions: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
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