diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index 8e4329b5234e..952e32d0d356 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/stl_util.h" +#include "content/common/url_schemes.h" #include "content/public/browser/child_process_security_policy.h" #include "gin/object_template_builder.h" #include "shell/browser/browser.h" @@ -124,6 +125,13 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, } if (custom_scheme.options.allowServiceWorkers) { service_worker_schemes.push_back(custom_scheme.scheme); + // There is no API to add service worker scheme, but there is an API to + // return const reference to the schemes vector. + // If in future the API is changed to return a copy instead of reference, + // the compilation will fail, and we should add a patch at that time. + auto& mutable_schemes = const_cast&>( + content::GetServiceWorkerSchemes()); + mutable_schemes.push_back(custom_scheme.scheme); } if (custom_scheme.options.stream) { g_streaming_schemes.push_back(custom_scheme.scheme); diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 80240d148f71..3a42043bf6bf 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1352,22 +1352,26 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( int render_process_id, int render_frame_id, NonNetworkURLLoaderFactoryMap* factories) { - content::RenderFrameHost* frame_host = - content::RenderFrameHost::FromID(render_process_id, render_frame_id); - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(frame_host); + auto* render_process_host = + content::RenderProcessHost::FromID(render_process_id); + DCHECK(render_process_host); + if (!render_process_host || !render_process_host->GetBrowserContext()) + return; + + ProtocolRegistry::FromBrowserContext(render_process_host->GetBrowserContext()) + ->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource, + factories); - if (web_contents) { - ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext()) - ->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource, - factories); - } #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, render_frame_id); if (factory) factories->emplace(extensions::kExtensionScheme, std::move(factory)); + content::RenderFrameHost* frame_host = + content::RenderFrameHost::FromID(render_process_id, render_frame_id); + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(frame_host); if (!web_contents) return; diff --git a/spec-main/api-protocol-spec.ts b/spec-main/api-protocol-spec.ts index 31abb6ce13e7..720d5bcaa10e 100644 --- a/spec-main/api-protocol-spec.ts +++ b/spec-main/api-protocol-spec.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { v4 } from 'uuid'; import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main'; import { AddressInfo } from 'net'; import * as ChildProcess from 'child_process'; @@ -724,6 +725,36 @@ describe('protocol module', () => { }); }); + describe('protocol.registerSchemesAsPrivileged allowServiceWorkers', () => { + const { serviceWorkerScheme } = global as any; + protocol.registerStringProtocol(serviceWorkerScheme, (request, cb) => { + if (request.url.endsWith('.js')) { + cb({ + mimeType: 'text/javascript', + charset: 'utf-8', + data: 'console.log("Loaded")' + }); + } else { + cb({ + mimeType: 'text/html', + charset: 'utf-8', + data: '' + }); + } + }); + after(() => protocol.unregisterProtocol(serviceWorkerScheme)); + + it('should fail when registering invalid service worker', async () => { + await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`); + await expect(contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.notjs', {scope: './'})`)).to.be.rejected(); + }); + + it('should be able to register service worker for custom scheme', async () => { + await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`); + await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.js', {scope: './'})`); + }); + }); + describe.skip('protocol.registerSchemesAsPrivileged standard', () => { const standardScheme = (global as any).standardScheme; const origin = `${standardScheme}://fake-host`; diff --git a/spec-main/index.js b/spec-main/index.js index 60c6d10602e0..406a7b7ecf3e 100644 --- a/spec-main/index.js +++ b/spec-main/index.js @@ -34,9 +34,11 @@ app.commandLine.appendSwitch('use-fake-device-for-media-stream'); global.standardScheme = 'app'; global.zoomScheme = 'zoom'; +global.serviceWorkerScheme = 'sw'; protocol.registerSchemesAsPrivileged([ { scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, + { scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } }, { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'no-cors', privileges: { supportFetchAPI: true } },