From 792f228dfb17b05659a21b49f856fd217279e702 Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 11 Mar 2020 21:50:29 +0100 Subject: [PATCH 01/44] fix(extensions): implement missing web_request hooks --- shell/browser/electron_browser_client.cc | 51 +++++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index eaca272fc6db4..e9a8e8b2a43c1 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -44,6 +44,8 @@ #include "content/public/common/web_preferences.h" #include "electron/buildflags/buildflags.h" #include "electron/grit/electron_resources.h" +#include "extensions/browser/api/web_request/web_request_api.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_navigation_ui_data.h" #include "extensions/browser/extension_protocols.h" #include "extensions/common/constants.h" @@ -1300,12 +1302,25 @@ bool ElectronBrowserClient::WillInterceptWebSocket( auto* browser_context = frame->GetProcess()->GetBrowserContext(); auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + const auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + frame->GetProcess()->GetBrowserContext()); + + // NOTE: Some unit test environments do not initialize + // BrowserContextKeyedAPI factories for e.g. WebRequest. + if (!web_request_api && !web_request.get()) + return false; + + return web_request->HasListener() || web_request_api->MayHaveProxies(); +#else // NOTE: Some unit test environments do not initialize // BrowserContextKeyedAPI factories for e.g. WebRequest. if (!web_request.get()) return false; return web_request->HasListener(); +#endif } void ElectronBrowserClient::CreateWebSocket( @@ -1316,8 +1331,20 @@ void ElectronBrowserClient::CreateWebSocket( const base::Optional& user_agent, mojo::PendingRemote handshake_client) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); auto* browser_context = frame->GetProcess()->GetBrowserContext(); + +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + browser_context); + + DCHECK(web_request_api); + web_request_api->ProxyWebSocket(frame, std::move(factory), url, + site_for_cookies.RepresentativeUrl(), + user_agent, std::move(handshake_client)); +#endif + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); DCHECK(web_request.get()); ProxyingWebSocket::StartProxying( @@ -1341,6 +1368,25 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( bool* bypass_redirect_checks, bool* disable_secure_dns, network::mojom::URLLoaderFactoryOverridePtr* factory_override) { + bool use_proxy = false; + +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto* web_request_api = + extensions::BrowserContextKeyedAPIFactory::Get( + browser_context); + + // NOTE: Some unit test environments do not initialize + // BrowserContextKeyedAPI factories for e.g. WebRequest. + if (web_request_api) { + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame, render_process_id, type, + std::move(navigation_id), factory_receiver, header_client); + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + use_proxy |= use_proxy_for_web_request; + } +#endif v8::Isolate* isolate = v8::Isolate::GetCurrent(); api::Protocol* protocol = api::Protocol::FromWrappedClass(isolate, browser_context); @@ -1377,7 +1423,8 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( if (bypass_redirect_checks) *bypass_redirect_checks = true; - return true; + + return use_proxy; } void ElectronBrowserClient::OverrideURLLoaderFactoryParams( From 3f679696603bfdfd0676c982d22b906e9f840c01 Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 11 Mar 2020 22:19:31 +0100 Subject: [PATCH 02/44] fix: buildflag typo --- shell/browser/electron_browser_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index e9a8e8b2a43c1..a538722239ab1 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1370,7 +1370,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( network::mojom::URLLoaderFactoryOverridePtr* factory_override) { bool use_proxy = false; -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); From 3dac5bc613acf8214120e0d833f6c5f63442454c Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 11 Mar 2020 22:22:09 +0100 Subject: [PATCH 03/44] fix: undeclared identifier frame --- shell/browser/electron_browser_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 03a14dee2f785..4752313575d39 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1385,7 +1385,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( if (web_request_api) { bool use_proxy_for_web_request = web_request_api->MaybeProxyURLLoaderFactory( - browser_context, frame, render_process_id, type, + browser_context, frame_host, render_process_id, type, std::move(navigation_id), factory_receiver, header_client); if (bypass_redirect_checks) *bypass_redirect_checks = use_proxy_for_web_request; From a77fb19606c4a4cb8f6502b8f58312fbabf93285 Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 11 Mar 2020 22:58:39 +0100 Subject: [PATCH 04/44] fix: condition --- shell/browser/electron_browser_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 4752313575d39..ce6d8606d51b6 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1313,7 +1313,7 @@ bool ElectronBrowserClient::WillInterceptWebSocket( // NOTE: Some unit test environments do not initialize // BrowserContextKeyedAPI factories for e.g. WebRequest. - if (!web_request_api && !web_request.get()) + if (!web_request_api || !web_request.get()) return false; return web_request->HasListener() || web_request_api->MayHaveProxies(); From 9d3cbd142e0a302bd3dff0e26ae52be2764dacd4 Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 11 Mar 2020 23:39:24 +0100 Subject: [PATCH 05/44] refactor: simplify the code --- shell/browser/electron_browser_client.cc | 30 +++++++++--------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index ce6d8606d51b6..4826bb81d91e8 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1306,23 +1306,21 @@ bool ElectronBrowserClient::WillInterceptWebSocket( auto* browser_context = frame->GetProcess()->GetBrowserContext(); auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); + // NOTE: Some unit test environments do not initialize + // BrowserContextKeyedAPI factories for e.g. WebRequest. + if (!web_request.get()) + return false; + #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) const auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( - frame->GetProcess()->GetBrowserContext()); + browser_context); - // NOTE: Some unit test environments do not initialize - // BrowserContextKeyedAPI factories for e.g. WebRequest. - if (!web_request_api || !web_request.get()) + if (!web_request_api) return false; return web_request->HasListener() || web_request_api->MayHaveProxies(); #else - // NOTE: Some unit test environments do not initialize - // BrowserContextKeyedAPI factories for e.g. WebRequest. - if (!web_request.get()) - return false; - return web_request->HasListener(); #endif } @@ -1373,8 +1371,6 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( bool* bypass_redirect_checks, bool* disable_secure_dns, network::mojom::URLLoaderFactoryOverridePtr* factory_override) { - bool use_proxy = false; - #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( @@ -1383,13 +1379,9 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( // NOTE: Some unit test environments do not initialize // BrowserContextKeyedAPI factories for e.g. WebRequest. if (web_request_api) { - bool use_proxy_for_web_request = - web_request_api->MaybeProxyURLLoaderFactory( - browser_context, frame_host, render_process_id, type, - std::move(navigation_id), factory_receiver, header_client); - if (bypass_redirect_checks) - *bypass_redirect_checks = use_proxy_for_web_request; - use_proxy |= use_proxy_for_web_request; + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame_host, render_process_id, type, + std::move(navigation_id), factory_receiver, header_client); } #endif v8::Isolate* isolate = v8::Isolate::GetCurrent(); @@ -1430,7 +1422,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( if (bypass_redirect_checks) *bypass_redirect_checks = true; - return use_proxy; + return true; } void ElectronBrowserClient::OverrideURLLoaderFactoryParams( From 3c06852e620d2215f8f36f4bcd23551497ed624f Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Mon, 6 Apr 2020 13:32:34 +0200 Subject: [PATCH 06/44] fix: update condition --- shell/browser/electron_browser_client.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 4826bb81d91e8..1520ec4b44bd4 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1316,13 +1316,11 @@ bool ElectronBrowserClient::WillInterceptWebSocket( extensions::BrowserContextKeyedAPIFactory::Get( browser_context); - if (!web_request_api) - return false; - - return web_request->HasListener() || web_request_api->MayHaveProxies(); -#else - return web_request->HasListener(); + if (web_request_api) + return web_request->HasListener() || web_request_api->MayHaveProxies(); #endif + + return web_request->HasListener(); } void ElectronBrowserClient::CreateWebSocket( From 89e3517cb10a8acca32e7da0dfcbd9833dc8c76f Mon Sep 17 00:00:00 2001 From: sentialx Date: Tue, 7 Apr 2020 01:20:57 +0200 Subject: [PATCH 07/44] fix: ProxyWebSocket hitting DCHECK --- shell/browser/electron_browser_client.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 1520ec4b44bd4..f0fa00352994c 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1319,7 +1319,7 @@ bool ElectronBrowserClient::WillInterceptWebSocket( if (web_request_api) return web_request->HasListener() || web_request_api->MayHaveProxies(); #endif - + return web_request->HasListener(); } @@ -1341,9 +1341,12 @@ void ElectronBrowserClient::CreateWebSocket( browser_context); DCHECK(web_request_api); - web_request_api->ProxyWebSocket(frame, std::move(factory), url, - site_for_cookies.RepresentativeUrl(), - user_agent, std::move(handshake_client)); + + if (web_request_api->MayHaveProxies()) { + web_request_api->ProxyWebSocket(frame, std::move(factory), url, + site_for_cookies.RepresentativeUrl(), + user_agent, std::move(handshake_client)); + } #endif auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); From bb847acb3d21007955aa2779ffe331a47f054030 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 10 Apr 2020 15:55:52 +0200 Subject: [PATCH 08/44] fix: chrome.webRequest now takes precendence over Electron.webRequest --- shell/browser/electron_browser_client.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index a0dbd348043f8..d0313964352c3 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1377,6 +1377,8 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( bool* disable_secure_dns, network::mojom::URLLoaderFactoryOverridePtr* factory_override) { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + bool use_proxy = false; + auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); @@ -1384,9 +1386,17 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( // NOTE: Some unit test environments do not initialize // BrowserContextKeyedAPI factories for e.g. WebRequest. if (web_request_api) { - web_request_api->MaybeProxyURLLoaderFactory( - browser_context, frame_host, render_process_id, type, - std::move(navigation_id), factory_receiver, header_client); + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame_host, render_process_id, type, navigation_id, + factory_receiver, header_client); + + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + use_proxy |= use_proxy_for_web_request; + + if (use_proxy) + return true; } #endif v8::Isolate* isolate = v8::Isolate::GetCurrent(); From 351af18852e61ba6ac77b388ee3ddef4ad28dcbf Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Fri, 10 Apr 2020 19:28:36 +0200 Subject: [PATCH 09/44] refactor: remove redundant use_proxy variable Co-Authored-By: Jeremy Apthorp --- shell/browser/electron_browser_client.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index d0313964352c3..9c581feac9cdd 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1393,9 +1393,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( if (bypass_redirect_checks) *bypass_redirect_checks = use_proxy_for_web_request; - use_proxy |= use_proxy_for_web_request; - - if (use_proxy) + if (use_proxy_for_web_request) return true; } #endif From 9e9075c086887ceebce89a00f5c25828c0219ab6 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 10 Apr 2020 23:15:25 +0200 Subject: [PATCH 10/44] fix: return in CreateWebSocket --- shell/browser/electron_browser_client.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 9c581feac9cdd..5b769dc7161d1 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1347,9 +1347,10 @@ void ElectronBrowserClient::CreateWebSocket( DCHECK(web_request_api); if (web_request_api->MayHaveProxies()) { - web_request_api->ProxyWebSocket(frame, std::move(factory), url, + web_request_api->ProxyWebSocket(frame, factory, url, site_for_cookies.RepresentativeUrl(), - user_agent, std::move(handshake_client)); + user_agent, handshake_client); + return; } #endif @@ -1377,8 +1378,6 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( bool* disable_secure_dns, network::mojom::URLLoaderFactoryOverridePtr* factory_override) { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - bool use_proxy = false; - auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); From 8bb79bb4756172fb1bff8436608f33e10fb0fe03 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 11 Apr 2020 00:15:34 +0200 Subject: [PATCH 11/44] test: add chrome.webRequest.onBeforeRequest tests --- shell/browser/electron_browser_client.cc | 4 +- spec-main/extensions-spec.ts | 74 ++++++++++++++++++- .../chrome-webRequest/background.js | 9 +++ .../chrome-webRequest/manifest.json | 10 +++ 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 spec-main/fixtures/extensions/chrome-webRequest/background.js create mode 100644 spec-main/fixtures/extensions/chrome-webRequest/manifest.json diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 5b769dc7161d1..4319940dd93d9 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1347,9 +1347,9 @@ void ElectronBrowserClient::CreateWebSocket( DCHECK(web_request_api); if (web_request_api->MayHaveProxies()) { - web_request_api->ProxyWebSocket(frame, factory, url, + web_request_api->ProxyWebSocket(frame, std::move(factory), url, site_for_cookies.RepresentativeUrl(), - user_agent, handshake_client); + user_agent, std::move(handshake_client)); return; } #endif diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 0059470ae3cfa..9bdcb3cc5c641 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { app, session, BrowserWindow, ipcMain, WebContents, Extension } from 'electron/main'; +import { app, session, BrowserWindow, ipcMain, WebContents, Extension, protocol } from 'electron/main'; import { closeAllWindows, closeWindow } from './window-helpers'; import * as http from 'http'; import { AddressInfo } from 'net'; @@ -170,6 +170,78 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex }); }); + describe('chrome.webRequest', () => { + const server = http.createServer(async (req, res) => { + if (req.url === '/serverRedirect') { + res.statusCode = 301; + res.setHeader('Location', 'http://' + req.rawHeaders[1]); + res.end(); + } else if (req.url === '/jquery') { + const html = await fs.promises.readFile(path.join(fixtures, 'pages', 'jquery.html')); + res.writeHeader(200, {"Content-Type": "text/html"}); + res.write(html); + res.end(); + } else { + res.setHeader('Custom', ['Header']); + let content = req.url; + if (req.headers.accept === '*/*;test/header') { + content += 'header/received'; + } + if (req.headers.origin === 'http://new-origin') { + content += 'new/origin'; + } + res.end(content); + } + }); + let defaultURL: string; + + before((done) => { + protocol.registerStringProtocol('neworigin', (req, cb) => cb('')); + server.listen(1, '127.0.0.1', () => { + const port = (server.address() as AddressInfo).port; + defaultURL = `http://127.0.0.1:${port}/`; + done(); + }); + }); + + after(() => { + server.close(); + protocol.unregisterProtocol('neworigin'); + }); + + async function ajax (contents: WebContents, url: string, options = {}) { + return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`); + } + + describe('chrome.webRequest.onBeforeRequest', () => { + it('can cancel the request', async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + await w.loadURL(`${defaultURL}jquery`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + + await expect(ajax(w.webContents, defaultURL)).to.eventually.be.rejectedWith('404'); + }); + }); + + describe('chrome.webRequest.onBeforeRequest and Electron webRequest', () => { + it('chrome.webRequest takes precedence over Electron webRequest', async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + await w.loadURL(`${defaultURL}jquery`); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + + customSession.webRequest.onBeforeRequest((details, callback) => { + callback({ + cancel: true + }); + }); + + await expect(ajax(w.webContents, defaultURL)).to.eventually.be.rejectedWith('404'); + }); + }); + }); + describe('chrome.tabs', () => { it('executeScript', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); diff --git a/spec-main/fixtures/extensions/chrome-webRequest/background.js b/spec-main/fixtures/extensions/chrome-webRequest/background.js new file mode 100644 index 0000000000000..a3fe1b22b023c --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest/background.js @@ -0,0 +1,9 @@ +/* global chrome */ + +chrome.webRequest.onBeforeRequest.addListener( + (details) => { + return {cancel: false}; + }, + {urls: [""]}, + ["blocking"] +); \ No newline at end of file diff --git a/spec-main/fixtures/extensions/chrome-webRequest/manifest.json b/spec-main/fixtures/extensions/chrome-webRequest/manifest.json new file mode 100644 index 0000000000000..cb250021097ab --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "chrome-webRequest", + "version": "1.0", + "background": { + "scripts": ["background.js"], + "persistent": false + }, + "permissions": ["webRequest", ""], + "manifest_version": 2 +} From d3da8eb968e6af15dde25cd64636d73bf68c2d3e Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 11 Apr 2020 01:15:14 +0200 Subject: [PATCH 12/44] fix: tests --- spec-main/extensions-spec.ts | 2 +- .../fixtures/extensions/chrome-webRequest/background.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 9bdcb3cc5c641..7d0d076f9b535 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -178,7 +178,7 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex res.end(); } else if (req.url === '/jquery') { const html = await fs.promises.readFile(path.join(fixtures, 'pages', 'jquery.html')); - res.writeHeader(200, {"Content-Type": "text/html"}); + res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': html.length }); res.write(html); res.end(); } else { diff --git a/spec-main/fixtures/extensions/chrome-webRequest/background.js b/spec-main/fixtures/extensions/chrome-webRequest/background.js index a3fe1b22b023c..6037ad1e239b9 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest/background.js +++ b/spec-main/fixtures/extensions/chrome-webRequest/background.js @@ -2,8 +2,8 @@ chrome.webRequest.onBeforeRequest.addListener( (details) => { - return {cancel: false}; + return { cancel: false }; }, - {urls: [""]}, - ["blocking"] + { urls: [''] }, + ['blocking'] ); \ No newline at end of file From 44718cc44ff9ca7742d367b88e28c4bbd6bb088e Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 11 Apr 2020 12:52:11 +0200 Subject: [PATCH 13/44] fix: tests --- spec-main/extensions-spec.ts | 65 ++++++------------- .../chrome-webRequest/background.js | 2 +- 2 files changed, 21 insertions(+), 46 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 7d0d076f9b535..caaabbc1b05cb 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { app, session, BrowserWindow, ipcMain, WebContents, Extension, protocol } from 'electron/main'; +import { app, session, BrowserWindow, ipcMain, WebContents, Extension } from 'electron/main'; import { closeAllWindows, closeWindow } from './window-helpers'; import * as http from 'http'; import { AddressInfo } from 'net'; @@ -15,7 +15,20 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex let server: http.Server; let url: string; before(async () => { - server = http.createServer((req, res) => res.end()); + server = http.createServer(async (req, res) => { + if (req.url === '/serverRedirect') { + res.statusCode = 301; + res.setHeader('Location', 'http://' + req.rawHeaders[1]); + res.end(); + } else if (req.url === '/jquery') { + const html = await fs.promises.readFile(path.join(fixtures, 'pages', 'jquery.html')); + res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': html.length }); + res.write(html); + res.end(); + } else { + res.end(); + } + }); await new Promise(resolve => server.listen(0, '127.0.0.1', () => { url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; resolve(); @@ -171,45 +184,7 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex }); describe('chrome.webRequest', () => { - const server = http.createServer(async (req, res) => { - if (req.url === '/serverRedirect') { - res.statusCode = 301; - res.setHeader('Location', 'http://' + req.rawHeaders[1]); - res.end(); - } else if (req.url === '/jquery') { - const html = await fs.promises.readFile(path.join(fixtures, 'pages', 'jquery.html')); - res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': html.length }); - res.write(html); - res.end(); - } else { - res.setHeader('Custom', ['Header']); - let content = req.url; - if (req.headers.accept === '*/*;test/header') { - content += 'header/received'; - } - if (req.headers.origin === 'http://new-origin') { - content += 'new/origin'; - } - res.end(content); - } - }); - let defaultURL: string; - - before((done) => { - protocol.registerStringProtocol('neworigin', (req, cb) => cb('')); - server.listen(1, '127.0.0.1', () => { - const port = (server.address() as AddressInfo).port; - defaultURL = `http://127.0.0.1:${port}/`; - done(); - }); - }); - - after(() => { - server.close(); - protocol.unregisterProtocol('neworigin'); - }); - - async function ajax (contents: WebContents, url: string, options = {}) { + async function ajax (contents: WebContents, url: string, options = {}) { return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`); } @@ -217,10 +192,10 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex it('can cancel the request', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(`${defaultURL}jquery`); + await w.loadURL(`${url}jquery`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - await expect(ajax(w.webContents, defaultURL)).to.eventually.be.rejectedWith('404'); + await expect(ajax(w.webContents, url)).to.eventually.be.rejectedWith('404'); }); }); @@ -228,7 +203,7 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex it('chrome.webRequest takes precedence over Electron webRequest', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(`${defaultURL}jquery`); + await w.loadURL(`${url}jquery`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); customSession.webRequest.onBeforeRequest((details, callback) => { @@ -237,7 +212,7 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex }); }); - await expect(ajax(w.webContents, defaultURL)).to.eventually.be.rejectedWith('404'); + await expect(ajax(w.webContents, url)).to.eventually.be.rejectedWith('404'); }); }); }); diff --git a/spec-main/fixtures/extensions/chrome-webRequest/background.js b/spec-main/fixtures/extensions/chrome-webRequest/background.js index 6037ad1e239b9..e713eef2bca22 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest/background.js +++ b/spec-main/fixtures/extensions/chrome-webRequest/background.js @@ -6,4 +6,4 @@ chrome.webRequest.onBeforeRequest.addListener( }, { urls: [''] }, ['blocking'] -); \ No newline at end of file +); From cbe08d1c174ce05fdc32d3318e02d89fb1bdda20 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 11 Apr 2020 14:51:05 +0200 Subject: [PATCH 14/44] fix: add slash in url --- spec-main/extensions-spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index caaabbc1b05cb..cdffd456ce46b 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -192,7 +192,7 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex it('can cancel the request', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(`${url}jquery`); + await w.loadURL(`${url}/jquery`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); await expect(ajax(w.webContents, url)).to.eventually.be.rejectedWith('404'); @@ -203,7 +203,7 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex it('chrome.webRequest takes precedence over Electron webRequest', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(`${url}jquery`); + await w.loadURL(`${url}/jquery`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); customSession.webRequest.onBeforeRequest((details, callback) => { From 95a8005a4a2204e5cef2dbcd16af39785cf86be1 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 11 Apr 2020 16:12:20 +0200 Subject: [PATCH 15/44] fix: correct extension permissions --- spec-main/fixtures/extensions/chrome-webRequest/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec-main/fixtures/extensions/chrome-webRequest/manifest.json b/spec-main/fixtures/extensions/chrome-webRequest/manifest.json index cb250021097ab..c1723d2118850 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest/manifest.json +++ b/spec-main/fixtures/extensions/chrome-webRequest/manifest.json @@ -3,8 +3,8 @@ "version": "1.0", "background": { "scripts": ["background.js"], - "persistent": false + "persistent": true }, - "permissions": ["webRequest", ""], + "permissions": ["webRequest", "webRequestBlocking", ""], "manifest_version": 2 } From b47ff30d0b4daa5a1d7f70a9734a667be4bcc238 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 01:00:05 +0200 Subject: [PATCH 16/44] fix: attempt to fix tests --- spec-main/extensions-spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index e5a169801e62c..3c21dafd3dee6 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -24,8 +24,7 @@ describe('chrome extensions', () => { } else if (req.url === '/jquery') { const html = await fs.promises.readFile(path.join(fixtures, 'pages', 'jquery.html')); res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': html.length }); - res.write(html); - res.end(); + res.end(html, 'utf-8'); } else { res.end(emptyPage); } From 138a94dfa3b7f2bebf7009caba1baec398900f9a Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 01:01:58 +0200 Subject: [PATCH 17/44] fix: headers under enable_electron_extensions --- shell/browser/electron_browser_client.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 749a857e17304..7a215771db3c9 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -47,12 +47,6 @@ #include "content/public/common/web_preferences.h" #include "electron/buildflags/buildflags.h" #include "electron/grit/electron_resources.h" -#include "extensions/browser/api/web_request/web_request_api.h" -#include "extensions/browser/browser_context_keyed_api_factory.h" -#include "extensions/browser/extension_navigation_ui_data.h" -#include "extensions/browser/extension_protocols.h" -#include "extensions/common/constants.h" -#include "extensions/common/switches.h" #include "mojo/public/cpp/bindings/binder_map.h" #include "net/base/escape.h" #include "net/ssl/ssl_cert_request_info.h" @@ -144,9 +138,13 @@ #include "content/public/browser/file_url_loader.h" #include "content/public/browser/web_ui_url_loader_factory.h" #include "extensions/browser/api/mime_handler_private/mime_handler_private.h" +#include "extensions/browser/api/web_request/web_request_api.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_message_filter.h" #include "extensions/browser/extension_navigation_throttle.h" +#include "extensions/browser/extension_navigation_ui_data.h" +#include "extensions/browser/extension_protocols.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/guest_view/extensions_guest_view_message_filter.h" @@ -155,7 +153,9 @@ #include "extensions/browser/process_manager.h" #include "extensions/browser/process_map.h" #include "extensions/common/api/mime_handler.mojom.h" +#include "extensions/common/constants.h" #include "extensions/common/extension.h" +#include "extensions/common/switches.h" #include "shell/browser/extensions/electron_extension_message_filter.h" #include "shell/browser/extensions/electron_extension_system.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h" From 3e3c8e87cd6789284f006946fac95efe605df27b Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 01:13:17 +0200 Subject: [PATCH 18/44] lint: fix spaces --- spec-main/extensions-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 3c21dafd3dee6..9217cd2f9919d 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -188,7 +188,7 @@ describe('chrome extensions', () => { }); describe('chrome.webRequest', () => { - async function ajax (contents: WebContents, url: string, options = {}) { + async function ajax (contents: WebContents, url: string, options = {}) { return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`); } From 29234147de0d3f0407251240cd370b1d8523bfe4 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 01:54:54 +0200 Subject: [PATCH 19/44] fix: use fetch instead of ajax --- spec-main/extensions-spec.ts | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 9217cd2f9919d..bda9fbc7754d4 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -16,19 +16,7 @@ describe('chrome extensions', () => { let server: http.Server; let url: string; before(async () => { - server = http.createServer(async (req, res) => { - if (req.url === '/serverRedirect') { - res.statusCode = 301; - res.setHeader('Location', 'http://' + req.rawHeaders[1]); - res.end(); - } else if (req.url === '/jquery') { - const html = await fs.promises.readFile(path.join(fixtures, 'pages', 'jquery.html')); - res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': html.length }); - res.end(html, 'utf-8'); - } else { - res.end(emptyPage); - } - }); + server = http.createServer((req, res) => res.end(emptyPage)); await new Promise(resolve => server.listen(0, '127.0.0.1', () => { url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; resolve(); @@ -189,14 +177,16 @@ describe('chrome extensions', () => { describe('chrome.webRequest', () => { async function ajax (contents: WebContents, url: string, options = {}) { - return contents.executeJavaScript(`ajax("${url}", ${JSON.stringify(options)})`); + return contents.executeJavaScript(`fetch("${url}", ${JSON.stringify(options)}).then((res) => { + if (!res.ok) throw new Error(res.status.toString()); + })`); } describe('chrome.webRequest.onBeforeRequest', () => { it('can cancel the request', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(`${url}/jquery`); + await w.loadURL(url); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); await expect(ajax(w.webContents, url)).to.eventually.be.rejectedWith('404'); @@ -207,7 +197,7 @@ describe('chrome extensions', () => { it('chrome.webRequest takes precedence over Electron webRequest', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(`${url}/jquery`); + await w.loadURL(url); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); customSession.webRequest.onBeforeRequest((details, callback) => { From 50829fb1019413ab8f49741659e70d059b11bcd3 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 12:04:30 +0200 Subject: [PATCH 20/44] fix: tests --- spec-main/extensions-spec.ts | 87 ++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index bda9fbc7754d4..3094db41bf19e 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -7,6 +7,8 @@ import * as path from 'path'; import * as fs from 'fs'; import { emittedOnce, emittedNTimes } from './events-helpers'; +const uuid = require('uuid'); + const fixtures = path.join(__dirname, 'fixtures'); describe('chrome extensions', () => { @@ -37,7 +39,7 @@ describe('chrome extensions', () => { // extension registry is redirected to the main session. so installing an // extension in an in-memory session results in it being installed in the // default session. - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); w.loadURL(url); @@ -49,7 +51,7 @@ describe('chrome extensions', () => { it('serializes a loaded extension', async () => { const extensionPath = path.join(fixtures, 'extensions', 'red-bg'); const manifest = JSON.parse(fs.readFileSync(path.join(extensionPath, 'manifest.json'), 'utf-8')); - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const extension = await customSession.loadExtension(extensionPath); expect(extension.id).to.be.a('string'); expect(extension.name).to.be.a('string'); @@ -60,7 +62,7 @@ describe('chrome extensions', () => { }); it('removes an extension', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); { const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); @@ -78,7 +80,7 @@ describe('chrome extensions', () => { }); it('lists loaded extensions in getAllExtensions', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); expect(customSession.getAllExtensions()).to.deep.equal([e]); customSession.removeExtension(e.id); @@ -86,13 +88,13 @@ describe('chrome extensions', () => { }); it('gets an extension by id', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const e = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); expect(customSession.getExtension(e.id)).to.deep.equal(e); }); it('confines an extension to the session it was loaded in', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); const w = new BrowserWindow({ show: false }); // not in the session w.loadURL(url); @@ -102,7 +104,7 @@ describe('chrome extensions', () => { }); it('loading an extension in a temporary session throws an error', async () => { - const customSession = session.fromPartition(require('uuid').v4()); + const customSession = session.fromPartition(uuid.v4()); await expect(customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg'))).to.eventually.be.rejectedWith('Extensions cannot be loaded in a temporary session'); }); @@ -116,7 +118,7 @@ describe('chrome extensions', () => { return result; }; beforeEach(async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-i18n')); w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); w.loadURL(url); @@ -136,7 +138,7 @@ describe('chrome extensions', () => { describe('chrome.runtime', () => { let content: any; before(async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-runtime')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); try { @@ -161,7 +163,7 @@ describe('chrome extensions', () => { describe('chrome.storage', () => { it('stores and retrieves a key', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-storage')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); try { @@ -176,44 +178,61 @@ describe('chrome extensions', () => { }); describe('chrome.webRequest', () => { - async function ajax (contents: WebContents, url: string, options = {}) { - return contents.executeJavaScript(`fetch("${url}", ${JSON.stringify(options)}).then((res) => { - if (!res.ok) throw new Error(res.status.toString()); - })`); + async function fetch(contents: WebContents, url: string) { + return contents.executeJavaScript(`fetch("${url}")`); } describe('chrome.webRequest.onBeforeRequest', () => { it('can cancel the request', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(url); - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - await expect(ajax(w.webContents, url)).to.eventually.be.rejectedWith('404'); + w.webContents.once('dom-ready', async () => { + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + + await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); + }); + + w.loadURL(url); + }); + + it('does not cancel the request', async () => { + const customSession = session.fromPartition(`persist:${uuid.v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + + w.webContents.once('dom-ready', async () => { + await expect(fetch(w.webContents, url)).to.have.property('status', 200); + }); + + w.loadURL(url); }); }); describe('chrome.webRequest.onBeforeRequest and Electron webRequest', () => { it('chrome.webRequest takes precedence over Electron webRequest', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - await w.loadURL(url); - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest((details, callback) => { - callback({ - cancel: true + w.webContents.once('dom-ready', async () => { + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + + customSession.webRequest.onBeforeRequest((details: any, callback: any) => { + callback({ + cancel: true + }); }); + + await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); - await expect(ajax(w.webContents, url)).to.eventually.be.rejectedWith('404'); + w.loadURL(url); }); }); }); describe('chrome.tabs', () => { it('executeScript', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); await w.loadURL(url); @@ -228,12 +247,12 @@ describe('chrome extensions', () => { }); it('connect', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); await w.loadURL(url); - const portName = require('uuid').v4(); + const portName = uuid.v4(); const message = { method: 'connectTab', args: [portName] }; w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); @@ -244,7 +263,7 @@ describe('chrome extensions', () => { }); it('sendMessage receives the response', async function () { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-api')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); await w.loadURL(url); @@ -262,7 +281,7 @@ describe('chrome extensions', () => { describe('background pages', () => { it('loads a lazy background page when sending a message', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } }); try { @@ -278,7 +297,7 @@ describe('chrome extensions', () => { }); it('can use extension.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w.loadURL(`chrome-extension://${id}/page-get-background.html`); @@ -287,7 +306,7 @@ describe('chrome extensions', () => { }); it('can use extension.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w.loadURL(`chrome-extension://${id}/page-get-background.html`); @@ -296,7 +315,7 @@ describe('chrome extensions', () => { }); it('can use runtime.getBackgroundPage from a ui page', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w.loadURL(`chrome-extension://${id}/page-runtime-get-background.html`); @@ -334,7 +353,7 @@ describe('chrome extensions', () => { }; it('loads a devtools extension', async () => { - const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + const customSession = session.fromPartition(`persist:${uuid.v4()}`); customSession.loadExtension(path.join(fixtures, 'extensions', 'devtools-extension')); const winningMessage = emittedOnce(ipcMain, 'winning'); const w = new BrowserWindow({ show: true, webPreferences: { session: customSession, nodeIntegration: true } }); From e7eb2a929fbb4d27e81fa2c7e4865b3cde64d799 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 13:22:46 +0200 Subject: [PATCH 21/44] feat: websocket tests --- spec-main/extensions-spec.ts | 79 +++++++++++++++---- .../chrome-webRequest-wss/background.js | 12 +++ .../chrome-webRequest-wss/manifest.json | 10 +++ 3 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 spec-main/fixtures/extensions/chrome-webRequest-wss/background.js create mode 100644 spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 3094db41bf19e..8fc7a371a918c 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -5,6 +5,7 @@ import * as http from 'http'; import { AddressInfo } from 'net'; import * as path from 'path'; import * as fs from 'fs'; +import * as WebSocket from 'ws'; import { emittedOnce, emittedNTimes } from './events-helpers'; const uuid = require('uuid'); @@ -17,10 +18,22 @@ describe('chrome extensions', () => { // NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default. let server: http.Server; let url: string; + let port: string; before(async () => { server = http.createServer((req, res) => res.end(emptyPage)); + + const wss = new WebSocket.Server({ noServer: true }); + wss.on('connection', function connection (ws) { + ws.on('message', function incoming (message) { + if (message === 'foo') { + ws.send('bar'); + } + }); + }); + await new Promise(resolve => server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + port = String((server.address() as AddressInfo).port); + url = `http://127.0.0.1:${port}`; resolve(); })); }); @@ -178,12 +191,12 @@ describe('chrome extensions', () => { }); describe('chrome.webRequest', () => { - async function fetch(contents: WebContents, url: string) { + async function fetch (contents: WebContents, url: string) { return contents.executeJavaScript(`fetch("${url}")`); } - describe('chrome.webRequest.onBeforeRequest', () => { - it('can cancel the request', async () => { + describe('onBeforeRequest', () => { + it('can cancel http requests', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); @@ -196,7 +209,7 @@ describe('chrome extensions', () => { w.loadURL(url); }); - it('does not cancel the request', async () => { + it('does not cancel http requests', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); @@ -208,24 +221,60 @@ describe('chrome extensions', () => { }); }); - describe('chrome.webRequest.onBeforeRequest and Electron webRequest', () => { - it('chrome.webRequest takes precedence over Electron webRequest', async () => { + it('takes precedence over Electron webRequest - http', async () => { + const customSession = session.fromPartition(`persist:${uuid.v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + + w.webContents.once('dom-ready', async () => { + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + + customSession.webRequest.onBeforeRequest(() => { + throw new Error('Electron onBeforeRequest callback has been called'); + }); + + await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); + }); + + w.loadURL(url); + }); + + it('takes precedence over Electron webRequest - WebSocket', async () => { + const customSession = session.fromPartition(`persist:${uuid.v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + + w.webContents.once('dom-ready', async () => { + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + + customSession.webRequest.onBeforeSendHeaders(() => { + throw new Error('Electron onBeforeSendHeaders callback has been called'); + }); + + customSession.webRequest.onSendHeaders((details) => { + if (details.url.startsWith('ws://')) { + expect(details.requestHeaders.foo).be.equal('bar'); + } + }); + }); + + w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + }); + + describe('WebSocket', () => { + it('can be proxied', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); w.webContents.once('dom-ready', async () => { - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - customSession.webRequest.onBeforeRequest((details: any, callback: any) => { - callback({ - cancel: true - }); + customSession.webRequest.onSendHeaders((details) => { + if (details.url.startsWith('ws://')) { + expect(details.requestHeaders.foo).be.equal('bar'); + } }); - - await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); - w.loadURL(url); + w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); }); }); }); diff --git a/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js new file mode 100644 index 0000000000000..9c3b117cf7762 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js @@ -0,0 +1,12 @@ +/* global chrome */ + +chrome.webRequest.onBeforeSendHeaders.addListener( + (details) => { + if (details.requestHeaders) { + details.requestHeaders.foo = 'bar'; + } + return { cancel: false, requestHeaders }; + }, + { urls: [''] }, + ['blocking'] +); diff --git a/spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json b/spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json new file mode 100644 index 0000000000000..c1723d2118850 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-webRequest-wss/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "chrome-webRequest", + "version": "1.0", + "background": { + "scripts": ["background.js"], + "persistent": true + }, + "permissions": ["webRequest", "webRequestBlocking", ""], + "manifest_version": 2 +} From 9b9a60bac757d4efb21fc62c7eafc008e92c104b Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 14:02:40 +0200 Subject: [PATCH 22/44] fix: no requestHeaders --- .../fixtures/extensions/chrome-webRequest-wss/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js index 9c3b117cf7762..47bcea29d2f25 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js +++ b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js @@ -5,7 +5,7 @@ chrome.webRequest.onBeforeSendHeaders.addListener( if (details.requestHeaders) { details.requestHeaders.foo = 'bar'; } - return { cancel: false, requestHeaders }; + return { cancel: false, requestHeaders: details.requestHeaders }; }, { urls: [''] }, ['blocking'] From f0c155825c49271fb281ee3d62c66bb6ef7e5736 Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Fri, 5 Jun 2020 23:31:37 +0200 Subject: [PATCH 23/44] Apply suggestions from code review Co-authored-by: Jeremy Rose --- spec-main/extensions-spec.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 8fc7a371a918c..f5de79b949f97 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -192,7 +192,7 @@ describe('chrome extensions', () => { describe('chrome.webRequest', () => { async function fetch (contents: WebContents, url: string) { - return contents.executeJavaScript(`fetch("${url}")`); + return contents.executeJavaScript(`fetch(${JSON.stringify(url)})`); } describe('onBeforeRequest', () => { @@ -200,24 +200,19 @@ describe('chrome extensions', () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - w.webContents.once('dom-ready', async () => { - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - - await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); - }); - w.loadURL(url); + await emittedOnce(w.webContents, 'dom-ready') + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); - it('does not cancel http requests', async () => { + it('does not cancel http requests when no extension loaded', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - w.webContents.once('dom-ready', async () => { - await expect(fetch(w.webContents, url)).to.have.property('status', 200); - }); - w.loadURL(url); + await emittedOnce(w.webContents, 'dom-ready'); + await expect(fetch(w.webContents, url)).to.have.property('status', 200); }); }); From d37ff74133cf8211b7c7b51f1ef97f1b105a0245 Mon Sep 17 00:00:00 2001 From: sentialx Date: Fri, 5 Jun 2020 23:36:14 +0200 Subject: [PATCH 24/44] fix: remaining tests --- spec-main/extensions-spec.ts | 59 +++++++++++++++--------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index f5de79b949f97..8f39a8b1ff817 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -201,7 +201,7 @@ describe('chrome extensions', () => { const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); w.loadURL(url); - await emittedOnce(w.webContents, 'dom-ready') + await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); @@ -220,38 +220,30 @@ describe('chrome extensions', () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - w.webContents.once('dom-ready', async () => { - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - - customSession.webRequest.onBeforeRequest(() => { - throw new Error('Electron onBeforeRequest callback has been called'); - }); - - await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); - }); - w.loadURL(url); + await emittedOnce(w.webContents, 'dom-ready'); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + customSession.webRequest.onBeforeRequest(() => { + throw new Error('Electron onBeforeRequest callback has been called'); + }); + await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); it('takes precedence over Electron webRequest - WebSocket', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - w.webContents.once('dom-ready', async () => { - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - - customSession.webRequest.onBeforeSendHeaders(() => { - throw new Error('Electron onBeforeSendHeaders callback has been called'); - }); - - customSession.webRequest.onSendHeaders((details) => { - if (details.url.startsWith('ws://')) { - expect(details.requestHeaders.foo).be.equal('bar'); - } - }); - }); - w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + await emittedOnce(w.webContents, 'dom-ready'); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + customSession.webRequest.onBeforeSendHeaders(() => { + throw new Error('Electron onBeforeSendHeaders callback has been called'); + }); + customSession.webRequest.onSendHeaders((details) => { + if (details.url.startsWith('ws://')) { + expect(details.requestHeaders.foo).be.equal('bar'); + } + }); }); describe('WebSocket', () => { @@ -259,17 +251,14 @@ describe('chrome extensions', () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - w.webContents.once('dom-ready', async () => { - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - - customSession.webRequest.onSendHeaders((details) => { - if (details.url.startsWith('ws://')) { - expect(details.requestHeaders.foo).be.equal('bar'); - } - }); - }); - w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + await emittedOnce(w.webContents, 'dom-ready'); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + customSession.webRequest.onSendHeaders((details) => { + if (details.url.startsWith('ws://')) { + expect(details.requestHeaders.foo).be.equal('bar'); + } + }); }); }); }); From a1cdc70435d953e0ad45fb8316b08dabbadc053d Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 6 Jun 2020 00:04:35 +0200 Subject: [PATCH 25/44] test: add a case --- spec-main/extensions-spec.ts | 24 +++++++++++++++++++ .../chrome-webRequest-wss/background.js | 2 +- .../chrome-webRequest/background.js | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 8f39a8b1ff817..468ea542e1bca 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -229,6 +229,18 @@ describe('chrome extensions', () => { await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); + it('does not take precedence over Electron webRequest with different filter - http', async (done) => { + const customSession = session.fromPartition(`persist:${uuid.v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + + w.loadURL(url); + await emittedOnce(w.webContents, 'dom-ready'); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + customSession.webRequest.onBeforeRequest({ urls: ['*.google.com'] }, async () => { + done(); + }); + }); + it('takes precedence over Electron webRequest - WebSocket', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); @@ -246,6 +258,18 @@ describe('chrome extensions', () => { }); }); + it('does not take precedence over Electron webRequest with different filter - WebSocket', async (done) => { + const customSession = session.fromPartition(`persist:${uuid.v4()}`); + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); + + w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + await emittedOnce(w.webContents, 'dom-ready'); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + customSession.webRequest.onBeforeSendHeaders({ urls: ['*.google.com'] }, () => { + done(); + }); + }); + describe('WebSocket', () => { it('can be proxied', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); diff --git a/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js index 47bcea29d2f25..94ddee976b1ee 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js +++ b/spec-main/fixtures/extensions/chrome-webRequest-wss/background.js @@ -7,6 +7,6 @@ chrome.webRequest.onBeforeSendHeaders.addListener( } return { cancel: false, requestHeaders: details.requestHeaders }; }, - { urls: [''] }, + { urls: ['*://127.0.0.1:*'] }, ['blocking'] ); diff --git a/spec-main/fixtures/extensions/chrome-webRequest/background.js b/spec-main/fixtures/extensions/chrome-webRequest/background.js index e713eef2bca22..868223758cbca 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest/background.js +++ b/spec-main/fixtures/extensions/chrome-webRequest/background.js @@ -4,6 +4,6 @@ chrome.webRequest.onBeforeRequest.addListener( (details) => { return { cancel: false }; }, - { urls: [''] }, + { urls: ['*://127.0.0.1:*'] }, ['blocking'] ); From a6ded49c8f336dc2cb294ac525f1714564386105 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 6 Jun 2020 00:10:29 +0200 Subject: [PATCH 26/44] fix: remove DCHECK --- shell/browser/electron_browser_client.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 7a215771db3c9..0c5e35d252cf8 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1446,9 +1446,7 @@ void ElectronBrowserClient::CreateWebSocket( extensions::BrowserContextKeyedAPIFactory::Get( browser_context); - DCHECK(web_request_api); - - if (web_request_api->MayHaveProxies()) { + if (web_request_api && web_request_api->MayHaveProxies()) { web_request_api->ProxyWebSocket(frame, std::move(factory), url, site_for_cookies.RepresentativeUrl(), user_agent, std::move(handshake_client)); From 5bb508ab539fb917dec2b15deae51dd53862a3ab Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 6 Jun 2020 00:27:17 +0200 Subject: [PATCH 27/44] fix: precedence test --- spec-main/extensions-spec.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 468ea542e1bca..1a87fa01bf90d 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -229,16 +229,17 @@ describe('chrome extensions', () => { await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); - it('does not take precedence over Electron webRequest with different filter - http', async (done) => { + it('does not take precedence over Electron webRequest with different filter - http', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); w.loadURL(url); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest({ urls: ['*.google.com'] }, async () => { - done(); + customSession.webRequest.onBeforeRequest({ urls: ['*.google.com'] }, async (details, callback) => { + callback({ cancel: true }); }); + await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); }); it('takes precedence over Electron webRequest - WebSocket', async () => { @@ -258,16 +259,17 @@ describe('chrome extensions', () => { }); }); - it('does not take precedence over Electron webRequest with different filter - WebSocket', async (done) => { + it('does not take precedence over Electron webRequest with different filter - WebSocket', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - customSession.webRequest.onBeforeSendHeaders({ urls: ['*.google.com'] }, () => { - done(); + customSession.webRequest.onBeforeRequest({ urls: ['*.google.com'] }, (details, callback) => { + callback({ cancel: true }); }); + await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); }); describe('WebSocket', () => { From 03ee78d021bcc87b7b9ce4af2138bc2fd7f8a575 Mon Sep 17 00:00:00 2001 From: sentialx Date: Sat, 6 Jun 2020 01:31:06 +0200 Subject: [PATCH 28/44] fix: test url filters --- spec-main/extensions-spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 1a87fa01bf90d..c5584917ff707 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -236,7 +236,7 @@ describe('chrome extensions', () => { w.loadURL(url); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest({ urls: ['*.google.com'] }, async (details, callback) => { + customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com'] }, async (details, callback) => { callback({ cancel: true }); }); await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); @@ -266,7 +266,7 @@ describe('chrome extensions', () => { w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - customSession.webRequest.onBeforeRequest({ urls: ['*.google.com'] }, (details, callback) => { + customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com'] }, (details, callback) => { callback({ cancel: true }); }); await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); From 38906dd5e035c718e82686e945a0bd2817120776 Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 8 Jul 2020 18:42:42 +0200 Subject: [PATCH 29/44] fix: webRequest events not firing --- shell/browser/extensions/electron_extension_loader.cc | 3 +++ shell/common/extensions/electron_extensions_client.cc | 4 +--- spec-main/extensions-spec.ts | 8 ++++---- .../fixtures/extensions/chrome-webRequest/background.js | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index b9b8294abc303..a441dd034b99d 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -110,6 +110,9 @@ void ElectronExtensionLoader::FinishExtensionLoad( if (extension) { extension_registrar_.AddExtension(extension); } + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); + extension_prefs->OnExtensionInstalled(extension.get(), Extension::ENABLED, + syncer::StringOrdinal(), std::string()); std::move(cb).Run(extension.get(), result.second); } diff --git a/shell/common/extensions/electron_extensions_client.cc b/shell/common/extensions/electron_extensions_client.cc index a2957f72debea..b6dcd3db8a788 100644 --- a/shell/common/extensions/electron_extensions_client.cc +++ b/shell/common/extensions/electron_extensions_client.cc @@ -130,9 +130,7 @@ const GURL& ElectronExtensionsClient::GetWebstoreUpdateURL() const { } bool ElectronExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { - // TODO(rockot): Maybe we want to do something else here. For now we accept - // any URL as a blacklist URL because we don't really care. - return true; + return false; } } // namespace electron diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index c5584917ff707..b9b71caa42e28 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -191,7 +191,7 @@ describe('chrome extensions', () => { }); describe('chrome.webRequest', () => { - async function fetch (contents: WebContents, url: string) { + function fetch (contents: WebContents, url: string) { return contents.executeJavaScript(`fetch(${JSON.stringify(url)})`); } @@ -212,7 +212,7 @@ describe('chrome extensions', () => { w.loadURL(url); await emittedOnce(w.webContents, 'dom-ready'); - await expect(fetch(w.webContents, url)).to.have.property('status', 200); + await expect(fetch(w.webContents, url)).to.not.be.rejectedWith(TypeError); }); }); @@ -236,7 +236,7 @@ describe('chrome extensions', () => { w.loadURL(url); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com'] }, async (details, callback) => { + customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com/*'] }, async (details, callback) => { callback({ cancel: true }); }); await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); @@ -266,7 +266,7 @@ describe('chrome extensions', () => { w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com'] }, (details, callback) => { + customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com/*'] }, (details, callback) => { callback({ cancel: true }); }); await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); diff --git a/spec-main/fixtures/extensions/chrome-webRequest/background.js b/spec-main/fixtures/extensions/chrome-webRequest/background.js index 868223758cbca..ba6f9f3f610ea 100644 --- a/spec-main/fixtures/extensions/chrome-webRequest/background.js +++ b/spec-main/fixtures/extensions/chrome-webRequest/background.js @@ -2,7 +2,7 @@ chrome.webRequest.onBeforeRequest.addListener( (details) => { - return { cancel: false }; + return { cancel: true }; }, { urls: ['*://127.0.0.1:*'] }, ['blocking'] From d7f4bbb2a060f581896b84dc5f61c3f597757243 Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Sat, 11 Jul 2020 18:46:49 +0200 Subject: [PATCH 30/44] fix: requests made from service workers --- shell/browser/electron_browser_client.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 0103f7e67ea80..fcfd6d2c5bec6 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1420,6 +1420,9 @@ bool ElectronBrowserClient::WillInterceptWebSocket( return false; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (!frame) { + return; + } const auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); @@ -1444,6 +1447,9 @@ void ElectronBrowserClient::CreateWebSocket( auto* browser_context = frame->GetProcess()->GetBrowserContext(); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (!frame) { + return; + } auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); From 540ef6e55e5ba8c165eb8905eb27a50bc0a2e24f Mon Sep 17 00:00:00 2001 From: sentialx Date: Mon, 20 Jul 2020 15:29:15 +0200 Subject: [PATCH 31/44] fix: build errors --- shell/browser/electron_browser_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 53b2fb27179a0..1dcbeccbdc418 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1414,7 +1414,7 @@ bool ElectronBrowserClient::WillInterceptWebSocket( #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) if (!frame) { - return; + return false; } const auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( From 39eb683c0f10e0fb8b6342e256042a04dff2e9ca Mon Sep 17 00:00:00 2001 From: sentialx Date: Thu, 30 Jul 2020 14:27:18 +0200 Subject: [PATCH 32/44] docs: add a note about chrome.webRequest --- docs/api/extensions.md | 214 +++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 104 deletions(-) diff --git a/docs/api/extensions.md b/docs/api/extensions.md index 4a93951024921..16cf76b242f72 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -1,104 +1,110 @@ -# Chrome Extension Support - -Electron supports a subset of the [Chrome Extensions -API][chrome-extensions-api-index], primarily to support DevTools extensions and -Chromium-internal extensions, but it also happens to support some other -extension capabilities. - -[chrome-extensions-api-index]: https://developer.chrome.com/extensions/api_index - -> **Note:** Electron does not support arbitrary Chrome extensions from the -> store, and it is a **non-goal** of the Electron project to be perfectly -> compatible with Chrome's implementation of Extensions. - -## Loading extensions - -Electron only supports loading unpacked extensions (i.e., `.crx` files do not -work). Extensions are installed per-`session`. To load an extension, call -[`ses.loadExtension`](session.md#sesloadextensionpath): - -```js -const { session } = require('electron') - -session.loadExtension('path/to/unpacked/extension').then(({ id }) => { - // ... -}) -``` - -Loaded extensions will not be automatically remembered across exits; if you do -not call `loadExtension` when the app runs, the extension will not be loaded. - -Note that loading extensions is only supported in persistent sessions. -Attempting to load an extension into an in-memory session will throw an error. - -See the [`session`](session.md) documentation for more information about -loading, unloading, and querying active extensions. - -## Supported Extensions APIs - -We support the following extensions APIs, with some caveats. Other APIs may -additionally be supported, but support for any APIs not listed here is -provisional and may be removed. - -### `chrome.devtools.inspectedWindow` - -All features of this API are supported. - -### `chrome.devtools.network` - -All features of this API are supported. - -### `chrome.devtools.panels` - -All features of this API are supported. - -### `chrome.extension` - -The following properties of `chrome.extension` are supported: - -- `chrome.extension.lastError` - -The following methods of `chrome.extension` are supported: - -- `chrome.extension.getURL` -- `chrome.extension.getBackgroundPage` - -### `chrome.runtime` - -The following properties of `chrome.runtime` are supported: - -- `chrome.runtime.lastError` -- `chrome.runtime.id` - -The following methods of `chrome.runtime` are supported: - -- `chrome.runtime.getBackgroundPage` -- `chrome.runtime.getManifest` -- `chrome.runtime.getURL` -- `chrome.runtime.connect` -- `chrome.runtime.sendMessage` - -The following events of `chrome.runtime` are supported: - -- `chrome.runtime.onStartup` -- `chrome.runtime.onInstalled` -- `chrome.runtime.onSuspend` -- `chrome.runtime.onSuspendCanceled` -- `chrome.runtime.onConnect` -- `chrome.runtime.onMessage` - -### `chrome.storage` - -Only `chrome.storage.local` is supported; `chrome.storage.sync` and -`chrome.storage.managed` are not. - -### `chrome.tabs` - -The following methods of `chrome.tabs` are supported: - -- `chrome.tabs.sendMessage` -- `chrome.tabs.executeScript` - -> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active -> tab". Since Electron has no such concept, passing `-1` as a tab ID is not -> supported and will raise an error. +# Chrome Extension Support + +Electron supports a subset of the [Chrome Extensions +API][chrome-extensions-api-index], primarily to support DevTools extensions and +Chromium-internal extensions, but it also happens to support some other +extension capabilities. + +[chrome-extensions-api-index]: https://developer.chrome.com/extensions/api_index + +> **Note:** Electron does not support arbitrary Chrome extensions from the +> store, and it is a **non-goal** of the Electron project to be perfectly +> compatible with Chrome's implementation of Extensions. + +## Loading extensions + +Electron only supports loading unpacked extensions (i.e., `.crx` files do not +work). Extensions are installed per-`session`. To load an extension, call +[`ses.loadExtension`](session.md#sesloadextensionpath): + +```js +const { session } = require('electron') + +session.loadExtension('path/to/unpacked/extension').then(({ id }) => { + // ... +}) +``` + +Loaded extensions will not be automatically remembered across exits; if you do +not call `loadExtension` when the app runs, the extension will not be loaded. + +Note that loading extensions is only supported in persistent sessions. +Attempting to load an extension into an in-memory session will throw an error. + +See the [`session`](session.md) documentation for more information about +loading, unloading, and querying active extensions. + +## Supported Extensions APIs + +We support the following extensions APIs, with some caveats. Other APIs may +additionally be supported, but support for any APIs not listed here is +provisional and may be removed. + +### `chrome.devtools.inspectedWindow` + +All features of this API are supported. + +### `chrome.devtools.network` + +All features of this API are supported. + +### `chrome.devtools.panels` + +All features of this API are supported. + +### `chrome.extension` + +The following properties of `chrome.extension` are supported: + +- `chrome.extension.lastError` + +The following methods of `chrome.extension` are supported: + +- `chrome.extension.getURL` +- `chrome.extension.getBackgroundPage` + +### `chrome.runtime` + +The following properties of `chrome.runtime` are supported: + +- `chrome.runtime.lastError` +- `chrome.runtime.id` + +The following methods of `chrome.runtime` are supported: + +- `chrome.runtime.getBackgroundPage` +- `chrome.runtime.getManifest` +- `chrome.runtime.getURL` +- `chrome.runtime.connect` +- `chrome.runtime.sendMessage` + +The following events of `chrome.runtime` are supported: + +- `chrome.runtime.onStartup` +- `chrome.runtime.onInstalled` +- `chrome.runtime.onSuspend` +- `chrome.runtime.onSuspendCanceled` +- `chrome.runtime.onConnect` +- `chrome.runtime.onMessage` + +### `chrome.storage` + +Only `chrome.storage.local` is supported; `chrome.storage.sync` and +`chrome.storage.managed` are not. + +### `chrome.tabs` + +The following methods of `chrome.tabs` are supported: + +- `chrome.tabs.sendMessage` +- `chrome.tabs.executeScript` + +> **Note:** In Chrome, passing `-1` as a tab ID signifies the "currently active +> tab". Since Electron has no such concept, passing `-1` as a tab ID is not +> supported and will raise an error. + +### `chrome.webRequest` + +All features of this API are supported. + +> **NOTE:** `chrome.webRequest` takes precedence over Electron's [`webRequest`](web-request.md) module. From b8b30eeebf9dcff96480ac5dba3b6ea13ec2099b Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Sun, 6 Sep 2020 20:22:35 +0200 Subject: [PATCH 33/44] Update extensions.md --- docs/api/extensions.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/api/extensions.md b/docs/api/extensions.md index 1cfa3326dfc5f..6986d4576b1e7 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -103,6 +103,18 @@ The following methods of `chrome.tabs` are supported: > tab". Since Electron has no such concept, passing `-1` as a tab ID is not > supported and will raise an error. +### `chrome.management` + +The following methods of `chrome.management` are supported: + +- `chrome.management.getAll` +- `chrome.management.get` +- `chrome.management.getSelf` +- `chrome.management.getPermissionWarningsById` +- `chrome.management.getPermissionWarningsByManifest` +- `chrome.management.onEnabled` +- `chrome.management.onDisabled` + ### `chrome.webRequest` All features of this API are supported. From 6967b36dadb31b58f83b89a31a00812e136ad1e2 Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Sun, 6 Sep 2020 20:26:43 +0200 Subject: [PATCH 34/44] Update electron_browser_client.cc --- shell/browser/electron_browser_client.cc | 36 +++++++++--------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 7e5c621a7ded3..35cd9edf165bc 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1424,20 +1424,18 @@ bool ElectronBrowserClient::WillInterceptWebSocket( // BrowserContextKeyedAPI factories for e.g. WebRequest. if (!web_request.get()) return false; - + + bool has_listener = web_request->HasListener(); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - if (!frame) { - return false; - } const auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); if (web_request_api) - return web_request->HasListener() || web_request_api->MayHaveProxies(); + has_listener ||= web_request_api->MayHaveProxies(); #endif - return web_request->HasListener(); + return has_listener; } void ElectronBrowserClient::CreateWebSocket( @@ -1453,9 +1451,6 @@ void ElectronBrowserClient::CreateWebSocket( auto* browser_context = frame->GetProcess()->GetBrowserContext(); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - if (!frame) { - return; - } auto* web_request_api = extensions::BrowserContextKeyedAPIFactory::Get( browser_context); @@ -1496,19 +1491,16 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( extensions::BrowserContextKeyedAPIFactory::Get( browser_context); - // NOTE: Some unit test environments do not initialize - // BrowserContextKeyedAPI factories for e.g. WebRequest. - if (web_request_api) { - bool use_proxy_for_web_request = - web_request_api->MaybeProxyURLLoaderFactory( - browser_context, frame_host, render_process_id, type, navigation_id, - factory_receiver, header_client); - - if (bypass_redirect_checks) - *bypass_redirect_checks = use_proxy_for_web_request; - if (use_proxy_for_web_request) - return true; - } + DCHECK(web_request_api); + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame_host, render_process_id, type, navigation_id, + factory_receiver, header_client); + + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + if (use_proxy_for_web_request) + return true; #endif v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); From df959931ed1fef6f6dfbe5247d6f72620aaf501a Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Sun, 6 Sep 2020 20:29:50 +0200 Subject: [PATCH 35/44] Update extensions-spec.ts --- spec-main/extensions-spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 12babc19342df..289bb5e343a36 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -286,10 +286,10 @@ describe('chrome extensions', () => { w.loadURL(url); await emittedOnce(w.webContents, 'dom-ready'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com/*'] }, async (details, callback) => { + customSession.webRequest.onBeforeRequest({ urls: ['*://*.does.not.exist.com/*'] }, async (details, callback) => { callback({ cancel: true }); }); - await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); + await expect(fetch(w.webContents, 'http://does.not.exist.com')).to.eventually.be.rejectedWith(TypeError); }); it('takes precedence over Electron webRequest - WebSocket', async () => { From 39894b3c77227dc727a8fdb445e1f9632ccc1b55 Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Mon, 7 Sep 2020 18:53:48 +0200 Subject: [PATCH 36/44] Update electron_browser_client.cc --- shell/browser/electron_browser_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 35cd9edf165bc..d4d73427b2d84 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1432,7 +1432,7 @@ bool ElectronBrowserClient::WillInterceptWebSocket( browser_context); if (web_request_api) - has_listener ||= web_request_api->MayHaveProxies(); + has_listener |= web_request_api->MayHaveProxies(); #endif return has_listener; From b5d407f24ae423b13194db0cb0cf48c7cbd28a96 Mon Sep 17 00:00:00 2001 From: samuelmaddock Date: Tue, 29 Sep 2020 04:10:10 -0400 Subject: [PATCH 37/44] refactor: cleanup webRequest tests --- spec-main/extensions-spec.ts | 52 +++++++++++------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index d2e26981a4ae7..0f7f6f8749b0a 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { app, session, BrowserWindow, ipcMain, WebContents, Extension } from 'electron/main'; +import { app, session, BrowserWindow, ipcMain, WebContents, Extension, Session } from 'electron/main'; import { closeAllWindows, closeWindow } from './window-helpers'; import * as http from 'http'; import { AddressInfo } from 'net'; @@ -265,33 +265,29 @@ describe('chrome extensions', () => { return contents.executeJavaScript(`fetch(${JSON.stringify(url)})`); } + let customSession: Session; + let w: BrowserWindow; + + beforeEach(() => { + customSession = session.fromPartition(`persist:${uuid.v4()}`); + w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true, contextIsolation: true } }); + }); + describe('onBeforeRequest', () => { it('can cancel http requests', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadURL(url); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadURL(url); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); }); it('does not cancel http requests when no extension loaded', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadURL(url); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadURL(url); await expect(fetch(w.webContents, url)).to.not.be.rejectedWith(TypeError); }); }); it('takes precedence over Electron webRequest - http', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadURL(url); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadURL(url); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); customSession.webRequest.onBeforeRequest(() => { throw new Error('Electron onBeforeRequest callback has been called'); @@ -300,11 +296,7 @@ describe('chrome extensions', () => { }); it('does not take precedence over Electron webRequest with different filter - http', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadURL(url); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadURL(url); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); customSession.webRequest.onBeforeRequest({ urls: ['*://*.does.not.exist.com/*'] }, async (details, callback) => { callback({ cancel: true }); @@ -313,11 +305,7 @@ describe('chrome extensions', () => { }); it('takes precedence over Electron webRequest - WebSocket', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); customSession.webRequest.onBeforeSendHeaders(() => { throw new Error('Electron onBeforeSendHeaders callback has been called'); @@ -330,11 +318,7 @@ describe('chrome extensions', () => { }); it('does not take precedence over Electron webRequest with different filter - WebSocket', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com/*'] }, (details, callback) => { callback({ cancel: true }); @@ -344,11 +328,7 @@ describe('chrome extensions', () => { describe('WebSocket', () => { it('can be proxied', async () => { - const customSession = session.fromPartition(`persist:${uuid.v4()}`); - const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); - - w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); - await emittedOnce(w.webContents, 'dom-ready'); + await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); customSession.webRequest.onSendHeaders((details) => { if (details.url.startsWith('ws://')) { From 8d837e4b9fb91bd75dbdb91ed98171fb40996bfe Mon Sep 17 00:00:00 2001 From: samuelmaddock Date: Wed, 30 Sep 2020 03:22:21 -0400 Subject: [PATCH 38/44] refactor: write minimal extension prefs to satisfy DCHECK --- .../extensions/electron_extension_loader.cc | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index 9190b1f8f9167..55471ec7367b9 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -12,12 +12,15 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/task_runner_util.h" #include "base/threading/thread_restrictions.h" +#include "base/time/time.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/pref_names.h" #include "extensions/common/file_util.h" namespace extensions { @@ -110,9 +113,26 @@ void ElectronExtensionLoader::FinishExtensionLoad( if (extension) { extension_registrar_.AddExtension(extension); } - ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); - extension_prefs->OnExtensionInstalled(extension.get(), Extension::ENABLED, - syncer::StringOrdinal(), std::string()); + + // Write extension install time to ExtensionPrefs. This is required by + // WebRequestAPI which calls extensions::ExtensionPrefs::GetInstallTime. + // + // Implementation for writing the pref was based on + // PreferenceAPIBase::SetExtensionControlledPref. + { + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); + ExtensionPrefs::ScopedDictionaryUpdate update( + extension_prefs, extension.get()->id(), + extensions::pref_names::kPrefPreferences); + auto preference = update.Create(); + const base::Time install_time = base::Time().Now(); + std::unique_ptr install_time_str = + base::Value(base::NumberToString(install_time.ToInternalValue())) + .CreateDeepCopy(); + preference->SetWithoutPathExpansion("install_time", + std::move(install_time_str)); + } + std::move(cb).Run(extension.get(), result.second); } From 436235f0ffa9a1cc32ea96a1d5b96fdcdf481fb6 Mon Sep 17 00:00:00 2001 From: sentialx Date: Wed, 18 Nov 2020 13:07:08 +0100 Subject: [PATCH 39/44] fix: make electron webrequest take priority over chrome --- shell/browser/api/electron_api_web_request.cc | 10 +++- shell/browser/electron_browser_client.cc | 57 ++++++++++--------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/shell/browser/api/electron_api_web_request.cc b/shell/browser/api/electron_api_web_request.cc index 389275a5d1f3e..2e45984a4f234 100644 --- a/shell/browser/api/electron_api_web_request.cc +++ b/shell/browser/api/electron_api_web_request.cc @@ -89,18 +89,22 @@ struct UserData : public base::SupportsUserData::Data { }; // Test whether the URL of |request| matches |patterns|. -bool MatchesFilterCondition(extensions::WebRequestInfo* info, - const std::set& patterns) { +bool MatchesFilterCondition(GURL url, const std::set& patterns) { if (patterns.empty()) return true; for (const auto& pattern : patterns) { - if (pattern.MatchesURL(info->url)) + if (pattern.MatchesURL(url)) return true; } return false; } +bool MatchesFilterCondition(extensions::WebRequestInfo* info, + const std::set& patterns) { + return MatchesFilterCondition(info, patterns); +} + // Convert HttpResponseHeaders to V8. // // Note that while we already have converters for HttpResponseHeaders, we can diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index ff1bf1556bd11..fa813dc8c8362 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1493,21 +1493,23 @@ void ElectronBrowserClient::CreateWebSocket( v8::HandleScope scope(isolate); auto* browser_context = frame->GetProcess()->GetBrowserContext(); -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - auto* web_request_api = - extensions::BrowserContextKeyedAPIFactory::Get( - browser_context); + auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); + DCHECK(web_request.get()); - if (web_request_api && web_request_api->MayHaveProxies()) { - web_request_api->ProxyWebSocket(frame, std::move(factory), url, - site_for_cookies.RepresentativeUrl(), - user_agent, std::move(handshake_client)); - return; +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (!web_request.HasListener()) { + auto* web_request_api = extensions::BrowserContextKeyedAPIFactory< + extensions::WebRequestAPI>::Get(browser_context); + + if (web_request_api && web_request_api->MayHaveProxies()) { + web_request_api->ProxyWebSocket(frame, std::move(factory), url, + site_for_cookies.RepresentativeUrl(), + user_agent, std::move(handshake_client)); + return; + } } #endif - auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); - DCHECK(web_request.get()); ProxyingWebSocket::StartProxying( web_request.get(), std::move(factory), url, site_for_cookies.RepresentativeUrl(), user_agent, @@ -1529,26 +1531,29 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( bool* bypass_redirect_checks, bool* disable_secure_dns, network::mojom::URLLoaderFactoryOverridePtr* factory_override) { -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - auto* web_request_api = - extensions::BrowserContextKeyedAPIFactory::Get( - browser_context); - - DCHECK(web_request_api); - bool use_proxy_for_web_request = web_request_api->MaybeProxyURLLoaderFactory( - browser_context, frame_host, render_process_id, type, navigation_id, - factory_receiver, header_client); - - if (bypass_redirect_checks) - *bypass_redirect_checks = use_proxy_for_web_request; - if (use_proxy_for_web_request) - return true; -#endif v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context); DCHECK(web_request.get()); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (!web_request.HasListener()) { + auto* web_request_api = extensions::BrowserContextKeyedAPIFactory< + extensions::WebRequestAPI>::Get(browser_context); + + DCHECK(web_request_api); + bool use_proxy_for_web_request = + web_request_api->MaybeProxyURLLoaderFactory( + browser_context, frame_host, render_process_id, type, navigation_id, + factory_receiver, header_client); + + if (bypass_redirect_checks) + *bypass_redirect_checks = use_proxy_for_web_request; + if (use_proxy_for_web_request) + return true; + } +#endif + auto proxied_receiver = std::move(*factory_receiver); mojo::PendingRemote target_factory_remote; *factory_receiver = target_factory_remote.InitWithNewPipeAndPassReceiver(); From 56ced0f4a85b781940681b4aee059a11893c8091 Mon Sep 17 00:00:00 2001 From: sentialx Date: Thu, 19 Nov 2020 12:15:04 +0100 Subject: [PATCH 40/44] fix: tests --- shell/browser/api/electron_api_web_request.cc | 10 ++-- shell/browser/electron_browser_client.cc | 6 +-- spec-main/extensions-spec.ts | 53 +++++++------------ 3 files changed, 26 insertions(+), 43 deletions(-) diff --git a/shell/browser/api/electron_api_web_request.cc b/shell/browser/api/electron_api_web_request.cc index ff19d5ea00a1c..f69ddad18d614 100644 --- a/shell/browser/api/electron_api_web_request.cc +++ b/shell/browser/api/electron_api_web_request.cc @@ -91,22 +91,18 @@ struct UserData : public base::SupportsUserData::Data { }; // Test whether the URL of |request| matches |patterns|. -bool MatchesFilterCondition(GURL url, const std::set& patterns) { +bool MatchesFilterCondition(extensions::WebRequestInfo* info, + const std::set& patterns) { if (patterns.empty()) return true; for (const auto& pattern : patterns) { - if (pattern.MatchesURL(url)) + if (pattern.MatchesURL(info->url)) return true; } return false; } -bool MatchesFilterCondition(extensions::WebRequestInfo* info, - const std::set& patterns) { - return MatchesFilterCondition(info, patterns); -} - // Convert HttpResponseHeaders to V8. // // Note that while we already have converters for HttpResponseHeaders, we can diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 5373c2eca4446..7f38775cf58c8 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1479,7 +1479,7 @@ void ElectronBrowserClient::CreateWebSocket( DCHECK(web_request.get()); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - if (!web_request.HasListener()) { + if (!web_request->HasListener()) { auto* web_request_api = extensions::BrowserContextKeyedAPIFactory< extensions::WebRequestAPI>::Get(browser_context); @@ -1520,7 +1520,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( DCHECK(web_request.get()); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - if (!web_request.HasListener()) { + if (!web_request->HasListener()) { auto* web_request_api = extensions::BrowserContextKeyedAPIFactory< extensions::WebRequestAPI>::Get(browser_context); @@ -1528,7 +1528,7 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory( bool use_proxy_for_web_request = web_request_api->MaybeProxyURLLoaderFactory( browser_context, frame_host, render_process_id, type, navigation_id, - factory_receiver, header_client); + ukm_source_id, factory_receiver, header_client); if (bypass_redirect_checks) *bypass_redirect_checks = use_proxy_for_web_request; diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 0f7f6f8749b0a..fc2cb23793cd3 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -286,44 +286,31 @@ describe('chrome extensions', () => { }); }); - it('takes precedence over Electron webRequest - http', async () => { - await w.loadURL(url); - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest(() => { - throw new Error('Electron onBeforeRequest callback has been called'); - }); - await expect(fetch(w.webContents, url)).to.eventually.be.rejectedWith(TypeError); - }); - - it('does not take precedence over Electron webRequest with different filter - http', async () => { - await w.loadURL(url); - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); - customSession.webRequest.onBeforeRequest({ urls: ['*://*.does.not.exist.com/*'] }, async (details, callback) => { - callback({ cancel: true }); - }); - await expect(fetch(w.webContents, 'http://does.not.exist.com')).to.eventually.be.rejectedWith(TypeError); - }); + it('does not take precedence over Electron webRequest - http', async () => { + return new Promise((resolve) => { + (async () => { + customSession.webRequest.onBeforeRequest((details, callback) => { + resolve(); + callback({ cancel: true }); + }); + await w.loadURL(url); - it('takes precedence over Electron webRequest - WebSocket', async () => { - await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - customSession.webRequest.onBeforeSendHeaders(() => { - throw new Error('Electron onBeforeSendHeaders callback has been called'); - }); - customSession.webRequest.onSendHeaders((details) => { - if (details.url.startsWith('ws://')) { - expect(details.requestHeaders.foo).be.equal('bar'); - } + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest')); + fetch(w.webContents, url); + })(); }); }); - it('does not take precedence over Electron webRequest with different filter - WebSocket', async () => { - await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); - await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); - customSession.webRequest.onBeforeRequest({ urls: ['*://*.google.com/*'] }, (details, callback) => { - callback({ cancel: true }); + it('does not take precedence over Electron webRequest - WebSocket', () => { + return new Promise((resolve) => { + (async () => { + customSession.webRequest.onBeforeSendHeaders(() => { + resolve(); + }); + await w.loadFile(path.join(fixtures, 'api', 'webrequest.html'), { query: { port } }); + await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-webRequest-wss')); + })(); }); - await expect(fetch(w.webContents, 'https://google.com')).to.eventually.be.rejectedWith(TypeError); }); describe('WebSocket', () => { From 73056ba924c65813995f14cce6ceaa39c73854ed Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Wed, 25 Nov 2020 16:27:41 -0800 Subject: [PATCH 41/44] Apply suggestions from code review --- docs/api/extensions.md | 2 +- shell/browser/extensions/electron_extension_loader.cc | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/api/extensions.md b/docs/api/extensions.md index 60ce51700d16a..7eb2a18bc20dd 100644 --- a/docs/api/extensions.md +++ b/docs/api/extensions.md @@ -120,4 +120,4 @@ The following methods of `chrome.management` are supported: All features of this API are supported. -> **NOTE:** `chrome.webRequest` takes precedence over Electron's [`webRequest`](web-request.md) module. +> **NOTE:** Electron's [`webRequest`](web-request.md) module takes precedence over `chrome.webRequest` if there are conflicting handlers. diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index 55471ec7367b9..456d5050eec75 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -126,11 +126,7 @@ void ElectronExtensionLoader::FinishExtensionLoad( extensions::pref_names::kPrefPreferences); auto preference = update.Create(); const base::Time install_time = base::Time().Now(); - std::unique_ptr install_time_str = - base::Value(base::NumberToString(install_time.ToInternalValue())) - .CreateDeepCopy(); - preference->SetWithoutPathExpansion("install_time", - std::move(install_time_str)); + preference->SetStringKey("install_time", base::NumberToString(install_time.ToInternalValue())); } std::move(cb).Run(extension.get(), result.second); From f22999037483cfffedc5c3d0dd3f3635870c685e Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Mon, 30 Nov 2020 14:42:10 -0800 Subject: [PATCH 42/44] lint --- shell/browser/extensions/electron_extension_loader.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index 456d5050eec75..07cd2217c192a 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -126,7 +126,8 @@ void ElectronExtensionLoader::FinishExtensionLoad( extensions::pref_names::kPrefPreferences); auto preference = update.Create(); const base::Time install_time = base::Time().Now(); - preference->SetStringKey("install_time", base::NumberToString(install_time.ToInternalValue())); + preference->SetStringKey( + "install_time", base::NumberToString(install_time.ToInternalValue())); } std::move(cb).Run(extension.get(), result.second); From 16a62fd4de319274ca85723bb30809d75c5c34fd Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Fri, 18 Dec 2020 19:49:36 +0100 Subject: [PATCH 43/44] Update electron_extension_loader.cc --- shell/browser/extensions/electron_extension_loader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index 07cd2217c192a..26532b72f9718 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -126,7 +126,7 @@ void ElectronExtensionLoader::FinishExtensionLoad( extensions::pref_names::kPrefPreferences); auto preference = update.Create(); const base::Time install_time = base::Time().Now(); - preference->SetStringKey( + preference->SetString( "install_time", base::NumberToString(install_time.ToInternalValue())); } From f063a27426008447d4be336518796c707d82d07d Mon Sep 17 00:00:00 2001 From: Eryk Rakowski Date: Fri, 18 Dec 2020 21:59:43 +0100 Subject: [PATCH 44/44] fix: lint --- shell/browser/extensions/electron_extension_loader.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/browser/extensions/electron_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc index 26532b72f9718..b77d061ae499a 100644 --- a/shell/browser/extensions/electron_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -126,8 +126,8 @@ void ElectronExtensionLoader::FinishExtensionLoad( extensions::pref_names::kPrefPreferences); auto preference = update.Create(); const base::Time install_time = base::Time().Now(); - preference->SetString( - "install_time", base::NumberToString(install_time.ToInternalValue())); + preference->SetString("install_time", + base::NumberToString(install_time.ToInternalValue())); } std::move(cb).Run(extension.get(), result.second);