From b045d42b0e19eeb86d6ca6f211c23017125ad20f Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 16 Mar 2021 01:54:41 -0700 Subject: [PATCH 01/32] docs: document the parameter structure of hookWindowMessage (#28189) Fixes #28178 --- docs/api/browser-window.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index edac2f4063..dec55f3b36 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1316,6 +1316,8 @@ The native type of the handle is `HWND` on Windows, `NSView*` on macOS, and * `message` Integer * `callback` Function + * `wParam` any - The `wParam` provided to the WndProc + * `lParam` any - The `lParam` provided to the WndProc Hooks a windows message. The `callback` is called when the message is received in the WndProc. From fdc2e2bc57792f7faaf6c1ed6a2ff4e6f0fb0103 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Tue, 16 Mar 2021 02:41:59 -0700 Subject: [PATCH 02/32] fix: recalibrate simpleFullscreen when display metrics change (#28150) * fix: recalibrate simpleFullscreen when display metrics change * Address review feedback * fix: compilation issues * Address feedback from review --- shell/browser/native_window.h | 1 + shell/browser/native_window_mac.h | 10 +++++++++- shell/browser/native_window_mac.mm | 21 +++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 2bf22fc72c..0b1b8585e8 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -207,6 +207,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetTrafficLightPosition(base::Optional position) = 0; virtual base::Optional GetTrafficLightPosition() const = 0; virtual void RedrawTrafficLights() = 0; + virtual void UpdateFrame() = 0; #endif // Touchbar API diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 45a7342dcc..88f65ea420 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -14,6 +14,7 @@ #include "base/mac/scoped_nsobject.h" #include "shell/browser/native_window.h" +#include "ui/display/display_observer.h" #include "ui/native_theme/native_theme_observer.h" #include "ui/views/controls/native/native_view_host.h" @@ -27,7 +28,9 @@ namespace electron { class RootViewMac; -class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { +class NativeWindowMac : public NativeWindow, + public ui::NativeThemeObserver, + public display::DisplayObserver { public: NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent); ~NativeWindowMac() override; @@ -124,6 +127,7 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { void SetTrafficLightPosition(base::Optional position) override; base::Optional GetTrafficLightPosition() const override; void RedrawTrafficLights() override; + void UpdateFrame() override; void SetTouchBar( std::vector items) override; void RefreshTouchBarItem(const std::string& item_id) override; @@ -188,6 +192,10 @@ class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { // ui::NativeThemeObserver: void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; + // display::DisplayObserver: + void OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) override; + private: // Add custom layers to the content view. void AddContentViewLayers(); diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 21fff3e724..618119a6eb 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -39,6 +39,7 @@ #include "shell/common/process_util.h" #include "skia/ext/skia_utils_mac.h" #include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h" +#include "ui/display/screen.h" #include "ui/gfx/skia_util.h" #include "ui/gl/gpu_switching_manager.h" #include "ui/views/background.h" @@ -258,6 +259,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options, NativeWindow* parent) : NativeWindow(options, parent), root_view_(new RootViewMac(this)) { ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); + display::Screen::GetScreen()->AddObserver(this); int width = 800, height = 600; options.Get(options::kWidth, &width); @@ -882,6 +884,17 @@ void NativeWindowMac::SetExcludedFromShownWindowsMenu(bool excluded) { [window setExcludedFromWindowsMenu:excluded]; } +void NativeWindowMac::OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) { + // We only want to force screen recalibration if we're in simpleFullscreen + // mode. + if (!is_simple_fullscreen_) + return; + + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&NativeWindow::UpdateFrame, GetWeakPtr())); +} + void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) { NSWindow* window = GetNativeWindow().GetNativeNSWindow(); @@ -1396,6 +1409,13 @@ void NativeWindowMac::RedrawTrafficLights() { [buttons_view_ setNeedsDisplayForButtons]; } +// In simpleFullScreen mode, update the frame for new bounds. +void NativeWindowMac::UpdateFrame() { + NSWindow* window = GetNativeWindow().GetNativeNSWindow(); + NSRect fullscreenFrame = [window.screen frame]; + [window setFrame:fullscreenFrame display:YES animate:YES]; +} + void NativeWindowMac::SetTouchBar( std::vector items) { if (@available(macOS 10.12.2, *)) { @@ -1551,6 +1571,7 @@ void NativeWindowMac::NotifyWindowWillLeaveFullScreen() { void NativeWindowMac::Cleanup() { DCHECK(!IsClosed()); ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); + display::Screen::GetScreen()->RemoveObserver(this); [NSEvent removeMonitor:wheel_event_monitor_]; } From 4a6bc7a42f81773f2a8b1e13835df23ebca9518c Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Tue, 16 Mar 2021 07:34:16 -0700 Subject: [PATCH 03/32] Bump v14.0.0-nightly.20210316 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 85caea238e..ade841cefd 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -14.0.0-nightly.20210315 \ No newline at end of file +14.0.0-nightly.20210316 \ No newline at end of file diff --git a/package.json b/package.json index dd820127f8..8b70067596 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "14.0.0-nightly.20210315", + "version": "14.0.0-nightly.20210316", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index edc04e307f..fcd1e80506 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 14,0,0,20210315 - PRODUCTVERSION 14,0,0,20210315 + FILEVERSION 14,0,0,20210316 + PRODUCTVERSION 14,0,0,20210316 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From 29dc5a2f83ad349102669817fe4d4b3c612fc4b9 Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Tue, 16 Mar 2021 11:41:49 -0400 Subject: [PATCH 04/32] docs: remove no longer relevant link (#28196) --- docs/development/issues.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/development/issues.md b/docs/development/issues.md index 7478433cdd..ec0ad5dc04 100644 --- a/docs/development/issues.md +++ b/docs/development/issues.md @@ -33,8 +33,7 @@ contributing, and more. Please use the issue tracker for bugs only! To submit a bug report: When opening a new issue in the [`electron/electron` issue tracker](https://github.com/electron/electron/issues/new/choose), users -will be presented with [a template](https://github.com/electron/electron/blob/master/.github/ISSUE_TEMPLATE/Bug_report.md) -that should be filled in. +will be presented with a template that should be filled in. If you believe that you have found a bug in Electron, please fill out the template to the best of your ability. From 80f89a34728281cc61cfacd7b682f0227f89a102 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 17 Mar 2021 06:02:47 +0900 Subject: [PATCH 05/32] test: disable some tests under ASan which might receive SIGKILL because of OOM (#28156) * test: running child app under ASan might receive SIGKILL * test: renderer process of webview might receive SIGKILL under ASan * test: increase timeout for asan build --- spec-main/api-app-spec.ts | 37 +++++++++---------- spec-main/api-protocol-spec.ts | 2 +- spec-main/api-web-contents-spec.ts | 18 --------- spec-main/api-web-contents-view-spec.ts | 20 ---------- spec-main/chromium-spec.ts | 11 ++---- spec-main/crash-spec.ts | 4 +- .../quit-on-crashed-event/index.js} | 0 .../webcontents-create-leak-exit/index.js | 0 .../index.js} | 0 spec-main/node-spec.ts | 16 ++++---- spec-main/spellchecker-spec.ts | 6 ++- spec-main/webview-spec.ts | 4 +- 12 files changed, 42 insertions(+), 76 deletions(-) rename spec-main/fixtures/{apps/quit/main.js => crash-cases/quit-on-crashed-event/index.js} (100%) rename spec/fixtures/api/leak-exit-webcontents.js => spec-main/fixtures/crash-cases/webcontents-create-leak-exit/index.js (100%) rename spec-main/fixtures/{api/leak-exit-webcontentsview.js => crash-cases/webcontentsview-create-leak-exit/index.js} (100%) diff --git a/spec-main/api-app-spec.ts b/spec-main/api-app-spec.ts index cbbcee1895..ceae307529 100644 --- a/spec-main/api-app-spec.ts +++ b/spec-main/api-app-spec.ts @@ -143,7 +143,8 @@ describe('app module', () => { }); }); - describe('app.exit(exitCode)', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(!process.env.IS_ASAN)('app.exit(exitCode)', () => { let appProcess: cp.ChildProcess | null = null; afterEach(() => { @@ -209,7 +210,7 @@ describe('app module', () => { }); }); - // TODO(jeremy): figure out why these tests time out under ASan + // Running child app under ASan might receive SIGKILL because of OOM. ifdescribe(!process.env.IS_ASAN)('app.requestSingleInstanceLock', () => { it('prevents the second launch of app', async function () { this.timeout(120000); @@ -252,7 +253,8 @@ describe('app module', () => { }); }); - describe('app.relaunch', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(!process.env.IS_ASAN)('app.relaunch', () => { let server: net.Server | null = null; const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'; @@ -852,7 +854,8 @@ describe('app module', () => { }); }); - describe('getAppPath', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(!process.env.IS_ASAN)('getAppPath', () => { it('works for directories with package.json', async () => { const { appPath } = await runTestApp('app-path'); expect(appPath).to.equal(path.resolve(fixturesPath, 'api/app-path')); @@ -1128,13 +1131,7 @@ describe('app module', () => { }); }); - describe('app launch through uri', () => { - before(function () { - if (process.platform !== 'win32') { - this.skip(); - } - }); - + ifdescribe(process.platform === 'win32')('app launch through uri', () => { it('does not launch for argument following a URL', async () => { const appPath = path.join(fixturesPath, 'api', 'quit-app'); // App should exit with non 123 code. @@ -1334,7 +1331,8 @@ describe('app module', () => { }); }); - describe('sandbox options', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(!process.env.IS_ASAN)('sandbox options', () => { let appProcess: cp.ChildProcess = null as any; let server: net.Server = null as any; const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'; @@ -1375,8 +1373,7 @@ describe('app module', () => { }); describe('when app.enableSandbox() is called', () => { - // TODO(jeremy): figure out why this times out under ASan - ifit(!process.env.IS_ASAN)('adds --enable-sandbox to all renderer processes', done => { + it('adds --enable-sandbox to all renderer processes', done => { const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app'); appProcess = cp.spawn(process.execPath, [appPath, '--app-enable-sandbox']); @@ -1401,8 +1398,7 @@ describe('app module', () => { }); describe('when the app is launched with --enable-sandbox', () => { - // TODO(jeremy): figure out why this times out under ASan - ifit(!process.env.IS_ASAN)('adds --enable-sandbox to all renderer processes', done => { + it('adds --enable-sandbox to all renderer processes', done => { const appPath = path.join(fixturesPath, 'api', 'mixed-sandbox-app'); appProcess = cp.spawn(process.execPath, [appPath, '--enable-sandbox']); @@ -1561,7 +1557,8 @@ describe('app module', () => { }); }); - describe('commandLine.hasSwitch (existing argv)', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(!process.env.IS_ASAN)('commandLine.hasSwitch (existing argv)', () => { it('returns true when present', async () => { const { hasSwitch } = await runTestApp('command-line', '--foobar'); expect(hasSwitch).to.equal(true); @@ -1589,7 +1586,8 @@ describe('app module', () => { }); }); - describe('commandLine.getSwitchValue (existing argv)', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(!process.env.IS_ASAN)('commandLine.getSwitchValue (existing argv)', () => { it('returns the value when present', async () => { const { getSwitchValue } = await runTestApp('command-line', '--foobar=test'); expect(getSwitchValue).to.equal('test'); @@ -1616,7 +1614,8 @@ describe('app module', () => { }); }); -describe('default behavior', () => { +// Running child app under ASan might receive SIGKILL because of OOM. +ifdescribe(!process.env.IS_ASAN)('default behavior', () => { describe('application menu', () => { it('creates the default menu if the app does not set it', async () => { const result = await runTestApp('default-menu'); diff --git a/spec-main/api-protocol-spec.ts b/spec-main/api-protocol-spec.ts index 54483eb98a..31abb6ce13 100644 --- a/spec-main/api-protocol-spec.ts +++ b/spec-main/api-protocol-spec.ts @@ -704,7 +704,7 @@ describe('protocol module', () => { }); describe('protocol.registerSchemeAsPrivileged', () => { - // TODO(jeremy): figure out why this times out under ASan + // Running child app under ASan might receive SIGKILL because of OOM. ifit(!process.env.IS_ASAN)('does not crash on exit', async () => { const appPath = path.join(__dirname, 'fixtures', 'api', 'custom-protocol-shutdown.js'); const appProcess = ChildProcess.spawn(process.execPath, ['--enable-logging', appPath]); diff --git a/spec-main/api-web-contents-spec.ts b/spec-main/api-web-contents-spec.ts index ed30aa76c6..058af2649f 100644 --- a/spec-main/api-web-contents-spec.ts +++ b/spec-main/api-web-contents-spec.ts @@ -3,7 +3,6 @@ import { AddressInfo } from 'net'; import * as path from 'path'; import * as fs from 'fs'; import * as http from 'http'; -import * as ChildProcess from 'child_process'; import { BrowserWindow, ipcMain, webContents, session, WebContents, app } from 'electron/main'; import { clipboard } from 'electron/common'; import { emittedOnce } from './events-helpers'; @@ -1268,16 +1267,6 @@ describe('webContents module', () => { }); }); - describe('create()', () => { - it('does not crash on exit', async () => { - const appPath = path.join(fixturesPath, 'api', 'leak-exit-webcontents.js'); - const electronPath = process.execPath; - const appProcess = ChildProcess.spawn(electronPath, [appPath]); - const [code] = await emittedOnce(appProcess, 'close'); - expect(code).to.equal(0); - }); - }); - const crashPrefs = [ { nodeIntegration: true @@ -2016,13 +2005,6 @@ describe('webContents module', () => { }); contents.loadURL('about:blank').then(() => contents.forcefullyCrashRenderer()); }); - - it('does not crash main process when quiting in it', async () => { - const appPath = path.join(mainFixturesPath, 'apps', 'quit', 'main.js'); - const appProcess = ChildProcess.spawn(process.execPath, [appPath]); - const [code] = await emittedOnce(appProcess, 'close'); - expect(code).to.equal(0); - }); }); it('emits a cancelable event before creating a child webcontents', async () => { diff --git a/spec-main/api-web-contents-view-spec.ts b/spec-main/api-web-contents-view-spec.ts index 4388dc2678..b8b7b26030 100644 --- a/spec-main/api-web-contents-view-spec.ts +++ b/spec-main/api-web-contents-view-spec.ts @@ -1,7 +1,3 @@ -import { expect } from 'chai'; -import * as ChildProcess from 'child_process'; -import * as path from 'path'; -import { emittedOnce } from './events-helpers'; import { closeWindow } from './window-helpers'; import { BaseWindow, WebContentsView } from 'electron/main'; @@ -15,22 +11,6 @@ describe('WebContentsView', () => { w.setContentView(new WebContentsView({})); }); - describe('new WebContentsView()', () => { - it('does not crash on exit', async () => { - const appPath = path.join(__dirname, 'fixtures', 'api', 'leak-exit-webcontentsview.js'); - const electronPath = process.execPath; - const appProcess = ChildProcess.spawn(electronPath, ['--enable-logging', appPath]); - let output = ''; - appProcess.stdout.on('data', data => { output += data; }); - appProcess.stderr.on('data', data => { output += data; }); - const [code] = await emittedOnce(appProcess, 'exit'); - if (code !== 0) { - console.log(code, output); - } - expect(code).to.equal(0); - }); - }); - function triggerGCByAllocation () { const arr = []; for (let i = 0; i < 1000000; i++) { diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index d753932064..c019114785 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -18,8 +18,6 @@ const features = process._linkedBinding('electron_common_features'); const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures'); -const isAsan = process.env.IS_ASAN; - describe('reporting api', () => { // TODO(nornagon): this started failing a lot on CI. Figure out why and fix // it. @@ -298,7 +296,8 @@ describe('web security', () => { }); }); -describe('command line switches', () => { +// Running child app under ASan might receive SIGKILL because of OOM. +ifdescribe(!process.env.IS_ASAN)('command line switches', () => { let appProcess: ChildProcess.ChildProcessWithoutNullStreams | undefined; afterEach(() => { if (appProcess && !appProcess.killed) { @@ -343,8 +342,7 @@ describe('command line switches', () => { ifit(process.platform === 'linux')('should not change LC_ALL when --lang is not set', async () => testLocale('', lcAll, true)); }); - // TODO(nornagon): figure out why these tests fail under ASan. - ifdescribe(!isAsan)('--remote-debugging-pipe switch', () => { + describe('--remote-debugging-pipe switch', () => { it('should expose CDP via pipe', async () => { const electronPath = process.execPath; appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe'], { @@ -386,8 +384,7 @@ describe('command line switches', () => { }); }); - // TODO(nornagon): figure out why these tests fail under ASan. - ifdescribe(!isAsan)('--remote-debugging-port switch', () => { + describe('--remote-debugging-port switch', () => { it('should display the discovery page', (done) => { const electronPath = process.execPath; let output = ''; diff --git a/spec-main/crash-spec.ts b/spec-main/crash-spec.ts index eb9718dac5..4457e9bcb8 100644 --- a/spec-main/crash-spec.ts +++ b/spec-main/crash-spec.ts @@ -2,6 +2,7 @@ import { expect } from 'chai'; import * as cp from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; +import { ifdescribe } from './spec-helpers'; const fixturePath = path.resolve(__dirname, 'fixtures', 'crash-cases'); @@ -30,7 +31,8 @@ const runFixtureAndEnsureCleanExit = (args: string[]) => { }); }; -describe('crash cases', () => { +// Running child app under ASan might receive SIGKILL because of OOM. +ifdescribe(!process.env.IS_ASAN)('crash cases', () => { afterEach(() => { for (const child of children) { child.kill(); diff --git a/spec-main/fixtures/apps/quit/main.js b/spec-main/fixtures/crash-cases/quit-on-crashed-event/index.js similarity index 100% rename from spec-main/fixtures/apps/quit/main.js rename to spec-main/fixtures/crash-cases/quit-on-crashed-event/index.js diff --git a/spec/fixtures/api/leak-exit-webcontents.js b/spec-main/fixtures/crash-cases/webcontents-create-leak-exit/index.js similarity index 100% rename from spec/fixtures/api/leak-exit-webcontents.js rename to spec-main/fixtures/crash-cases/webcontents-create-leak-exit/index.js diff --git a/spec-main/fixtures/api/leak-exit-webcontentsview.js b/spec-main/fixtures/crash-cases/webcontentsview-create-leak-exit/index.js similarity index 100% rename from spec-main/fixtures/api/leak-exit-webcontentsview.js rename to spec-main/fixtures/crash-cases/webcontentsview-create-leak-exit/index.js diff --git a/spec-main/node-spec.ts b/spec-main/node-spec.ts index 227f9c4003..def9ad1e46 100644 --- a/spec-main/node-spec.ts +++ b/spec-main/node-spec.ts @@ -123,11 +123,12 @@ describe('node feature', () => { }); }); - describe('Node.js cli flags', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(features.isRunAsNodeEnabled() && !process.env.IS_ASAN)('Node.js cli flags', () => { let child: childProcess.ChildProcessWithoutNullStreams; let exitPromise: Promise; - ifit(features.isRunAsNodeEnabled())('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => { + it('Prohibits crypto-related flags in ELECTRON_RUN_AS_NODE mode', (done) => { after(async () => { const [code, signal] = await exitPromise; expect(signal).to.equal(null); @@ -165,7 +166,8 @@ describe('node feature', () => { }); }); - ifdescribe(features.isRunAsNodeEnabled())('inspector', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifdescribe(features.isRunAsNodeEnabled() && !process.env.IS_ASAN)('inspector', () => { let child: childProcess.ChildProcessWithoutNullStreams; let exitPromise: Promise; @@ -242,9 +244,8 @@ describe('node feature', () => { } }); - // IPC Electron child process not supported on Windows - // TODO(jeremy): figure out why this times out under ASan - ifit(process.platform !== 'win32' && !process.env.IS_ASAN)('does not crash when quitting with the inspector connected', function (done) { + // IPC Electron child process not supported on Windows. + ifit(process.platform !== 'win32')('does not crash when quitting with the inspector connected', function (done) { child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], { stdio: ['ipc'] }) as childProcess.ChildProcessWithoutNullStreams; @@ -304,7 +305,8 @@ describe('node feature', () => { }); }); - it('Can find a module using a package.json main field', () => { + // Running child app under ASan might receive SIGKILL because of OOM. + ifit(!process.env.IS_ASAN)('Can find a module using a package.json main field', () => { const result = childProcess.spawnSync(process.execPath, [path.resolve(fixtures, 'api', 'electron-main-module', 'app.asar')]); expect(result.status).to.equal(0); }); diff --git a/spec-main/spellchecker-spec.ts b/spec-main/spellchecker-spec.ts index e4ef5ae81e..31443d6a4a 100644 --- a/spec-main/spellchecker-spec.ts +++ b/spec-main/spellchecker-spec.ts @@ -9,7 +9,9 @@ import { ifit, ifdescribe, delay } from './spec-helpers'; const features = process._linkedBinding('electron_common_features'); const v8Util = process._linkedBinding('electron_common_v8_util'); -ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { +ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () { + this.timeout(200 * 1000); + let w: BrowserWindow; async function rightClick () { @@ -28,7 +30,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { // to detect spellchecker is to keep checking with a busy loop. async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) { const now = Date.now(); - const timeout = 10 * 1000; + const timeout = (process.env.IS_ASAN ? 180 : 10) * 1000; let contextMenuParams = await rightClick(); while (!fn(contextMenuParams) && (Date.now() - now < timeout)) { await delay(100); diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index 46b164a9dc..9478457814 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -3,6 +3,7 @@ import * as url from 'url'; import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main'; import { closeAllWindows } from './window-helpers'; import { emittedOnce, emittedUntil } from './events-helpers'; +import { ifdescribe } from './spec-helpers'; import { expect } from 'chai'; async function loadWebView (w: WebContents, attributes: Record, openDevTools: boolean = false): Promise { @@ -25,7 +26,8 @@ async function loadWebView (w: WebContents, attributes: Record, `); } -describe(' tag', function () { +// The render process of webview might receive SIGKILL because of OOM. +ifdescribe(!process.env.IS_ASAN)(' tag', function () { const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); afterEach(closeAllWindows); From b6ff12ef7f07b1ee2e7bcdca0d4b1d20c815e77b Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Tue, 16 Mar 2021 16:45:38 -0700 Subject: [PATCH 06/32] docs: update Quick Start Guide for Electron 12 (#28223) * docs: Update Quick Start Guide for Electron 12 With `contextIsolation` enabled by default in Electron 12, the Getting Started Guide no longer works as it is written. In order for the basic example to display values from `process.versions`, we need to add a `preload.js` to the example. * fix: annotate preload code block with a language * docs: update quick-start Fiddle example to use preload to provide version info * fix: ensure example files end in a newline * docs: add security warning to instructions for turning off contextIsolation Co-authored-by: John Kleinschmidt * docs: treat preload as an adjective instead of a noun Co-authored-by: John Kleinschmidt Co-authored-by: John Kleinschmidt --- docs/fiddles/quick-start/index.html | 6 +-- docs/fiddles/quick-start/main.js | 19 +++++---- docs/fiddles/quick-start/preload.js | 11 +++++ docs/tutorial/quick-start.md | 63 +++++++++++++++++++++-------- 4 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 docs/fiddles/quick-start/preload.js diff --git a/docs/fiddles/quick-start/index.html b/docs/fiddles/quick-start/index.html index a3855d2640..f008d867a0 100644 --- a/docs/fiddles/quick-start/index.html +++ b/docs/fiddles/quick-start/index.html @@ -8,9 +8,9 @@

Hello World!

- We are using node , - Chrome , - and Electron . + We are using Node.js , + Chromium , + and Electron .

diff --git a/docs/fiddles/quick-start/main.js b/docs/fiddles/quick-start/main.js index bfc856e606..519a67947c 100644 --- a/docs/fiddles/quick-start/main.js +++ b/docs/fiddles/quick-start/main.js @@ -1,19 +1,27 @@ const { app, BrowserWindow } = require('electron') +const path = require('path') function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { - nodeIntegration: true, - contextIsolation: false + preload: path.join(__dirname, 'preload.js') } }) win.loadFile('index.html') } -app.whenReady().then(createWindow) +app.whenReady().then(() => { + createWindow() + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } + }) +}) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { @@ -21,8 +29,3 @@ app.on('window-all-closed', () => { } }) -app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) { - createWindow() - } -}) diff --git a/docs/fiddles/quick-start/preload.js b/docs/fiddles/quick-start/preload.js new file mode 100644 index 0000000000..7674d01224 --- /dev/null +++ b/docs/fiddles/quick-start/preload.js @@ -0,0 +1,11 @@ +window.addEventListener('DOMContentLoaded', () => { + const replaceText = (selector, text) => { + const element = document.getElementById(selector) + if (element) element.innerText = text + } + + for (const type of ['chrome', 'node', 'electron']) { + replaceText(`${type}-version`, process.versions[type]) + } +}) + diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 1358e523f9..d13b753fb9 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -32,6 +32,7 @@ From a development perspective, an Electron application is essentially a Node.js my-electron-app/ ├── package.json ├── main.js +├── preload.js └── index.html ``` @@ -55,45 +56,49 @@ The main script may look as follows: ```javascript fiddle='docs/fiddles/quick-start' const { app, BrowserWindow } = require('electron') +const path = require('path') function createWindow () { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { - nodeIntegration: true + preload: path.join(__dirname, 'preload.js') } }) win.loadFile('index.html') } -app.whenReady().then(createWindow) +app.whenReady().then(() => { + createWindow() + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } + }) +) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) - -app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) { - createWindow() - } -}) ``` ##### What is going on above? 1. Line 1: First, you import the `app` and `BrowserWindow` modules of the `electron` package to be able to manage your application's lifecycle events, as well as create and control browser windows. -2. Line 3: After that, you define a function that creates a [new browser window](../api/browser-window.md#new-browserwindowoptions) with node integration enabled, loads `index.html` file into this window (line 12, we will discuss the file later). -3. Line 15: You create a new browser window by invoking the `createWindow` function once the Electron application [is initialized](../api/app.md#appwhenready). -4. Line 17: You add a new listener that tries to quit the application when it no longer has any open windows. This listener is a no-op on macOS due to the operating system's [window management behavior](https://support.apple.com/en-ca/guide/mac-help/mchlp2469/mac). -5. Line 23: You add a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application. +2. Line 2: Second, you import the `path` package which provides utility functions for file paths. +3. Line 4: After that, you define a function that creates a [new browser window](../api/browser-window.md#new-browserwindowoptions) with a preload script, loads `index.html` file into this window (line 13, we will discuss the file later). +4. Line 16: You create a new browser window by invoking the `createWindow` function once the Electron application [is initialized](../api/app.md#appwhenready). +5. Line 18: You add a new listener that creates a new browser window only if when the application has no visible windows after being activated. For example, after launching the application for the first time, or re-launching the already running application. +6. Line 25: You add a new listener that tries to quit the application when it no longer has any open windows. This listener is a no-op on macOS due to the operating system's [window management behavior](https://support.apple.com/en-ca/guide/mac-help/mchlp2469/mac). #### Create a web page -This is the web page you want to display once the application is initialized. This web page represents the Renderer process. You can create multiple browser windows, where each window uses its own independent Renderer. Each window can optionally be granted with full access to Node.js API through the `nodeIntegration` preference. +This is the web page you want to display once the application is initialized. This web page represents the Renderer process. You can create multiple browser windows, where each window uses its own independent Renderer. You can optionally grant access to additional Node.js APIs by exposing them from your preload script. The `index.html` page looks as follows: @@ -108,14 +113,38 @@ The `index.html` page looks as follows:

Hello World!

- We are using node , - Chrome , - and Electron . + We are using Node.js , + Chromium , + and Electron .

``` +#### Define a preload script + +Your preload script acts as a bridge between Node.js and your web page. It allows you to expose specific APIs and behaviors to your web page rather than insecurely exposing the entire Node.js API. In this example we will use the preload script to read version information from the `process` object and update the web page with that info. + +```javascript fiddle='docs/fiddles/quick-start' +window.addEventListener('DOMContentLoaded', () => { + const replaceText = (selector, text) => { + const element = document.getElementById(selector) + if (element) element.innerText = text + } + + for (const type of ['chrome', 'node', 'electron']) { + replaceText(`${type}-version`, process.versions[type]) + } +}) +``` + +##### What's going on above? + +1. On line 1: First you define an event listener that tells you when the web page has loaded +2. On line 2: Second you define a utility function used to set the text of the placeholders in the `index.html` +3. On line 7: Next you loop through the list of components whose version you want to display +4. On line 8: Finally, you call `replaceText` to look up the version placeholders in `index.html` and set their text value to the values from `process.versions` + #### Modify your package.json file Your Electron application uses the `package.json` file as the main entry point (as any other Node.js application). The main script of your application is `main.js`, so modify the `package.json` file accordingly: @@ -283,7 +312,7 @@ ipcRenderer.invoke('perform-action', ...args) ##### Node.js API -> NOTE: To access the Node.js API from the Renderer process, you need to set the `nodeIntegration` preference to `true`. +> NOTE: To access the Node.js API from the Renderer process, you need to set the `nodeIntegration` preference to `true` and the `contextIsolation` preference to `false`. Please note that access to the Node.js API in any renderer that loads remote content is not recommended for [security reasons](../tutorial/security.md#2-do-not-enable-nodejs-integration-for-remote-content). Electron exposes full access to Node.js API and its modules both in the Main and the Renderer processes. For example, you can read all the files from the root directory: From 54bc21929a08e144cf30e17aec66c152cfc31062 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Wed, 17 Mar 2021 07:31:38 -0700 Subject: [PATCH 07/32] Bump v14.0.0-nightly.20210317 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index ade841cefd..cecc93e72f 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -14.0.0-nightly.20210316 \ No newline at end of file +14.0.0-nightly.20210317 \ No newline at end of file diff --git a/package.json b/package.json index 8b70067596..566761bab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "14.0.0-nightly.20210316", + "version": "14.0.0-nightly.20210317", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index fcd1e80506..3f96bbbd85 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 14,0,0,20210316 - PRODUCTVERSION 14,0,0,20210316 + FILEVERSION 14,0,0,20210317 + PRODUCTVERSION 14,0,0,20210317 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From 485fa5bea9313b45c014b65a1fb8291aa448782e Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Wed, 17 Mar 2021 19:23:03 +0100 Subject: [PATCH 08/32] feat: add process.contextId used by @electron/remote (#28007) --- docs/api/process.md | 8 ++++++++ lib/renderer/init.ts | 4 ++++ lib/sandboxed_renderer/init.ts | 4 ++++ spec-main/api-browser-window-spec.ts | 1 + spec-main/fixtures/module/preload-sandbox.js | 3 ++- spec/api-process-spec.js | 6 ++++++ 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/api/process.md b/docs/api/process.md index ee00672129..e4b5c26d55 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -35,6 +35,7 @@ In sandboxed renderers the `process` object contains only a subset of the APIs: * `versions` * `mas` * `windowsStore` +* `contextId` ## Events @@ -133,6 +134,13 @@ A `String` representing Electron's version string. A `Boolean`. If the app is running as a Windows Store app (appx), this property is `true`, for otherwise it is `undefined`. +### `process.contextId` _Readonly_ + +A `String` (optional) representing a globally unique ID of the current JavaScript context. +Each frame has its own JavaScript context. When contextIsolation is enabled, the isolated +world also has a separate JavaScript context. +This property is only available in the renderer process. + ## Methods The `process` object has the following methods: diff --git a/lib/renderer/init.ts b/lib/renderer/init.ts index c41fa56188..3bb3c2d6ef 100644 --- a/lib/renderer/init.ts +++ b/lib/renderer/init.ts @@ -39,6 +39,10 @@ require('@electron/internal/common/init'); // The global variable will be used by ipc for event dispatching const v8Util = process._linkedBinding('electron_common_v8_util'); +// Expose process.contextId +const contextId = v8Util.getHiddenValue(global, 'contextId'); +Object.defineProperty(process, 'contextId', { enumerable: true, value: contextId }); + const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer').default; diff --git a/lib/sandboxed_renderer/init.ts b/lib/sandboxed_renderer/init.ts index a23082ef04..f82f0cbe00 100644 --- a/lib/sandboxed_renderer/init.ts +++ b/lib/sandboxed_renderer/init.ts @@ -89,6 +89,10 @@ Object.defineProperty(preloadProcess, 'noDeprecation', { } }); +// Expose process.contextId +const contextId = v8Util.getHiddenValue(global, 'contextId'); +Object.defineProperty(preloadProcess, 'contextId', { enumerable: true, value: contextId }); + process.on('loaded', () => (preloadProcess as events.EventEmitter).emit('loaded')); process.on('exit', () => (preloadProcess as events.EventEmitter).emit('exit')); (process as events.EventEmitter).on('document-start', () => (preloadProcess as events.EventEmitter).emit('document-start')); diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index bc673855bb..957607b855 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -2490,6 +2490,7 @@ describe('BrowserWindow module', () => { expect(test.type).to.equal('renderer'); expect(test.version).to.equal(process.version); expect(test.versions).to.deep.equal(process.versions); + expect(test.contextId).to.be.a('string'); if (process.platform === 'linux' && test.osSandbox) { expect(test.creationTime).to.be.null('creation time'); diff --git a/spec-main/fixtures/module/preload-sandbox.js b/spec-main/fixtures/module/preload-sandbox.js index 3f216a3e03..816b26be24 100644 --- a/spec-main/fixtures/module/preload-sandbox.js +++ b/spec-main/fixtures/module/preload-sandbox.js @@ -42,7 +42,8 @@ sandboxed: process.sandboxed, type: process.type, version: process.version, - versions: process.versions + versions: process.versions, + contextId: process.contextId }; } } else if (location.href !== 'about:blank') { diff --git a/spec/api-process-spec.js b/spec/api-process-spec.js index 6eada5c945..8029107413 100644 --- a/spec/api-process-spec.js +++ b/spec/api-process-spec.js @@ -115,4 +115,10 @@ describe('process module', () => { expect(success).to.be.false(); }); }); + + describe('process.contextId', () => { + it('is a string', () => { + expect(process.contextId).to.be.a('string'); + }); + }); }); From fc7f2042ecb6bd43b39dd93649de0de736d4829a Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Wed, 17 Mar 2021 19:23:29 +0100 Subject: [PATCH 09/32] feat: add process.contextIsolation property (#28030) --- docs/api/process.md | 6 ++++++ shell/renderer/electron_renderer_client.cc | 3 +-- .../renderer/electron_sandboxed_renderer_client.cc | 7 +++---- .../renderer/electron_sandboxed_renderer_client.h | 2 +- shell/renderer/renderer_client_base.cc | 10 +++++++--- shell/renderer/renderer_client_base.h | 6 ++++-- spec-main/api-browser-window-spec.ts | 14 ++++++++++++++ spec-main/fixtures/module/preload-sandbox.js | 1 + spec/fixtures/api/isolated-process.js | 3 +++ 9 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 spec/fixtures/api/isolated-process.js diff --git a/docs/api/process.md b/docs/api/process.md index e4b5c26d55..43a72f62dd 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -30,6 +30,7 @@ In sandboxed renderers the `process` object contains only a subset of the APIs: * `arch` * `platform` * `sandboxed` +* `contextIsolation` * `type` * `version` * `versions` @@ -94,6 +95,11 @@ A `String` representing the path to the resources directory. A `Boolean`. When the renderer process is sandboxed, this property is `true`, otherwise it is `undefined`. +### `process.contextIsolation` _Readonly_ + +A `Boolean` that indicates whether the current renderer context has `contextIsolation` enabled. +It is `undefined` in the main process. + ### `process.throwDeprecation` A `Boolean` that controls whether or not deprecation warnings will be thrown as diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index a577a34e4a..74284e4128 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -146,9 +146,8 @@ void ElectronRendererClient::DidCreateScriptContext( // Add Electron extended APIs. electron_bindings_->BindTo(env->isolate(), env->process_object()); - AddRenderBindings(env->isolate(), env->process_object()); gin_helper::Dictionary process_dict(env->isolate(), env->process_object()); - process_dict.SetReadOnly("isMainFrame", render_frame->IsMainFrame()); + BindProcess(env->isolate(), &process_dict, render_frame); // Load everything. node_bindings_->LoadEnvironment(env); diff --git a/shell/renderer/electron_sandboxed_renderer_client.cc b/shell/renderer/electron_sandboxed_renderer_client.cc index c6b58c06f7..96d6eccb34 100644 --- a/shell/renderer/electron_sandboxed_renderer_client.cc +++ b/shell/renderer/electron_sandboxed_renderer_client.cc @@ -131,7 +131,7 @@ ElectronSandboxedRendererClient::~ElectronSandboxedRendererClient() = default; void ElectronSandboxedRendererClient::InitializeBindings( v8::Local binding, v8::Local context, - bool is_main_frame) { + content::RenderFrame* render_frame) { auto* isolate = context->GetIsolate(); gin_helper::Dictionary b(isolate, binding); b.SetMethod("get", GetBinding); @@ -141,13 +141,13 @@ void ElectronSandboxedRendererClient::InitializeBindings( b.Set("process", process); ElectronBindings::BindProcess(isolate, &process, metrics_.get()); + BindProcess(isolate, &process, render_frame); process.SetMethod("uptime", Uptime); process.Set("argv", base::CommandLine::ForCurrentProcess()->argv()); process.SetReadOnly("pid", base::GetCurrentProcId()); process.SetReadOnly("sandboxed", true); process.SetReadOnly("type", "renderer"); - process.SetReadOnly("isMainFrame", is_main_frame); } void ElectronSandboxedRendererClient::RenderFrameCreated( @@ -218,8 +218,7 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext( // argument. auto* isolate = context->GetIsolate(); auto binding = v8::Object::New(isolate); - InitializeBindings(binding, context, render_frame->IsMainFrame()); - AddRenderBindings(isolate, binding); + InitializeBindings(binding, context, render_frame); std::vector> sandbox_preload_bundle_params = { node::FIXED_ONE_BYTE_STRING(isolate, "binding")}; diff --git a/shell/renderer/electron_sandboxed_renderer_client.h b/shell/renderer/electron_sandboxed_renderer_client.h index ab481b422f..623896ff8f 100644 --- a/shell/renderer/electron_sandboxed_renderer_client.h +++ b/shell/renderer/electron_sandboxed_renderer_client.h @@ -21,7 +21,7 @@ class ElectronSandboxedRendererClient : public RendererClientBase { void InitializeBindings(v8::Local binding, v8::Local context, - bool is_main_frame); + content::RenderFrame* render_frame); // electron::RendererClientBase: void DidCreateScriptContext(v8::Handle context, content::RenderFrame* render_frame) override; diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index e1fde902b1..ef09ca7704 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -137,9 +137,13 @@ void RendererClientBase::DidCreateScriptContext( global.SetHidden("contextId", context_id); } -void RendererClientBase::AddRenderBindings( - v8::Isolate* isolate, - v8::Local binding_object) {} +void RendererClientBase::BindProcess(v8::Isolate* isolate, + gin_helper::Dictionary* process, + content::RenderFrame* render_frame) { + process->SetReadOnly("isMainFrame", render_frame->IsMainFrame()); + process->SetReadOnly("contextIsolation", + render_frame->GetBlinkPreferences().context_isolation); +} void RendererClientBase::RenderThreadStarted() { auto* command_line = base::CommandLine::ForCurrentProcess(); diff --git a/shell/renderer/renderer_client_base.h b/shell/renderer/renderer_client_base.h index 14b766b8cf..e0a6d4016c 100644 --- a/shell/renderer/renderer_client_base.h +++ b/shell/renderer/renderer_client_base.h @@ -13,6 +13,7 @@ #include "content/public/renderer/content_renderer_client.h" #include "electron/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h" +#include "shell/common/gin_helper/dictionary.h" #include "third_party/blink/public/web/web_local_frame.h" // In SHARED_INTERMEDIATE_DIR. #include "widevine_cdm_version.h" // NOLINT(build/include_directory) @@ -92,8 +93,9 @@ class RendererClientBase : public content::ContentRendererClient #endif protected: - void AddRenderBindings(v8::Isolate* isolate, - v8::Local binding_object); + void BindProcess(v8::Isolate* isolate, + gin_helper::Dictionary* process, + content::RenderFrame* render_frame); // content::ContentRendererClient: void RenderThreadStarted() override; diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 957607b855..a62087843c 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -2487,6 +2487,7 @@ describe('BrowserWindow module', () => { expect(test.env).to.deep.equal(process.env); expect(test.execPath).to.equal(process.helperExecPath); expect(test.sandboxed).to.be.true('sandboxed'); + expect(test.contextIsolation).to.be.false('contextIsolation'); expect(test.type).to.equal('renderer'); expect(test.version).to.equal(process.version); expect(test.versions).to.deep.equal(process.versions); @@ -4305,6 +4306,19 @@ describe('BrowserWindow module', () => { const [, data] = await p; expect(data.pageContext.openedLocation).to.equal('about:blank'); }); + it('reports process.contextIsolation', async () => { + const iw = new BrowserWindow({ + show: false, + webPreferences: { + contextIsolation: true, + preload: path.join(fixtures, 'api', 'isolated-process.js') + } + }); + const p = emittedOnce(ipcMain, 'context-isolation'); + iw.loadURL('about:blank'); + const [, contextIsolation] = await p; + expect(contextIsolation).to.be.true('contextIsolation'); + }); }); describe('reloading with allowRendererProcessReuse enabled', () => { diff --git a/spec-main/fixtures/module/preload-sandbox.js b/spec-main/fixtures/module/preload-sandbox.js index 816b26be24..e72aac520c 100644 --- a/spec-main/fixtures/module/preload-sandbox.js +++ b/spec-main/fixtures/module/preload-sandbox.js @@ -40,6 +40,7 @@ arch: process.arch, platform: process.platform, sandboxed: process.sandboxed, + contextIsolation: process.contextIsolation, type: process.type, version: process.version, versions: process.versions, diff --git a/spec/fixtures/api/isolated-process.js b/spec/fixtures/api/isolated-process.js new file mode 100644 index 0000000000..370436bfe0 --- /dev/null +++ b/spec/fixtures/api/isolated-process.js @@ -0,0 +1,3 @@ +const { ipcRenderer } = require('electron'); + +ipcRenderer.send('context-isolation', process.contextIsolation); From 14acf00ba9ecdd8103828659b8a4f9d405fd81ec Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Thu, 18 Mar 2021 00:58:03 -0700 Subject: [PATCH 10/32] docs: add missing curly brace to quick start example code (#28253) --- docs/tutorial/quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index d13b753fb9..4c7c078806 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -78,7 +78,7 @@ app.whenReady().then(() => { createWindow() } }) -) +}) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { From baadcd48df9db574a6697c79a1378785844808a0 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Thu, 18 Mar 2021 07:32:19 -0700 Subject: [PATCH 11/32] Bump v14.0.0-nightly.20210318 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index cecc93e72f..f1ef873e74 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -14.0.0-nightly.20210317 \ No newline at end of file +14.0.0-nightly.20210318 \ No newline at end of file diff --git a/package.json b/package.json index 566761bab2..381727f700 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "14.0.0-nightly.20210317", + "version": "14.0.0-nightly.20210318", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 3f96bbbd85..5f23d20977 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 14,0,0,20210317 - PRODUCTVERSION 14,0,0,20210317 + FILEVERSION 14,0,0,20210318 + PRODUCTVERSION 14,0,0,20210318 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From 8476bed36eb4e7c5c58b50bc6b186fa82263fd21 Mon Sep 17 00:00:00 2001 From: Karel Braeckman Date: Thu, 18 Mar 2021 21:35:04 +0100 Subject: [PATCH 12/32] Change example to work with latest versions of selenium-webdriver (#28231) See https://github.com/SeleniumHQ/selenium/issues/9286 The existing snippet works with selenium-webdriver <= 3.6.0, but any more recent version seems to require using 'goog:chromeOptions' and forBrowser('chrome'). --- docs/tutorial/using-selenium-and-webdriver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/using-selenium-and-webdriver.md b/docs/tutorial/using-selenium-and-webdriver.md index 61ff0e1cb4..197c29a5ff 100644 --- a/docs/tutorial/using-selenium-and-webdriver.md +++ b/docs/tutorial/using-selenium-and-webdriver.md @@ -86,12 +86,12 @@ const driver = new webdriver.Builder() // The "9515" is the port opened by chrome driver. .usingServer('http://localhost:9515') .withCapabilities({ - chromeOptions: { + 'goog:chromeOptions': { // Here is the path to your Electron binary. binary: '/Path-to-Your-App.app/Contents/MacOS/Electron' } }) - .forBrowser('electron') + .forBrowser('chrome') // note: use .forBrowser('electron') for selenium-webdriver <= 3.6.0 .build() driver.get('http://www.google.com') From b52ccc97264481fba6a092a24ba8b63058c964fb Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Thu, 18 Mar 2021 13:37:14 -0700 Subject: [PATCH 13/32] fix: bad menu position when no positioning item specified (#28224) --- shell/browser/api/electron_api_menu_mac.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_menu_mac.mm b/shell/browser/api/electron_api_menu_mac.mm index bab36f6a65..49d7934187 100644 --- a/shell/browser/api/electron_api_menu_mac.mm +++ b/shell/browser/api/electron_api_menu_mac.mm @@ -88,7 +88,7 @@ void MenuMac::PopupOnUI(const base::WeakPtr& native_window, } // If no preferred item is specified, try to show all of the menu items. - if (!positioning_item) { + if (!item) { CGFloat windowBottom = CGRectGetMinY([view window].frame); CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height; CGFloat screenBottom = CGRectGetMinY([view window].screen.frame); From 4057e6b56e7d35575d5b46aaebeee91be05c5782 Mon Sep 17 00:00:00 2001 From: Samuel Maddock Date: Thu, 18 Mar 2021 16:43:35 -0400 Subject: [PATCH 14/32] fix: DesktopCapturer gc'd prior to capture completion (#28273) desktopCapture.getSources() returns a promise which should resolve when capturing finishes. Internally it creates an instance of DesktopCapturer which is responsible for resolving or rejecting the promise. Between the time DesktopCapturer starts capturing frames and when it finishes, it's possible for its handle to be GC'd leading to it never resolving. These changes pin the instance of DesktopCapturer until it either finishes or errors. fixes #25595 --- shell/browser/api/electron_api_desktop_capturer.cc | 12 +++++++++++- shell/browser/api/electron_api_desktop_capturer.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/shell/browser/api/electron_api_desktop_capturer.cc b/shell/browser/api/electron_api_desktop_capturer.cc index a6b406afe0..00aded1a1b 100644 --- a/shell/browser/api/electron_api_desktop_capturer.cc +++ b/shell/browser/api/electron_api_desktop_capturer.cc @@ -162,6 +162,9 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) { v8::Locker locker(isolate); v8::HandleScope scope(isolate); gin_helper::CallMethod(this, "_onerror", "Failed to get sources."); + + Unpin(); + return; } @@ -195,12 +198,19 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) { v8::Locker locker(isolate); v8::HandleScope scope(isolate); gin_helper::CallMethod(this, "_onfinished", captured_sources_); + + Unpin(); } } // static gin::Handle DesktopCapturer::Create(v8::Isolate* isolate) { - return gin::CreateHandle(isolate, new DesktopCapturer(isolate)); + auto handle = gin::CreateHandle(isolate, new DesktopCapturer(isolate)); + + // Keep reference alive until capturing has finished. + handle->Pin(isolate); + + return handle; } gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder( diff --git a/shell/browser/api/electron_api_desktop_capturer.h b/shell/browser/api/electron_api_desktop_capturer.h index 3ef7c0bd1f..9664836a0f 100644 --- a/shell/browser/api/electron_api_desktop_capturer.h +++ b/shell/browser/api/electron_api_desktop_capturer.h @@ -13,12 +13,14 @@ #include "chrome/browser/media/webrtc/native_desktop_media_list.h" #include "gin/handle.h" #include "gin/wrappable.h" +#include "shell/common/gin_helper/pinnable.h" namespace electron { namespace api { class DesktopCapturer : public gin::Wrappable, + public gin_helper::Pinnable, public DesktopMediaListObserver { public: struct Source { From f35fc93080872802c9e203d253c83c689a72b8b0 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Thu, 18 Mar 2021 14:00:19 -0700 Subject: [PATCH 15/32] chore: rename process.contextIsolation to process.contextIsolated (#28259) * chore: rename process.contextIsolation to process.contextIsolated * thing --- docs/api/process.md | 4 ++-- shell/renderer/renderer_client_base.cc | 2 +- spec-main/api-browser-window-spec.ts | 4 ++-- spec-main/fixtures/module/preload-sandbox.js | 2 +- spec/fixtures/api/isolated-process.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/api/process.md b/docs/api/process.md index 43a72f62dd..2f7d97ad1a 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -30,7 +30,7 @@ In sandboxed renderers the `process` object contains only a subset of the APIs: * `arch` * `platform` * `sandboxed` -* `contextIsolation` +* `contextIsolated` * `type` * `version` * `versions` @@ -95,7 +95,7 @@ A `String` representing the path to the resources directory. A `Boolean`. When the renderer process is sandboxed, this property is `true`, otherwise it is `undefined`. -### `process.contextIsolation` _Readonly_ +### `process.contextIsolated` _Readonly_ A `Boolean` that indicates whether the current renderer context has `contextIsolation` enabled. It is `undefined` in the main process. diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index ef09ca7704..ba4676bf78 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -141,7 +141,7 @@ void RendererClientBase::BindProcess(v8::Isolate* isolate, gin_helper::Dictionary* process, content::RenderFrame* render_frame) { process->SetReadOnly("isMainFrame", render_frame->IsMainFrame()); - process->SetReadOnly("contextIsolation", + process->SetReadOnly("contextIsolated", render_frame->GetBlinkPreferences().context_isolation); } diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index a62087843c..81e57f32fc 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -2487,7 +2487,7 @@ describe('BrowserWindow module', () => { expect(test.env).to.deep.equal(process.env); expect(test.execPath).to.equal(process.helperExecPath); expect(test.sandboxed).to.be.true('sandboxed'); - expect(test.contextIsolation).to.be.false('contextIsolation'); + expect(test.contextIsolated).to.be.false('contextIsolated'); expect(test.type).to.equal('renderer'); expect(test.version).to.equal(process.version); expect(test.versions).to.deep.equal(process.versions); @@ -4306,7 +4306,7 @@ describe('BrowserWindow module', () => { const [, data] = await p; expect(data.pageContext.openedLocation).to.equal('about:blank'); }); - it('reports process.contextIsolation', async () => { + it('reports process.contextIsolated', async () => { const iw = new BrowserWindow({ show: false, webPreferences: { diff --git a/spec-main/fixtures/module/preload-sandbox.js b/spec-main/fixtures/module/preload-sandbox.js index e72aac520c..d774c54301 100644 --- a/spec-main/fixtures/module/preload-sandbox.js +++ b/spec-main/fixtures/module/preload-sandbox.js @@ -40,7 +40,7 @@ arch: process.arch, platform: process.platform, sandboxed: process.sandboxed, - contextIsolation: process.contextIsolation, + contextIsolated: process.contextIsolated, type: process.type, version: process.version, versions: process.versions, diff --git a/spec/fixtures/api/isolated-process.js b/spec/fixtures/api/isolated-process.js index 370436bfe0..d5e949ded5 100644 --- a/spec/fixtures/api/isolated-process.js +++ b/spec/fixtures/api/isolated-process.js @@ -1,3 +1,3 @@ const { ipcRenderer } = require('electron'); -ipcRenderer.send('context-isolation', process.contextIsolation); +ipcRenderer.send('context-isolation', process.contextIsolated); From 502d4c19ce58801196dd230531dfbc7109289956 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Thu, 18 Mar 2021 14:15:19 -0700 Subject: [PATCH 16/32] feat: allow omitting submitURL when uploadToServer is false (#28105) --- docs/api/crash-reporter.md | 3 ++- lib/browser/api/crash-reporter.ts | 4 ++-- spec-main/api-crash-reporter-spec.ts | 8 +++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/api/crash-reporter.md b/docs/api/crash-reporter.md index c03f8a7e03..8331bc8d43 100644 --- a/docs/api/crash-reporter.md +++ b/docs/api/crash-reporter.md @@ -77,7 +77,8 @@ The `crashReporter` module has the following methods: ### `crashReporter.start(options)` * `options` Object - * `submitURL` String - URL that crash reports will be sent to as POST. + * `submitURL` String (optional) - URL that crash reports will be sent to as + POST. Required unless `uploadToServer` is `false`. * `productName` String (optional) - Defaults to `app.name`. * `companyName` String (optional) _Deprecated_ - Deprecated alias for `{ globalExtra: { _companyName: ... } }`. diff --git a/lib/browser/api/crash-reporter.ts b/lib/browser/api/crash-reporter.ts index 99672c690d..16a9b1b5cb 100644 --- a/lib/browser/api/crash-reporter.ts +++ b/lib/browser/api/crash-reporter.ts @@ -10,13 +10,13 @@ class CrashReporter { extra = {}, globalExtra = {}, ignoreSystemCrashHandler = false, - submitURL, + submitURL = '', uploadToServer = true, rateLimit = false, compress = true } = options || {}; - if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start'); + if (uploadToServer && !submitURL) throw new Error('submitURL must be specified when uploadToServer is true'); if (!compress && uploadToServer) { deprecate.log('Sending uncompressed crash reports is deprecated and will be removed in a future version of Electron. Set { compress: true } to opt-in to the new behavior. Crash reports will be uploaded gzipped, which most crash reporting servers support.'); diff --git a/spec-main/api-crash-reporter-spec.ts b/spec-main/api-crash-reporter-spec.ts index 1136d9a522..739d5c09a8 100644 --- a/spec-main/api-crash-reporter-spec.ts +++ b/spec-main/api-crash-reporter-spec.ts @@ -361,7 +361,13 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_ it('requires that the submitURL option be specified', () => { expect(() => { crashReporter.start({} as any); - }).to.throw('submitURL is a required option to crashReporter.start'); + }).to.throw('submitURL must be specified when uploadToServer is true'); + }); + + it('allows the submitURL option to be omitted when uploadToServer is false', () => { + expect(() => { + crashReporter.start({ uploadToServer: false } as any); + }).not.to.throw(); }); it('can be called twice', async () => { From a68d43ce8ba2ff4f6bcdf6e01e5a1f260af265d6 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 18 Mar 2021 18:24:55 -0700 Subject: [PATCH 17/32] fix: missing HandleScope in ResetBrowserViews (#28266) --- shell/browser/api/electron_api_base_window.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index ff233e56bc..ed9e0eeb90 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -1112,6 +1112,8 @@ int32_t BaseWindow::GetID() const { } void BaseWindow::ResetBrowserViews() { + v8::HandleScope scope(isolate()); + for (auto& item : browser_views_) { gin::Handle browser_view; if (gin::ConvertFromV8(isolate(), From 79bcb882acc06989af601d3818d7eb9f0aaabfbe Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Fri, 19 Mar 2021 13:22:05 +0000 Subject: [PATCH 18/32] fix: drag region offsets in BrowserViews (#28268) --- shell/browser/native_browser_view_mac.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/browser/native_browser_view_mac.mm b/shell/browser/native_browser_view_mac.mm index 7478220587..ce0e3ff889 100644 --- a/shell/browser/native_browser_view_mac.mm +++ b/shell/browser/native_browser_view_mac.mm @@ -317,8 +317,9 @@ void NativeBrowserViewMac::UpdateDraggableRegions( const auto window_content_view_height = NSHeight(window_content_view.bounds); for (const auto& rect : drag_exclude_rects) { const auto x = rect.x() + offset.x(); - const auto y = window_content_view_height - rect.bottom() + offset.y(); + const auto y = window_content_view_height - (rect.bottom() + offset.y()); const auto exclude_rect = NSMakeRect(x, y, rect.width(), rect.height()); + const auto drag_region_view_exclude_rect = [window_content_view convertRect:exclude_rect toView:drag_region_view]; From a79ef2d5258f446f13ab0e7e2ed82a106cf1eb9e Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Fri, 19 Mar 2021 07:32:26 -0700 Subject: [PATCH 19/32] Bump v14.0.0-nightly.20210319 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index f1ef873e74..2aa1090fc7 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -14.0.0-nightly.20210318 \ No newline at end of file +14.0.0-nightly.20210319 \ No newline at end of file diff --git a/package.json b/package.json index 381727f700..f9cabb2d12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "14.0.0-nightly.20210318", + "version": "14.0.0-nightly.20210319", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index 5f23d20977..df1e842dc9 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 14,0,0,20210318 - PRODUCTVERSION 14,0,0,20210318 + FILEVERSION 14,0,0,20210319 + PRODUCTVERSION 14,0,0,20210319 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From db7059eb0a24910d673050395f9c099d98a465cd Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 22 Mar 2021 10:56:08 +0900 Subject: [PATCH 20/32] test: spellchecker may take several minutes to load under ASan (#28230) * test: spellchecker may take several minutes to load under ASan * Add TODO for the timeout --- spec-main/spellchecker-spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec-main/spellchecker-spec.ts b/spec-main/spellchecker-spec.ts index 31443d6a4a..d8f5c6ccb2 100644 --- a/spec-main/spellchecker-spec.ts +++ b/spec-main/spellchecker-spec.ts @@ -10,7 +10,9 @@ const features = process._linkedBinding('electron_common_features'); const v8Util = process._linkedBinding('electron_common_v8_util'); ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () { - this.timeout(200 * 1000); + // TODO(zcbenz): Spellchecker loads really slow on ASan, we should provide + // a small testing dictionary to make the tests load faster. + this.timeout((process.env.IS_ASAN ? 700 : 20) * 1000); let w: BrowserWindow; @@ -30,7 +32,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () // to detect spellchecker is to keep checking with a busy loop. async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) { const now = Date.now(); - const timeout = (process.env.IS_ASAN ? 180 : 10) * 1000; + const timeout = (process.env.IS_ASAN ? 600 : 10) * 1000; let contextMenuParams = await rightClick(); while (!fn(contextMenuParams) && (Date.now() - now < timeout)) { await delay(100); From c8d18a0a1c4d7ff9dcbfb45889a21b14a5e50ff6 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 22 Mar 2021 10:56:30 +0900 Subject: [PATCH 21/32] fix: destroy MessageDispatcher before WebContents (#28286) --- shell/browser/api/electron_api_web_contents.cc | 2 ++ shell/browser/ui/inspectable_web_contents.h | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index aa9cb0fdd4..65c4610855 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -914,6 +914,7 @@ WebContents::~WebContents() { return; } + inspectable_web_contents_->GetView()->SetDelegate(nullptr); if (guest_delegate_) guest_delegate_->WillDestroy(); @@ -1760,6 +1761,7 @@ void WebContents::DevToolsOpened() { v8::Locker locker(isolate); v8::HandleScope handle_scope(isolate); DCHECK(inspectable_web_contents_); + DCHECK(inspectable_web_contents_->GetDevToolsWebContents()); auto handle = FromOrCreate( isolate, inspectable_web_contents_->GetDevToolsWebContents()); devtools_web_contents_.Reset(isolate, handle.ToV8()); diff --git a/shell/browser/ui/inspectable_web_contents.h b/shell/browser/ui/inspectable_web_contents.h index f491798158..e59976a737 100644 --- a/shell/browser/ui/inspectable_web_contents.h +++ b/shell/browser/ui/inspectable_web_contents.h @@ -201,12 +201,6 @@ class InspectableWebContents void AddDevToolsExtensionsToClient(); #endif - bool frontend_loaded_ = false; - scoped_refptr agent_host_; - std::unique_ptr frontend_host_; - std::unique_ptr - embedder_message_dispatcher_; - DevToolsContentsResizingStrategy contents_resizing_strategy_; gfx::Rect devtools_bounds_; bool can_dock_ = true; @@ -228,6 +222,12 @@ class InspectableWebContents bool is_guest_; std::unique_ptr view_; + bool frontend_loaded_ = false; + scoped_refptr agent_host_; + std::unique_ptr frontend_host_; + std::unique_ptr + embedder_message_dispatcher_; + class NetworkResourceLoader; std::set, base::UniquePtrComparator> loaders_; From 8c3165434a5e329d9811c605b0de5bfdeb446916 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Sun, 21 Mar 2021 19:01:12 -0700 Subject: [PATCH 22/32] docs: update Node global symbols example to use contextBridge (#28245) * docs: update Node global symbols example to use contextBridge * Trigger Build * docs: change Node API example to show how to expose a crypto API Co-authored-by: Jeremy Rose * docs: Fix lint warning for crypto code sample * docs: update node API example description to emphasize APIs instead of symbols Co-authored-by: Jeremy Rose --- docs/api/browser-window.md | 2 +- docs/api/context-bridge.md | 21 ++++++++++++++++++++- docs/api/process.md | 13 ------------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index dec55f3b36..8bb4b4bd75 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -267,7 +267,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. be the absolute file path to the script. When node integration is turned off, the preload script can reintroduce Node global symbols back to the global scope. See example - [here](process.md#event-loaded). + [here](context-bridge.md#exposing-node-global-symbols). * `sandbox` Boolean (optional) - If set, this will sandbox the renderer associated with the window, making it compatible with the Chromium OS-level sandbox and disabling the Node.js engine. This is not the same as diff --git a/docs/api/context-bridge.md b/docs/api/context-bridge.md index ab157f79f6..04eb887bb7 100644 --- a/docs/api/context-bridge.md +++ b/docs/api/context-bridge.md @@ -33,7 +33,7 @@ page you load in your renderer executes code in this world. ### Isolated World -When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an +When `contextIsolation` is enabled in your `webPreferences` (this is the default behavior since Electron 12.0.0), your `preload` scripts run in an "Isolated World". You can read more about context isolation and what it affects in the [security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs. @@ -110,3 +110,22 @@ has been included below for completeness: | `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped | If the type you care about is not in the above table, it is probably not supported. + +### Exposing Node Global Symbols + +The `contextBridge` can be used by the preload script to give your renderer access to Node APIs. +The table of supported types described above also applies to Node APIs that you expose through `contextBridge`. +Please note that many Node APIs grant access to local system resources. +Be very cautious about which globals and APIs you expose to untrusted remote content. + +```javascript +const { contextBridge } = require('electron') +const crypto = require('crypto') +contextBridge.exposeInMainWorld('nodeCrypto', { + sha256sum (data) { + const hash = crypto.createHash('sha256') + hash.update(data) + return hash.digest('hex') + } +}) +``` diff --git a/docs/api/process.md b/docs/api/process.md index 2f7d97ad1a..3f0d2ed867 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -45,19 +45,6 @@ In sandboxed renderers the `process` object contains only a subset of the APIs: Emitted when Electron has loaded its internal initialization script and is beginning to load the web page or the main script. -It can be used by the preload script to add removed Node global symbols back to -the global scope when node integration is turned off: - -```javascript -// preload.js -const _setImmediate = setImmediate -const _clearImmediate = clearImmediate -process.once('loaded', () => { - global.setImmediate = _setImmediate - global.clearImmediate = _clearImmediate -}) -``` - ## Properties ### `process.defaultApp` _Readonly_ From 703f8707db7ec773bf8392c7877fb2f6b1d44604 Mon Sep 17 00:00:00 2001 From: Adrian Li Date: Mon, 22 Mar 2021 10:01:49 +0800 Subject: [PATCH 23/32] fix: Fix main.js source code (#28265) fix: Fix main.js source code, missing large bracket From 96ce59609db42dea00e0c21a257e90377c92f755 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Mon, 22 Mar 2021 09:42:06 +0000 Subject: [PATCH 24/32] refactor: prefer embedder-focused InitializeNodeWithArgs (#28271) --- shell/common/node_bindings.cc | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 7db58675e5..8f36ce62de 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -363,21 +363,22 @@ void NodeBindings::Initialize() { // Parse and set Node.js cli flags. SetNodeCliFlags(); - // pass non-null program name to argv so it doesn't crash - // trying to index into a nullptr - int argc = 1; - int exec_argc = 0; - const char* prog_name = "electron"; - const char** argv = &prog_name; - const char** exec_argv = nullptr; - std::unique_ptr env(base::Environment::Create()); SetNodeOptions(env.get()); - // TODO(codebytere): this is going to be deprecated in the near future - // in favor of Init(std::vector* argv, - // std::vector* exec_argv) - node::Init(&argc, argv, &exec_argc, &exec_argv); + std::vector argv = {"electron"}; + std::vector exec_argv; + std::vector errors; + + int exit_code = node::InitializeNodeWithArgs(&argv, &exec_argv, &errors); + + for (const std::string& error : errors) { + fprintf(stderr, "%s: %s\n", argv[0].c_str(), error.c_str()); + } + + if (exit_code != 0) { + exit(exit_code); + } #if defined(OS_WIN) // uv_init overrides error mode to suppress the default crash dialog, bring From fa320eeb90e0e8108b0b4cbf56fc704a2ef435fa Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Mon, 22 Mar 2021 07:32:51 -0700 Subject: [PATCH 25/32] Bump v14.0.0-nightly.20210322 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 2aa1090fc7..7f94c6b0e4 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -14.0.0-nightly.20210319 \ No newline at end of file +14.0.0-nightly.20210322 \ No newline at end of file diff --git a/package.json b/package.json index f9cabb2d12..0babc04185 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "14.0.0-nightly.20210319", + "version": "14.0.0-nightly.20210322", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index df1e842dc9..d1ab4adf6a 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 14,0,0,20210319 - PRODUCTVERSION 14,0,0,20210319 + FILEVERSION 14,0,0,20210322 + PRODUCTVERSION 14,0,0,20210322 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From e99893df2279d6bf567843384f67cb6f061faec0 Mon Sep 17 00:00:00 2001 From: Nikita Kot Date: Mon, 22 Mar 2021 18:16:35 +0100 Subject: [PATCH 26/32] feat: add ContextBridgeMutability feature (#27348) --- .../api/electron_api_context_bridge.cc | 13 +++++++++ spec-main/api-context-bridge-spec.ts | 29 +++++++++++++++++++ .../context-bridge-mutability/index.html | 17 +++++++++++ .../context-bridge-mutability/main.js | 20 +++++++++++++ .../context-bridge-mutability/package.json | 4 +++ .../context-bridge-mutability/preload.js | 5 ++++ 6 files changed, 88 insertions(+) create mode 100644 spec-main/fixtures/api/context-bridge/context-bridge-mutability/index.html create mode 100644 spec-main/fixtures/api/context-bridge/context-bridge-mutability/main.js create mode 100644 spec-main/fixtures/api/context-bridge/context-bridge-mutability/package.json create mode 100644 spec-main/fixtures/api/context-bridge/context-bridge-mutability/preload.js diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 0af41c50ec..64b13604fb 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -11,6 +11,7 @@ #include #include +#include "base/feature_list.h" #include "base/no_destructor.h" #include "base/strings/string_number_conversions.h" #include "content/public/renderer/render_frame.h" @@ -25,6 +26,12 @@ #include "third_party/blink/public/web/web_element.h" #include "third_party/blink/public/web/web_local_frame.h" +namespace features { + +const base::Feature kContextBridgeMutability{"ContextBridgeMutability", + base::FEATURE_DISABLED_BY_DEFAULT}; +} + namespace electron { namespace api { @@ -554,6 +561,12 @@ void ExposeAPIInMainWorld(v8::Isolate* isolate, if (maybe_proxy.IsEmpty()) return; auto proxy = maybe_proxy.ToLocalChecked(); + + if (base::FeatureList::IsEnabled(features::kContextBridgeMutability)) { + global.Set(key, proxy); + return; + } + if (proxy->IsObject() && !proxy->IsTypedArray() && !DeepFreeze(v8::Local::Cast(proxy), main_context)) return; diff --git a/spec-main/api-context-bridge-spec.ts b/spec-main/api-context-bridge-spec.ts index 743a660902..176711af2b 100644 --- a/spec-main/api-context-bridge-spec.ts +++ b/spec-main/api-context-bridge-spec.ts @@ -5,6 +5,7 @@ import * as fs from 'fs-extra'; import * as http from 'http'; import * as os from 'os'; import * as path from 'path'; +import * as cp from 'child_process'; import { closeWindow } from './window-helpers'; import { emittedOnce } from './events-helpers'; @@ -1165,3 +1166,31 @@ describe('contextBridge', () => { generateTests(true); generateTests(false); }); + +describe('ContextBridgeMutability', () => { + it('should not make properties unwriteable and read-only if ContextBridgeMutability is on', async () => { + const appPath = path.join(fixturesPath, 'context-bridge-mutability'); + const appProcess = cp.spawn(process.execPath, ['--enable-logging', '--enable-features=ContextBridgeMutability', appPath]); + + let output = ''; + appProcess.stdout.on('data', data => { output += data; }); + await emittedOnce(appProcess, 'exit'); + + expect(output).to.include('some-modified-text'); + expect(output).to.include('obj-modified-prop'); + expect(output).to.include('1,2,5,3,4'); + }); + + it('should make properties unwriteable and read-only if ContextBridgeMutability is off', async () => { + const appPath = path.join(fixturesPath, 'context-bridge-mutability'); + const appProcess = cp.spawn(process.execPath, ['--enable-logging', appPath]); + + let output = ''; + appProcess.stdout.on('data', data => { output += data; }); + await emittedOnce(appProcess, 'exit'); + + expect(output).to.include('some-text'); + expect(output).to.include('obj-prop'); + expect(output).to.include('1,2,3,4'); + }); +}); diff --git a/spec-main/fixtures/api/context-bridge/context-bridge-mutability/index.html b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/index.html new file mode 100644 index 0000000000..980a3d7f32 --- /dev/null +++ b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/index.html @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/spec-main/fixtures/api/context-bridge/context-bridge-mutability/main.js b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/main.js new file mode 100644 index 0000000000..622ef6acbf --- /dev/null +++ b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/main.js @@ -0,0 +1,20 @@ +const { app, BrowserWindow } = require('electron'); +const path = require('path'); + +let win; +app.whenReady().then(function () { + win = new BrowserWindow({ + webPreferences: { + contextIsolation: true, + preload: path.join(__dirname, 'preload.js') + } + }); + + win.loadFile('index.html'); + + win.webContents.on('console-message', (event, level, message) => { + console.log(message); + }); + + win.webContents.on('did-finish-load', () => app.quit()); +}); diff --git a/spec-main/fixtures/api/context-bridge/context-bridge-mutability/package.json b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/package.json new file mode 100644 index 0000000000..d1fc13838e --- /dev/null +++ b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/package.json @@ -0,0 +1,4 @@ +{ + "name": "context-bridge-mutability", + "main": "main.js" +} \ No newline at end of file diff --git a/spec-main/fixtures/api/context-bridge/context-bridge-mutability/preload.js b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/preload.js new file mode 100644 index 0000000000..e3d3d9abfa --- /dev/null +++ b/spec-main/fixtures/api/context-bridge/context-bridge-mutability/preload.js @@ -0,0 +1,5 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('str', 'some-text'); +contextBridge.exposeInMainWorld('obj', { prop: 'obj-prop' }); +contextBridge.exposeInMainWorld('arr', [1, 2, 3, 4]); From d10398610bb8f434b0a9c870115a1b704abdca85 Mon Sep 17 00:00:00 2001 From: John Kleinschmidt Date: Mon, 22 Mar 2021 13:34:13 -0400 Subject: [PATCH 27/32] ci: cleanup directories on arm64 machines after running tests (#28329) --- .circleci/config.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ee057c7f9..09cfa6e6b4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -230,6 +230,18 @@ step-maybe-notify-slack-success: &step-maybe-notify-slack-success fi when: on_success +step-maybe-cleanup-arm64-mac: &step-maybe-cleanup-arm64-mac + run: + name: Cleanup after testing + command: | + if [ "$TARGET_ARCH" == "arm64" ] &&[ "`uname`" == "Darwin" ]; then + killall Electron || echo "No Electron processes left running" + killall Safari || echo "No Safari processes left running" + rm -rf ~/Library/Application\ Support/Electron* + rm -rf ~/Library/Application\ Support/electron* + fi + when: always + step-checkout-electron: &step-checkout-electron checkout: path: src/electron @@ -1340,6 +1352,8 @@ steps-tests: &steps-tests - *step-maybe-notify-slack-failure + - *step-maybe-cleanup-arm64-mac + steps-test-nan: &steps-test-nan steps: - attach_workspace: From 665ac6f9c830edbf85596369b49844e1b910c59a Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Mon, 22 Mar 2021 20:11:03 +0000 Subject: [PATCH 28/32] fix: libuv hang on Windows (#28175) --- shell/common/node_bindings.cc | 4 +-- shell/common/node_bindings.h | 4 +-- spec-main/fixtures/apps/libuv-hang/index.html | 13 +++++++ spec-main/fixtures/apps/libuv-hang/main.js | 36 +++++++++++++++++++ spec-main/fixtures/apps/libuv-hang/preload.js | 16 +++++++++ .../fixtures/apps/libuv-hang/renderer.js | 8 +++++ spec-main/node-spec.ts | 11 ++++++ 7 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 spec-main/fixtures/apps/libuv-hang/index.html create mode 100644 spec-main/fixtures/apps/libuv-hang/main.js create mode 100644 spec-main/fixtures/apps/libuv-hang/preload.js create mode 100644 spec-main/fixtures/apps/libuv-hang/renderer.js diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index 8f36ce62de..91bf112554 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -534,15 +534,13 @@ void NodeBindings::LoadEnvironment(node::Environment* env) { void NodeBindings::PrepareMessageLoop() { #if !defined(OS_WIN) int handle = uv_backend_fd(uv_loop_); -#else - HANDLE handle = uv_loop_->iocp; -#endif // If the backend fd hasn't changed, don't proceed. if (handle == handle_) return; handle_ = handle; +#endif // Add dummy handle for libuv, otherwise libuv would quit when there is // nothing to do. diff --git a/shell/common/node_bindings.h b/shell/common/node_bindings.h index efc8b40364..3af029e387 100644 --- a/shell/common/node_bindings.h +++ b/shell/common/node_bindings.h @@ -159,9 +159,7 @@ class NodeBindings { // Isolate data used in creating the environment node::IsolateData* isolate_data_ = nullptr; -#if defined(OS_WIN) - HANDLE handle_; -#else +#if !defined(OS_WIN) int handle_ = -1; #endif diff --git a/spec-main/fixtures/apps/libuv-hang/index.html b/spec-main/fixtures/apps/libuv-hang/index.html new file mode 100644 index 0000000000..a3534d419a --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/index.html @@ -0,0 +1,13 @@ + + + + + + + Hello World! + + +

