import { expect } from 'chai'; import * as path from 'node:path'; import * as http from 'node:http'; import { emittedNTimes } from './lib/events-helpers'; import { closeWindow } from './lib/window-helpers'; import { app, BrowserWindow, ipcMain } from 'electron/main'; import { ifdescribe, listen } from './lib/spec-helpers'; import { once } from 'node:events'; describe('renderer nodeIntegrationInSubFrames', () => { const generateTests = (description: string, webPreferences: any) => { describe(description, () => { const fixtureSuffix = webPreferences.webviewTag ? '-webview' : ''; let w: BrowserWindow; beforeEach(async () => { await closeWindow(w); w = new BrowserWindow({ show: false, width: 400, height: 400, webPreferences }); }); afterEach(async () => { await closeWindow(w); w = null as unknown as BrowserWindow; }); it('should load preload scripts in top level iframes', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [event1, event2] = await detailsPromise; expect(event1[0].frameId).to.not.equal(event2[0].frameId); expect(event1[0].frameId).to.equal(event1[2]); expect(event2[0].frameId).to.equal(event2[2]); expect(event1[0].senderFrame.routingId).to.equal(event1[2]); expect(event2[0].senderFrame.routingId).to.equal(event2[2]); }); it('should load preload scripts in nested iframes', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`)); const [event1, event2, event3] = await detailsPromise; expect(event1[0].frameId).to.not.equal(event2[0].frameId); expect(event1[0].frameId).to.not.equal(event3[0].frameId); expect(event2[0].frameId).to.not.equal(event3[0].frameId); expect(event1[0].frameId).to.equal(event1[2]); expect(event2[0].frameId).to.equal(event2[2]); expect(event3[0].frameId).to.equal(event3[2]); expect(event1[0].senderFrame.routingId).to.equal(event1[2]); expect(event2[0].senderFrame.routingId).to.equal(event2[2]); expect(event3[0].senderFrame.routingId).to.equal(event3[2]); }); it('should correctly reply to the main frame with using event.reply', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [event1] = await detailsPromise; const pongPromise = once(ipcMain, 'preload-pong'); event1[0].reply('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event1[0].frameId); }); it('should correctly reply to the main frame with using event.senderFrame.send', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [event1] = await detailsPromise; const pongPromise = once(ipcMain, 'preload-pong'); event1[0].senderFrame.send('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event1[0].frameId); }); it('should correctly reply to the sub-frames with using event.reply', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [, event2] = await detailsPromise; const pongPromise = once(ipcMain, 'preload-pong'); event2[0].reply('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event2[0].frameId); }); it('should correctly reply to the sub-frames with using event.senderFrame.send', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [, event2] = await detailsPromise; const pongPromise = once(ipcMain, 'preload-pong'); event2[0].senderFrame.send('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event2[0].frameId); }); it('should correctly reply to the nested sub-frames with using event.reply', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`)); const [, , event3] = await detailsPromise; const pongPromise = once(ipcMain, 'preload-pong'); event3[0].reply('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event3[0].frameId); }); it('should correctly reply to the nested sub-frames with using event.senderFrame.send', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`)); const [, , event3] = await detailsPromise; const pongPromise = once(ipcMain, 'preload-pong'); event3[0].senderFrame.send('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event3[0].frameId); }); it('should not expose globals in main world', async () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const details = await detailsPromise; const senders = details.map(event => event[0].sender); const isolatedGlobals = await Promise.all(senders.map(sender => sender.executeJavaScript('window.isolatedGlobal'))); for (const result of isolatedGlobals) { if (webPreferences.contextIsolation === undefined || webPreferences.contextIsolation) { expect(result).to.be.undefined(); } else { expect(result).to.equal(true); } } }); }); }; const generateConfigs = (webPreferences: any, ...permutations: {name: string, webPreferences: any}[]) => { const configs = [{ webPreferences, names: [] as string[] }]; for (const permutation of permutations) { const length = configs.length; for (let j = 0; j < length; j++) { const newConfig = Object.assign({}, configs[j]); newConfig.webPreferences = Object.assign({}, newConfig.webPreferences, permutation.webPreferences); newConfig.names = newConfig.names.slice(0); newConfig.names.push(permutation.name); configs.push(newConfig); } } return configs.map((config: any) => { if (config.names.length > 0) { config.title = `with ${config.names.join(', ')} on`; } else { config.title = 'without anything special turned on'; } delete config.names; return config as {title: string, webPreferences: any}; }); }; const configs = generateConfigs( { preload: path.resolve(__dirname, 'fixtures/sub-frames/preload.js'), nodeIntegrationInSubFrames: true }, { name: 'sandbox', webPreferences: { sandbox: true } }, { name: 'context isolation disabled', webPreferences: { contextIsolation: false } }, { name: 'webview', webPreferences: { webviewTag: true, preload: false } } ); for (const config of configs) { generateTests(config.title, config.webPreferences); } describe('internal