mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
1 Commits
refactor/a
...
nikwen/ext
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
346d31f5e0 |
@@ -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
|
||||
|
||||
@@ -144,3 +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
|
||||
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 +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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "base/environment.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
@@ -280,8 +279,7 @@ void OnDeploymentCompleted(std::unique_ptr<DeploymentCallbackData> data,
|
||||
HRESULT error_code;
|
||||
hr = async_info->get_ErrorCode(&error_code);
|
||||
if (SUCCEEDED(hr)) {
|
||||
error +=
|
||||
" (" + base::NumberToString(static_cast<int>(error_code)) + ")";
|
||||
error += " (" + std::to_string(static_cast<int>(error_code)) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -800,10 +798,10 @@ v8::Local<v8::Value> GetPackageInfo() {
|
||||
ABI::Windows::ApplicationModel::PackageVersion pkg_version;
|
||||
hr = package_id->get_Version(&pkg_version);
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::string version = base::NumberToString(pkg_version.Major) + "." +
|
||||
base::NumberToString(pkg_version.Minor) + "." +
|
||||
base::NumberToString(pkg_version.Build) + "." +
|
||||
base::NumberToString(pkg_version.Revision);
|
||||
std::string version = std::to_string(pkg_version.Major) + "." +
|
||||
std::to_string(pkg_version.Minor) + "." +
|
||||
std::to_string(pkg_version.Build) + "." +
|
||||
std::to_string(pkg_version.Revision);
|
||||
result.Set("version", version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/current_thread.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
@@ -2660,7 +2659,7 @@ void WebContents::RestoreHistory(
|
||||
thrower.ThrowError(
|
||||
"Failed to restore navigation history: Invalid navigation entry at "
|
||||
"index " +
|
||||
base::NumberToString(index) + ".");
|
||||
std::to_string(index) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "base/environment.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
@@ -193,7 +192,7 @@ void V8OOMErrorCallback(const char* location, const v8::OOMDetails& details) {
|
||||
|
||||
#if !IS_MAS_BUILD()
|
||||
electron::crash_keys::SetCrashKey("electron.v8-oom.is_heap_oom",
|
||||
base::NumberToString(details.is_heap_oom));
|
||||
std::to_string(details.is_heap_oom));
|
||||
if (location) {
|
||||
electron::crash_keys::SetCrashKey("electron.v8-oom.location", location);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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