Hello World!

+ + + diff --git a/spec-main/fixtures/apps/libuv-hang/main.js b/spec-main/fixtures/apps/libuv-hang/main.js new file mode 100644 index 0000000000..4ca4ef15d9 --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/main.js @@ -0,0 +1,36 @@ +const { app, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); + +async function createWindow () { + const mainWindow = new BrowserWindow({ + show: false, + webPreferences: { + preload: path.join(__dirname, 'preload.js') + } + }); + + await mainWindow.loadFile('index.html'); +} + +app.whenReady().then(() => { + createWindow(); + app.on('activate', function () { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); +}); + +let count = 0; +ipcMain.handle('reload-successful', () => { + if (count === 2) { + app.quit(); + } else { + count++; + return count; + } +}); + +app.on('window-all-closed', function () { + if (process.platform !== 'darwin') app.quit(); +}); diff --git a/spec-main/fixtures/apps/libuv-hang/preload.js b/spec-main/fixtures/apps/libuv-hang/preload.js new file mode 100644 index 0000000000..a5840f557f --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/preload.js @@ -0,0 +1,16 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('api', { + ipcRenderer, + run: async () => { + const { promises: fs } = require('fs'); + for (let i = 0; i < 10; i++) { + const list = await fs.readdir('.', { withFileTypes: true }); + for (const file of list) { + if (file.isFile()) { + await fs.readFile(file.name, 'utf-8'); + } + } + } + } +}); diff --git a/spec-main/fixtures/apps/libuv-hang/renderer.js b/spec-main/fixtures/apps/libuv-hang/renderer.js new file mode 100644 index 0000000000..5f0a2b58b5 --- /dev/null +++ b/spec-main/fixtures/apps/libuv-hang/renderer.js @@ -0,0 +1,8 @@ +const count = localStorage.getItem('count'); + +const { run, ipcRenderer } = window.api; + +run().then(async () => { + const count = await ipcRenderer.invoke('reload-successful'); + if (count < 3) location.reload(); +}).catch(console.log); diff --git a/spec-main/node-spec.ts b/spec-main/node-spec.ts index def9ad1e46..45396fdd31 100644 --- a/spec-main/node-spec.ts +++ b/spec-main/node-spec.ts @@ -7,6 +7,7 @@ import { ifdescribe, ifit } from './spec-helpers'; import { webContents, WebContents } from 'electron/main'; const features = process._linkedBinding('electron_common_features'); +const mainFixturesPath = path.resolve(__dirname, 'fixtures'); describe('node feature', () => { const fixtures = path.join(__dirname, '..', 'spec', 'fixtures'); @@ -22,6 +23,16 @@ describe('node feature', () => { }); }); + it('does not hang when using the fs module in the renderer process', async () => { + const appPath = path.join(mainFixturesPath, 'apps', 'libuv-hang', 'main.js'); + const appProcess = childProcess.spawn(process.execPath, [appPath], { + cwd: path.join(mainFixturesPath, 'apps', 'libuv-hang'), + stdio: 'inherit' + }); + const [code] = await emittedOnce(appProcess, 'close'); + expect(code).to.equal(0); + }); + describe('contexts', () => { describe('setTimeout called under Chromium event loop in browser process', () => { it('Can be scheduled in time', (done) => { From 94af0e8bb081fb6d8033a94538eaeafdab6b583c Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 22 Mar 2021 16:33:03 -0700 Subject: [PATCH 29/32] fix: escape URL passed to shell.openExternal on windows (#28334) --- shell/common/platform_util_win.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shell/common/platform_util_win.cc b/shell/common/platform_util_win.cc index dae1e87720..ecef1f8a2a 100644 --- a/shell/common/platform_util_win.cc +++ b/shell/common/platform_util_win.cc @@ -32,6 +32,7 @@ #include "base/win/windows_version.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "net/base/escape.h" #include "shell/common/electron_paths.h" #include "ui/base/win/shell.h" #include "url/gurl.h" @@ -241,7 +242,9 @@ std::string OpenExternalOnWorkerThread( // Quote the input scheme to be sure that the command does not have // parameters unexpected by the external program. This url should already // have been escaped. - base::string16 escaped_url = L"\"" + base::UTF8ToUTF16(url.spec()) + L"\""; + base::string16 escaped_url = + L"\"" + base::UTF8ToUTF16(net::EscapeExternalHandlerValue(url.spec())) + + L"\""; base::string16 working_dir = options.working_dir.value(); if (reinterpret_cast( From 6a0b03ba6aa9d5f2294592b5494f0c0ef97488f7 Mon Sep 17 00:00:00 2001 From: Electron Bot Date: Tue, 23 Mar 2021 07:32:53 -0700 Subject: [PATCH 30/32] Bump v14.0.0-nightly.20210323 --- ELECTRON_VERSION | 2 +- package.json | 2 +- shell/browser/resources/win/electron.rc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index 7f94c6b0e4..450026c06d 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -14.0.0-nightly.20210322 \ No newline at end of file +14.0.0-nightly.20210323 \ No newline at end of file diff --git a/package.json b/package.json index 0babc04185..b56a254ad0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "14.0.0-nightly.20210322", + "version": "14.0.0-nightly.20210323", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { diff --git a/shell/browser/resources/win/electron.rc b/shell/browser/resources/win/electron.rc index d1ab4adf6a..c54bbbb036 100644 --- a/shell/browser/resources/win/electron.rc +++ b/shell/browser/resources/win/electron.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 14,0,0,20210322 - PRODUCTVERSION 14,0,0,20210322 + FILEVERSION 14,0,0,20210323 + PRODUCTVERSION 14,0,0,20210323 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From 7c3646308507633532a200addfa1e54d0691d03c Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Tue, 23 Mar 2021 14:40:37 +0000 Subject: [PATCH 31/32] fix: window.print() in pdf plugin (#28328) --- lib/browser/api/web-contents.ts | 1 + .../print_render_frame_helper_delegate.cc | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts index 01fd54aa3b..e9aab2cf82 100644 --- a/lib/browser/api/web-contents.ts +++ b/lib/browser/api/web-contents.ts @@ -94,6 +94,7 @@ const defaultPrintingSetting = { pagesPerSheet: 1, isFirstRequest: false, previewUIID: 0, + // True, if the document source is modifiable. e.g. HTML and not PDF. previewModifiable: true, printToPDF: true, deviceName: 'Save as PDF', diff --git a/shell/renderer/printing/print_render_frame_helper_delegate.cc b/shell/renderer/printing/print_render_frame_helper_delegate.cc index c554d4d723..8eb83a3be2 100644 --- a/shell/renderer/printing/print_render_frame_helper_delegate.cc +++ b/shell/renderer/printing/print_render_frame_helper_delegate.cc @@ -11,6 +11,7 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "extensions/common/constants.h" +#include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) namespace electron { @@ -50,6 +51,20 @@ bool PrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() { bool PrintRenderFrameHelperDelegate::OverridePrint( blink::WebLocalFrame* frame) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + auto* post_message_support = + extensions::PostMessageSupport::FromWebLocalFrame(frame); + if (post_message_support) { + // This message is handled in chrome/browser/resources/pdf/pdf_viewer.js and + // instructs the PDF plugin to print. This is to make window.print() on a + // PDF plugin document correctly print the PDF. See + // https://crbug.com/448720. + base::DictionaryValue message; + message.SetString("type", "print"); + post_message_support->PostMessageFromValue(message); + return true; + } +#endif // BUILDFLAG(ENABLE_EXTENSIONS) return false; } From 1e9e2f8cf69e1ec739b3f8e60658896042f65e2e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 24 Mar 2021 00:16:53 +0900 Subject: [PATCH 32/32] fix: make sure service worker scheme is registered with allowServiceWorkers (#28326) * Fix custom scheme not registered as service worker scheme * ServiceWorker loaders do not have WebContents associated * Add test for service worker * Revert "Fix custom scheme not registered as service worker scheme" This reverts commit a249235b220a0edcfcb906e0b3b3c0486ece73a6. * Add scheme to ServiceWorkerSchemes --- shell/browser/api/electron_api_protocol.cc | 8 ++++++ shell/browser/electron_browser_client.cc | 22 ++++++++------- spec-main/api-protocol-spec.ts | 31 ++++++++++++++++++++++ spec-main/index.js | 2 ++ 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/shell/browser/api/electron_api_protocol.cc b/shell/browser/api/electron_api_protocol.cc index 8e4329b523..952e32d0d3 100644 --- a/shell/browser/api/electron_api_protocol.cc +++ b/shell/browser/api/electron_api_protocol.cc @@ -10,6 +10,7 @@ #include "base/command_line.h" #include "base/stl_util.h" +#include "content/common/url_schemes.h" #include "content/public/browser/child_process_security_policy.h" #include "gin/object_template_builder.h" #include "shell/browser/browser.h" @@ -124,6 +125,13 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower, } if (custom_scheme.options.allowServiceWorkers) { service_worker_schemes.push_back(custom_scheme.scheme); + // There is no API to add service worker scheme, but there is an API to + // return const reference to the schemes vector. + // If in future the API is changed to return a copy instead of reference, + // the compilation will fail, and we should add a patch at that time. + auto& mutable_schemes = const_cast&>( + content::GetServiceWorkerSchemes()); + mutable_schemes.push_back(custom_scheme.scheme); } if (custom_scheme.options.stream) { g_streaming_schemes.push_back(custom_scheme.scheme); diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 80240d148f..3a42043bf6 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -1352,22 +1352,26 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( int render_process_id, int render_frame_id, NonNetworkURLLoaderFactoryMap* factories) { - content::RenderFrameHost* frame_host = - content::RenderFrameHost::FromID(render_process_id, render_frame_id); - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(frame_host); + auto* render_process_host = + content::RenderProcessHost::FromID(render_process_id); + DCHECK(render_process_host); + if (!render_process_host || !render_process_host->GetBrowserContext()) + return; + + ProtocolRegistry::FromBrowserContext(render_process_host->GetBrowserContext()) + ->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource, + factories); - if (web_contents) { - ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext()) - ->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource, - factories); - } #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, render_frame_id); if (factory) factories->emplace(extensions::kExtensionScheme, std::move(factory)); + content::RenderFrameHost* frame_host = + content::RenderFrameHost::FromID(render_process_id, render_frame_id); + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(frame_host); if (!web_contents) return; diff --git a/spec-main/api-protocol-spec.ts b/spec-main/api-protocol-spec.ts index 31abb6ce13..720d5bcaa1 100644 --- a/spec-main/api-protocol-spec.ts +++ b/spec-main/api-protocol-spec.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +import { v4 } from 'uuid'; import { protocol, webContents, WebContents, session, BrowserWindow, ipcMain } from 'electron/main'; import { AddressInfo } from 'net'; import * as ChildProcess from 'child_process'; @@ -724,6 +725,36 @@ describe('protocol module', () => { }); }); + describe('protocol.registerSchemesAsPrivileged allowServiceWorkers', () => { + const { serviceWorkerScheme } = global as any; + protocol.registerStringProtocol(serviceWorkerScheme, (request, cb) => { + if (request.url.endsWith('.js')) { + cb({ + mimeType: 'text/javascript', + charset: 'utf-8', + data: 'console.log("Loaded")' + }); + } else { + cb({ + mimeType: 'text/html', + charset: 'utf-8', + data: '' + }); + } + }); + after(() => protocol.unregisterProtocol(serviceWorkerScheme)); + + it('should fail when registering invalid service worker', async () => { + await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`); + await expect(contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.notjs', {scope: './'})`)).to.be.rejected(); + }); + + it('should be able to register service worker for custom scheme', async () => { + await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`); + await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.js', {scope: './'})`); + }); + }); + describe.skip('protocol.registerSchemesAsPrivileged standard', () => { const standardScheme = (global as any).standardScheme; const origin = `${standardScheme}://fake-host`; diff --git a/spec-main/index.js b/spec-main/index.js index 60c6d10602..406a7b7ecf 100644 --- a/spec-main/index.js +++ b/spec-main/index.js @@ -34,9 +34,11 @@ app.commandLine.appendSwitch('use-fake-device-for-media-stream'); global.standardScheme = 'app'; global.zoomScheme = 'zoom'; +global.serviceWorkerScheme = 'sw'; protocol.registerSchemesAsPrivileged([ { scheme: global.standardScheme, privileges: { standard: true, secure: true, stream: false } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, + { scheme: global.serviceWorkerScheme, privileges: { allowServiceWorkers: true, standard: true, secure: true } }, { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'no-cors', privileges: { supportFetchAPI: true } },