refactor: move JS dialog handling to JS (#40598)

This commit is contained in:
Jeremy Rose 2023-12-05 17:36:23 -08:00 committed by GitHub
parent ee8d97d7fe
commit 85bc005cd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 302 additions and 200 deletions

View file

@ -1,5 +1,5 @@
import { expect } from 'chai';
import { BrowserWindow, WebContents, webFrameMain, session, ipcMain, app, protocol, webContents } from 'electron/main';
import { BrowserWindow, WebContents, webFrameMain, session, ipcMain, app, protocol, webContents, dialog, MessageBoxOptions } from 'electron/main';
import { closeAllWindows } from './lib/window-helpers';
import * as https from 'node:https';
import * as http from 'node:http';
@ -2396,6 +2396,26 @@ describe('chromium features', () => {
window.alert({ toString: null });
}).to.throw('Cannot convert object to primitive value');
});
it('shows a message box', async () => {
const w = new BrowserWindow({ show: false });
w.loadURL('about:blank');
const p = once(w.webContents, '-run-dialog');
w.webContents.executeJavaScript('alert("hello")', true);
const [info] = await p;
expect(info.frame).to.equal(w.webContents.mainFrame);
expect(info.messageText).to.equal('hello');
expect(info.dialogType).to.equal('alert');
});
it('does not crash if a webContents is destroyed while an alert is showing', async () => {
const w = new BrowserWindow({ show: false });
w.loadURL('about:blank');
const p = once(w.webContents, '-run-dialog');
w.webContents.executeJavaScript('alert("hello")', true);
await p;
w.webContents.close();
});
});
describe('window.confirm(message, title)', () => {
@ -2404,6 +2424,160 @@ describe('chromium features', () => {
(window.confirm as any)({ toString: null }, 'title');
}).to.throw('Cannot convert object to primitive value');
});
it('shows a message box', async () => {
const w = new BrowserWindow({ show: false });
w.loadURL('about:blank');
const p = once(w.webContents, '-run-dialog');
const resultPromise = w.webContents.executeJavaScript('confirm("hello")', true);
const [info, cb] = await p;
expect(info.frame).to.equal(w.webContents.mainFrame);
expect(info.messageText).to.equal('hello');
expect(info.dialogType).to.equal('confirm');
cb(true, '');
const result = await resultPromise;
expect(result).to.be.true();
});
});
describe('safeDialogs web preference', () => {
const originalShowMessageBox = dialog.showMessageBox;
afterEach(() => {
dialog.showMessageBox = originalShowMessageBox;
if (protocol.isProtocolHandled('https')) protocol.unhandle('https');
if (protocol.isProtocolHandled('file')) protocol.unhandle('file');
});
it('does not show the checkbox if not enabled', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { safeDialogs: false } });
w.loadURL('about:blank');
// 1. The first alert() doesn't show the safeDialogs message.
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: false });
await w.webContents.executeJavaScript('alert("hi")');
let recordedOpts: MessageBoxOptions | undefined;
dialog.showMessageBox = (bw, opts?: MessageBoxOptions) => {
recordedOpts = opts;
return Promise.resolve({ response: 0, checkboxChecked: false });
};
await w.webContents.executeJavaScript('alert("hi")');
expect(recordedOpts?.checkboxLabel).to.equal('');
});
it('is respected', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { safeDialogs: true } });
w.loadURL('about:blank');
// 1. The first alert() doesn't show the safeDialogs message.
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: false });
await w.webContents.executeJavaScript('alert("hi")');
// 2. The second alert() shows the message with a checkbox. Respond that we checked it.
let recordedOpts: MessageBoxOptions | undefined;
dialog.showMessageBox = (bw, opts?: MessageBoxOptions) => {
recordedOpts = opts;
return Promise.resolve({ response: 0, checkboxChecked: true });
};
await w.webContents.executeJavaScript('alert("hi")');
expect(recordedOpts?.checkboxLabel).to.be.a('string').with.length.above(0);
// 3. The third alert() shouldn't show a dialog.
dialog.showMessageBox = () => Promise.reject(new Error('unexpected showMessageBox'));
await w.webContents.executeJavaScript('alert("hi")');
});
it('shows the safeDialogMessage', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { safeDialogs: true, safeDialogsMessage: 'foo bar' } });
w.loadURL('about:blank');
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: false });
await w.webContents.executeJavaScript('alert("hi")');
let recordedOpts: MessageBoxOptions | undefined;
dialog.showMessageBox = (bw, opts?: MessageBoxOptions) => {
recordedOpts = opts;
return Promise.resolve({ response: 0, checkboxChecked: true });
};
await w.webContents.executeJavaScript('alert("hi")');
expect(recordedOpts?.checkboxLabel).to.equal('foo bar');
});
it('has persistent state across navigations', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { safeDialogs: true } });
w.loadURL('about:blank');
// 1. The first alert() doesn't show the safeDialogs message.
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: false });
await w.webContents.executeJavaScript('alert("hi")');
// 2. The second alert() shows the message with a checkbox. Respond that we checked it.
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: true });
await w.webContents.executeJavaScript('alert("hi")');
// 3. The third alert() shouldn't show a dialog.
dialog.showMessageBox = () => Promise.reject(new Error('unexpected showMessageBox'));
await w.webContents.executeJavaScript('alert("hi")');
// 4. After navigating to the same origin, message boxes should still be hidden.
w.loadURL('about:blank');
await w.webContents.executeJavaScript('alert("hi")');
});
it('is separated by origin', async () => {
protocol.handle('https', () => new Response(''));
const w = new BrowserWindow({ show: false, webPreferences: { safeDialogs: true } });
w.loadURL('https://example1');
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: false });
await w.webContents.executeJavaScript('alert("hi")');
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: true });
await w.webContents.executeJavaScript('alert("hi")');
dialog.showMessageBox = () => Promise.reject(new Error('unexpected showMessageBox'));
await w.webContents.executeJavaScript('alert("hi")');
// A different origin is allowed to show message boxes after navigation.
w.loadURL('https://example2');
let dialogWasShown = false;
dialog.showMessageBox = () => {
dialogWasShown = true;
return Promise.resolve({ response: 0, checkboxChecked: false });
};
await w.webContents.executeJavaScript('alert("hi")');
expect(dialogWasShown).to.be.true();
// Navigating back to the first origin means alerts are blocked again.
w.loadURL('https://example1');
dialog.showMessageBox = () => Promise.reject(new Error('unexpected showMessageBox'));
await w.webContents.executeJavaScript('alert("hi")');
});
it('treats different file: paths as different origins', async () => {
protocol.handle('file', () => new Response(''));
const w = new BrowserWindow({ show: false, webPreferences: { safeDialogs: true } });
w.loadURL('file:///path/1');
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: false });
await w.webContents.executeJavaScript('alert("hi")');
dialog.showMessageBox = () => Promise.resolve({ response: 0, checkboxChecked: true });
await w.webContents.executeJavaScript('alert("hi")');
dialog.showMessageBox = () => Promise.reject(new Error('unexpected showMessageBox'));
await w.webContents.executeJavaScript('alert("hi")');
w.loadURL('file:///path/2');
let dialogWasShown = false;
dialog.showMessageBox = () => {
dialogWasShown = true;
return Promise.resolve({ response: 0, checkboxChecked: false });
};
await w.webContents.executeJavaScript('alert("hi")');
expect(dialogWasShown).to.be.true();
});
});
describe('disableDialogs web preference', () => {
const originalShowMessageBox = dialog.showMessageBox;
afterEach(() => {
dialog.showMessageBox = originalShowMessageBox;
if (protocol.isProtocolHandled('https')) protocol.unhandle('https');
});
it('is respected', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { disableDialogs: true } });
w.loadURL('about:blank');
dialog.showMessageBox = () => Promise.reject(new Error('unexpected message box'));
await w.webContents.executeJavaScript('alert("hi")');
});
});
});