diff --git a/docs/api/context-bridge.md b/docs/api/context-bridge.md index 966ab40f97c41..93949d374a292 100644 --- a/docs/api/context-bridge.md +++ b/docs/api/context-bridge.md @@ -90,7 +90,7 @@ contextBridge.exposeInMainWorld( ) ``` -An example of complex API exposed in an isolated world is shown below: +An example of API exposed in an isolated world is shown below: ```javascript @@ -104,22 +104,7 @@ contextBridge.exposeInIsolatedWorld( 1005, 'electron', { - doThing: () => ipcRenderer.send('do-a-thing'), - myPromises: [Promise.resolve(), Promise.reject(new Error('whoops'))], - anAsyncFunction: async () => 123, - data: { - myFlags: ['a', 'b', 'c'], - bootTime: 1234 - }, - nestedAPI: { - evenDeeper: { - youCanDoThisAsMuchAsYouWant: { - fn: () => ({ - returnData: 123 - }) - } - } - } + doThing: () => ipcRenderer.send('do-a-thing') } ) diff --git a/lib/renderer/api/context-bridge.ts b/lib/renderer/api/context-bridge.ts index 566d6de9f21e9..5833f15af3c0a 100644 --- a/lib/renderer/api/context-bridge.ts +++ b/lib/renderer/api/context-bridge.ts @@ -1,11 +1,7 @@ const binding = process._linkedBinding('electron_renderer_context_bridge'); const checkContextIsolationEnabled = () => { - if (!process.contextIsolated) { - throw new Error( - 'contextBridge API can only be used when contextIsolation is enabled' - ); - } + if (!process.contextIsolated) throw new Error('contextBridge API can only be used when contextIsolation is enabled'); }; const contextBridge: Electron.ContextBridge = { @@ -26,22 +22,11 @@ export const internalContextBridge = { overrideGlobalValueFromIsolatedWorld: (keys: string[], value: any) => { return binding._overrideGlobalValueFromIsolatedWorld(keys, value, false); }, - overrideGlobalValueWithDynamicPropsFromIsolatedWorld: ( - keys: string[], - value: any - ) => { + overrideGlobalValueWithDynamicPropsFromIsolatedWorld: (keys: string[], value: any) => { return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true); }, - overrideGlobalPropertyFromIsolatedWorld: ( - keys: string[], - getter: Function, - setter?: Function - ) => { - return binding._overrideGlobalPropertyFromIsolatedWorld( - keys, - getter, - setter || null - ); + overrideGlobalPropertyFromIsolatedWorld: (keys: string[], getter: Function, setter?: Function) => { + return binding._overrideGlobalPropertyFromIsolatedWorld(keys, getter, setter || null); }, isInMainWorld: () => binding._isCalledFromMainWorld() as boolean }; diff --git a/spec-main/api-context-bridge-spec.ts b/spec-main/api-context-bridge-spec.ts index 5dd102de32df8..6d02a0bc0343c 100644 --- a/spec-main/api-context-bridge-spec.ts +++ b/spec-main/api-context-bridge-spec.ts @@ -1,23 +1,17 @@ -import * as cp from 'child_process'; +import { BrowserWindow, ipcMain } from 'electron/main'; +import { contextBridge } from 'electron/renderer'; +import { expect } from 'chai'; 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 { BrowserWindow, ipcMain } from 'electron/main'; - -import { AddressInfo } from 'net'; import { closeWindow } from './window-helpers'; -import { contextBridge } from 'electron/renderer'; import { emittedOnce } from './events-helpers'; -import { expect } from 'chai'; +import { AddressInfo } from 'net'; -const fixturesPath = path.resolve( - __dirname, - 'fixtures', - 'api', - 'context-bridge' -); +const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'context-bridge'); describe('contextBridge', () => { let w: BrowserWindow; @@ -29,13 +23,11 @@ describe('contextBridge', () => { res.setHeader('Content-Type', 'text/html'); res.end(''); }); - await new Promise((resolve) => - server.listen(0, '127.0.0.1', resolve) - ); + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); }); after(async () => { - if (server) await new Promise((resolve) => server.close(resolve)); + if (server) await new Promise(resolve => server.close(resolve)); server = null as any; }); @@ -52,9 +44,7 @@ describe('contextBridge', () => { preload: path.resolve(fixturesPath, 'can-bind-preload.js') } }); - const [, bound] = await emittedOnce(ipcMain, 'context-bridge-bound', () => - w.loadFile(path.resolve(fixturesPath, 'empty.html')) - ); + const [, bound] = await emittedOnce(ipcMain, 'context-bridge-bound', () => w.loadFile(path.resolve(fixturesPath, 'empty.html'))); expect(bound).to.equal(false); }); @@ -66,67 +56,55 @@ describe('contextBridge', () => { preload: path.resolve(fixturesPath, 'can-bind-preload.js') } }); - const [, bound] = await emittedOnce(ipcMain, 'context-bridge-bound', () => - w.loadFile(path.resolve(fixturesPath, 'empty.html')) - ); + const [, bound] = await emittedOnce(ipcMain, 'context-bridge-bound', () => w.loadFile(path.resolve(fixturesPath, 'empty.html'))); expect(bound).to.equal(true); }); const generateTests = (useSandbox: boolean) => { - const getGCInfo = async (): Promise<{ - trackedValues: number; - }> => { - const [, info] = await emittedOnce(ipcMain, 'gc-info', () => - w.webContents.send('get-gc-info') - ); - return info; - }; - - const forceGCOnWindow = async () => { - w.webContents.debugger.attach(); - await w.webContents.debugger.sendCommand('HeapProfiler.enable'); - await w.webContents.debugger.sendCommand('HeapProfiler.collectGarbage'); - await w.webContents.debugger.sendCommand('HeapProfiler.disable'); - w.webContents.debugger.detach(); - }; - describe('exposeInMainWorld', () => { - const makeBindingWindow = async (bindingCreator: Function) => { - const preloadContent = `const renderer_1 = require('electron'); - ${ - useSandbox - ? '' - : `require('v8').setFlagsFromString('--expose_gc'); + describe(`with sandbox=${useSandbox}`, () => { + describe('exposeInMainWorld', () => { + const makeBindingWindow = async (bindingCreator: Function) => { + const preloadContent = `const renderer_1 = require('electron'); + ${useSandbox ? '' : `require('v8').setFlagsFromString('--expose_gc'); const gc=require('vm').runInNewContext('gc'); renderer_1.contextBridge.exposeInMainWorld('GCRunner', { run: () => gc() - });` - } + });`} (${bindingCreator.toString()})();`; - const tmpDir = await fs.mkdtemp( - path.resolve(os.tmpdir(), 'electron-spec-preload-') - ); - dir = tmpDir; - await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent); - w = new BrowserWindow({ - show: false, - webPreferences: { - contextIsolation: true, - nodeIntegration: true, - sandbox: useSandbox, - preload: path.resolve(tmpDir, 'preload.js'), - additionalArguments: [ - '--unsafely-expose-electron-internals-for-testing' - ] - } - }); - await w.loadURL( - `http://127.0.0.1:${(server.address() as AddressInfo).port}` - ); - }; - const callWithBindings = (fn: Function) => - w.webContents.executeJavaScript(`(${fn.toString()})(window)`); - - describe(`with sandbox=${useSandbox}`, () => { + const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-spec-preload-')); + dir = tmpDir; + await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent); + w = new BrowserWindow({ + show: false, + webPreferences: { + contextIsolation: true, + nodeIntegration: true, + sandbox: useSandbox, + preload: path.resolve(tmpDir, 'preload.js'), + additionalArguments: ['--unsafely-expose-electron-internals-for-testing'] + } + }); + await w.loadURL(`http://127.0.0.1:${(server.address() as AddressInfo).port}`); + }; + + const callWithBindings = (fn: Function) => + w.webContents.executeJavaScript(`(${fn.toString()})(window)`); + + const getGCInfo = async (): Promise<{ + trackedValues: number; + }> => { + const [, info] = await emittedOnce(ipcMain, 'gc-info', () => w.webContents.send('get-gc-info')); + return info; + }; + + const forceGCOnWindow = async () => { + w.webContents.debugger.attach(); + await w.webContents.debugger.sendCommand('HeapProfiler.enable'); + await w.webContents.debugger.sendCommand('HeapProfiler.collectGarbage'); + await w.webContents.debugger.sendCommand('HeapProfiler.disable'); + w.webContents.debugger.detach(); + }; + it('should proxy numbers', async () => { await makeBindingWindow(() => { contextBridge.exposeInMainWorld('example', 123); @@ -273,8 +251,7 @@ describe('contextBridge', () => { it('should proxy promises and resolve with the correct value', async () => { await makeBindingWindow(() => { - contextBridge.exposeInMainWorld( - 'example', + contextBridge.exposeInMainWorld('example', Promise.resolve('i-resolved') ); }); @@ -298,10 +275,7 @@ describe('contextBridge', () => { it('should proxy promises and reject with the correct value', async () => { await makeBindingWindow(() => { - contextBridge.exposeInMainWorld( - 'example', - Promise.reject(new Error('i-rejected')) - ); + contextBridge.exposeInMainWorld('example', Promise.reject(new Error('i-rejected'))); }); const result = await callWithBindings(async (root: any) => { try { @@ -311,9 +285,7 @@ describe('contextBridge', () => { return err; } }); - expect(result) - .to.be.an.instanceOf(Error) - .with.property('message', 'i-rejected'); + expect(result).to.be.an.instanceOf(Error).with.property('message', 'i-rejected'); }); it('should proxy nested promises and reject with the correct value', async () => { @@ -330,18 +302,13 @@ describe('contextBridge', () => { return err; } }); - expect(result) - .to.be.an.instanceOf(Error) - .with.property('message', 'i-rejected'); + expect(result).to.be.an.instanceOf(Error).with.property('message', 'i-rejected'); }); it('should proxy promises and resolve with the correct value if it resolves later', async () => { await makeBindingWindow(() => { contextBridge.exposeInMainWorld('example', { - myPromise: () => - new Promise((resolve) => - setTimeout(() => resolve('delayed'), 20) - ) + myPromise: () => new Promise(resolve => setTimeout(() => resolve('delayed'), 20)) }); }); const result = await callWithBindings((root: any) => { @@ -353,10 +320,7 @@ describe('contextBridge', () => { it('should proxy nested promises correctly', async () => { await makeBindingWindow(() => { contextBridge.exposeInMainWorld('example', { - myPromise: () => - new Promise((resolve) => - setTimeout(() => resolve(Promise.resolve(123)), 20) - ) + myPromise: () => new Promise(resolve => setTimeout(() => resolve(Promise.resolve(123)), 20)) }); }); const result = await callWithBindings((root: any) => { @@ -375,12 +339,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings(async (root: any) => { - return [ - root.example.getNumber(), - root.example.getString(), - root.example.getBoolean(), - await root.example.getPromise() - ]; + return [root.example.getNumber(), root.example.getString(), root.example.getBoolean(), await root.example.getPromise()]; }); expect(result).to.deep.equal([123, 'help', false, 'promise']); }); @@ -429,11 +388,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings(async (root: any) => { - return [ - root.example.doThing(), - root.example.doThing(), - root.example.doThing() - ]; + return [root.example.doThing(), root.example.doThing(), root.example.doThing()]; }); expect(result).to.deep.equal([123, 123, 123]); }); @@ -457,9 +412,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings((root: any) => { - return root.example.getPromiseValue( - Promise.resolve('my-proxied-value') - ); + return root.example.getPromiseValue(Promise.resolve('my-proxied-value')); }); expect(result).to.equal('my-proxied-value'); }); @@ -473,12 +426,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings(async (root: any) => { - return [ - root.example[1], - root.example[2], - root.example[3], - Array.isArray(root.example) - ]; + return [root.example[1], root.example[2], root.example[3], Array.isArray(root.example)]; }); expect(result).to.deep.equal([123, 456, 789, false]); }); @@ -531,10 +479,7 @@ describe('contextBridge', () => { const result = await callWithBindings((root: any) => { return root.isSymbol(root.symbol); }); - expect(result).to.equal( - true, - 'symbols should be equal across contexts' - ); + expect(result).to.equal(true, 'symbols should be equal across contexts'); }); it('should proxy symbols such that symbol equality works', async () => { @@ -548,10 +493,7 @@ describe('contextBridge', () => { const result = await callWithBindings((root: any) => { return root.example.isSymbol(root.example.getSymbol()); }); - expect(result).to.equal( - true, - 'symbols should be equal across contexts' - ); + expect(result).to.equal(true, 'symbols should be equal across contexts'); }); it('should proxy symbols such that symbol key lookup works', async () => { @@ -565,10 +507,7 @@ describe('contextBridge', () => { const result = await callWithBindings((root: any) => { return root.example.getObject()[root.example.getSymbol()]; }); - expect(result).to.equal( - 123, - 'symbols key lookup should work across contexts' - ); + expect(result).to.equal(123, 'symbols key lookup should work across contexts'); }); it('should proxy typed arrays', async () => { @@ -616,11 +555,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings((root: any) => { - return [ - root.example.o.value, - root.example.o.o.value, - root.example.o.o.o.value - ]; + return [root.example.o.value, root.example.o.o.value, root.example.o.o.o.value]; }); expect(result).to.deep.equal([135, 135, 135]); }); @@ -632,11 +567,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings((root: any) => { - return [ - root.example.getElem().tagName, - root.example.getElem().constructor.name, - typeof root.example.getElem().querySelector - ]; + return [root.example.getElem().tagName, root.example.getElem().constructor.name, typeof root.example.getElem().querySelector]; }); expect(result).to.deep.equal(['BODY', 'HTMLBodyElement', 'function']); }); @@ -646,11 +577,7 @@ describe('contextBridge', () => { contextBridge.exposeInMainWorld('example', { getElemInfo: (fn: Function) => { const elem = fn(); - return [ - elem.tagName, - elem.constructor.name, - typeof elem.querySelector - ]; + return [elem.tagName, elem.constructor.name, typeof elem.querySelector]; } }); }); @@ -692,12 +619,7 @@ describe('contextBridge', () => { it('should release the global hold on methods sent across contexts', async () => { await makeBindingWindow(() => { const trackedValues: WeakRef[] = []; - require('electron').ipcRenderer.on('get-gc-info', (e) => - e.sender.send('gc-info', { - trackedValues: trackedValues.filter((value) => value.deref()) - .length - }) - ); + require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length })); contextBridge.exposeInMainWorld('example', { getFunction: () => () => 123, track: (value: object) => { @@ -727,12 +649,7 @@ describe('contextBridge', () => { it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => { await makeBindingWindow(() => { const trackedValues: WeakRef[] = []; - require('electron').ipcRenderer.on('get-gc-info', (e) => - e.sender.send('gc-info', { - trackedValues: trackedValues.filter((value) => value.deref()) - .length - }) - ); + require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length })); contextBridge.exposeInMainWorld('example', { getFunction: () => () => 123, track: (value: object) => { @@ -741,10 +658,7 @@ describe('contextBridge', () => { }); require('electron').ipcRenderer.send('window-ready-for-tasking'); }); - const loadPromise = emittedOnce( - ipcMain, - 'window-ready-for-tasking' - ); + const loadPromise = emittedOnce(ipcMain, 'window-ready-for-tasking'); expect((await getGCInfo()).trackedValues).to.equal(0); await callWithBindings((root: any) => { root.example.track(root.example.getFunction()); @@ -786,10 +700,9 @@ describe('contextBridge', () => { it('should work with complex nested methods and promises', async () => { await makeBindingWindow(() => { contextBridge.exposeInMainWorld('example', { - first: (second: Function) => - second((fourth: Function) => { - return fourth(); - }) + first: (second: Function) => second((fourth: Function) => { + return fourth(); + }) }); }); const result = await callWithBindings((root: any) => { @@ -802,8 +715,8 @@ describe('contextBridge', () => { it('should work with complex nested methods and promises attached directly to the global', async () => { await makeBindingWindow(() => { - contextBridge.exposeInMainWorld('example', (second: Function) => - second((fourth: Function) => { + contextBridge.exposeInMainWorld('example', + (second: Function) => second((fourth: Function) => { return fourth(); }) ); @@ -862,7 +775,8 @@ describe('contextBridge', () => { throwNotClonable: () => { return Object(Symbol('foo')); }, - argumentConvert: () => {} + argumentConvert: () => { + } }); }); const result = await callWithBindings((root: any) => { @@ -874,32 +788,13 @@ describe('contextBridge', () => { } return null; }; - const normalIsError = - Object.getPrototypeOf(getError(root.example.throwNormal)) === - Error.prototype; - const weirdIsError = - Object.getPrototypeOf(getError(root.example.throwWeird)) === - Error.prototype; - const notClonableIsError = - Object.getPrototypeOf(getError(root.example.throwNotClonable)) === - Error.prototype; - const argumentConvertIsError = - Object.getPrototypeOf( - getError(() => - root.example.argumentConvert(Object(Symbol('test'))) - ) - ) === Error.prototype; - return [ - normalIsError, - weirdIsError, - notClonableIsError, - argumentConvertIsError - ]; + const normalIsError = Object.getPrototypeOf(getError(root.example.throwNormal)) === Error.prototype; + const weirdIsError = Object.getPrototypeOf(getError(root.example.throwWeird)) === Error.prototype; + const notClonableIsError = Object.getPrototypeOf(getError(root.example.throwNotClonable)) === Error.prototype; + const argumentConvertIsError = Object.getPrototypeOf(getError(() => root.example.argumentConvert(Object(Symbol('test'))))) === Error.prototype; + return [normalIsError, weirdIsError, notClonableIsError, argumentConvertIsError]; }); - expect(result).to.deep.equal( - [true, true, true, true], - 'should all be errors in the current context' - ); + expect(result).to.deep.equal([true, true, true, true], 'should all be errors in the current context'); }); it('should not leak prototypes', async () => { @@ -952,10 +847,8 @@ describe('contextBridge', () => { arg = o; }); const protoChecks = [ - ...Object.keys(example).map((key) => [key, String]), - ...Object.getOwnPropertySymbols(example.symbolKeyed).map( - (key) => [key, Symbol] - ), + ...Object.keys(example).map(key => [key, String]), + ...Object.getOwnPropertySymbols(example.symbolKeyed).map(key => [key, Symbol]), [example, Object], [example.number, Number], [example.string, String], @@ -1023,16 +916,11 @@ describe('contextBridge', () => { [example.getBlob(), Blob] ]; return { - protoMatches: protoChecks.map( - ([a, Constructor]) => - Object.getPrototypeOf(a) === Constructor.prototype - ) + protoMatches: protoChecks.map(([a, Constructor]) => Object.getPrototypeOf(a) === Constructor.prototype) }; }); // Every protomatch should be true - expect(result.protoMatches).to.deep.equal( - result.protoMatches.map(() => true) - ); + expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true)); }); it('should not leak prototypes when attaching directly to the global', async () => { @@ -1102,10 +990,8 @@ describe('contextBridge', () => { arg = o; }); const protoChecks = [ - ...Object.keys(cleanedRoot).map((key) => [key, String]), - ...Object.getOwnPropertySymbols(cleanedRoot.symbolKeyed).map( - (key) => [key, Symbol] - ), + ...Object.keys(cleanedRoot).map(key => [key, String]), + ...Object.getOwnPropertySymbols(cleanedRoot.symbolKeyed).map(key => [key, Symbol]), [cleanedRoot, Object], [cleanedRoot.number, Number], [cleanedRoot.string, String], @@ -1175,61 +1061,60 @@ describe('contextBridge', () => { [arg.key, String] ]; return { - protoMatches: protoChecks.map( - ([a, Constructor]) => - Object.getPrototypeOf(a) === Constructor.prototype - ) + protoMatches: protoChecks.map(([a, Constructor]) => Object.getPrototypeOf(a) === Constructor.prototype) }; }); // Every protomatch should be true - expect(result.protoMatches).to.deep.equal( - result.protoMatches.map(() => true) - ); + expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true)); }); }); - }); - describe('exposeInIsolatedWorld', () => { - const makeBindingWindow = async (bindingCreator: Function) => { - const preloadContent = `const renderer_1 = require('electron'); - ${ - useSandbox - ? '' - : `require('v8').setFlagsFromString('--expose_gc'); + + describe('exposeInIsolatedWorld', () => { + const makeBindingWindow = async (bindingCreator: Function) => { + const preloadContent = `const renderer_1 = require('electron'); + ${useSandbox ? '' : `require('v8').setFlagsFromString('--expose_gc'); const gc=require('vm').runInNewContext('gc'); renderer_1.webFrame.setIsolatedWorldInfo(1004, { name: "Isolated World" - }); - renderer_1.contextBridge.exposeInIsolatedWorld(1004,'GCRunner', { + }); + renderer_1.contextBridge.exposeInIsolatedWorld(1004, 'GCRunner', { run: () => gc() - });` - } + });`} (${bindingCreator.toString()})();`; - const tmpDir = await fs.mkdtemp( - path.resolve(os.tmpdir(), 'electron-spec-preload-') - ); - dir = tmpDir; - await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent); - w = new BrowserWindow({ - show: false, - webPreferences: { - contextIsolation: true, - nodeIntegration: true, - sandbox: useSandbox, - preload: path.resolve(tmpDir, 'preload.js'), - additionalArguments: [ - '--unsafely-expose-electron-internals-for-testing' - ] - } - }); - await w.loadURL( - `http://127.0.0.1:${(server.address() as AddressInfo).port}` - ); - }; - const callWithBindings = (fn: Function) => - w.webContents.executeJavaScriptInIsolatedWorld(1004, [{ - code: `(${fn.toString()})(window)` - }]); - describe(`with sandbox=${useSandbox}`, () => { + const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-spec-preload-')); + dir = tmpDir; + await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent); + w = new BrowserWindow({ + show: false, + webPreferences: { + contextIsolation: true, + nodeIntegration: true, + sandbox: useSandbox, + preload: path.resolve(tmpDir, 'preload.js'), + additionalArguments: ['--unsafely-expose-electron-internals-for-testing'] + } + }); + await w.loadURL(`http://127.0.0.1:${(server.address() as AddressInfo).port}`); + }; + + const callWithBindings = (fn: Function) => + w.webContents.executeJavaScriptInIsolatedWorld(1004, [{ code: `(${fn.toString()})(window)` }]); + + const getGCInfo = async (): Promise<{ + trackedValues: number; + }> => { + const [, info] = await emittedOnce(ipcMain, 'gc-info', () => w.webContents.send('get-gc-info')); + return info; + }; + + const forceGCOnWindow = async () => { + w.webContents.debugger.attach(); + await w.webContents.debugger.sendCommand('HeapProfiler.enable'); + await w.webContents.debugger.sendCommand('HeapProfiler.collectGarbage'); + await w.webContents.debugger.sendCommand('HeapProfiler.disable'); + w.webContents.debugger.detach(); + }; + it('should proxy numbers', async () => { await makeBindingWindow(() => { contextBridge.exposeInIsolatedWorld(1004, 'example', 123); @@ -1300,10 +1185,7 @@ describe('contextBridge', () => { it('should proxy arrays', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld(1004, 'example', [ - 123, - 'my-words' - ]); + contextBridge.exposeInIsolatedWorld(1004, 'example', [123, 'my-words']); }); const result = await callWithBindings((root: any) => { return [root.example, Array.isArray(root.example)]; @@ -1325,10 +1207,7 @@ describe('contextBridge', () => { it('should make arrays immutable', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld(1004, 'example', [ - 123, - 'my-words' - ]); + contextBridge.exposeInIsolatedWorld(1004, 'example', [123, 'my-words']); }); const immutable = await callWithBindings((root: any) => { try { @@ -1382,9 +1261,7 @@ describe('contextBridge', () => { it('should proxy promises and resolve with the correct value', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld( - 1004, - 'example', + contextBridge.exposeInIsolatedWorld(1004, 'example', Promise.resolve('i-resolved') ); }); @@ -1408,11 +1285,7 @@ describe('contextBridge', () => { it('should proxy promises and reject with the correct value', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld( - 1004, - 'example', - Promise.reject(new Error('i-rejected')) - ); + contextBridge.exposeInIsolatedWorld(1004, 'example', Promise.reject(new Error('i-rejected'))); }); const result = await callWithBindings(async (root: any) => { try { @@ -1422,9 +1295,7 @@ describe('contextBridge', () => { return err; } }); - expect(result) - .to.be.an.instanceOf(Error) - .with.property('message', 'i-rejected'); + expect(result).to.be.an.instanceOf(Error).with.property('message', 'i-rejected'); }); it('should proxy nested promises and reject with the correct value', async () => { @@ -1441,18 +1312,13 @@ describe('contextBridge', () => { return err; } }); - expect(result) - .to.be.an.instanceOf(Error) - .with.property('message', 'i-rejected'); + expect(result).to.be.an.instanceOf(Error).with.property('message', 'i-rejected'); }); it('should proxy promises and resolve with the correct value if it resolves later', async () => { await makeBindingWindow(() => { contextBridge.exposeInIsolatedWorld(1004, 'example', { - myPromise: () => - new Promise((resolve) => - setTimeout(() => resolve('delayed'), 20) - ) + myPromise: () => new Promise(resolve => setTimeout(() => resolve('delayed'), 20)) }); }); const result = await callWithBindings((root: any) => { @@ -1464,10 +1330,7 @@ describe('contextBridge', () => { it('should proxy nested promises correctly', async () => { await makeBindingWindow(() => { contextBridge.exposeInIsolatedWorld(1004, 'example', { - myPromise: () => - new Promise((resolve) => - setTimeout(() => resolve(Promise.resolve(123)), 20) - ) + myPromise: () => new Promise(resolve => setTimeout(() => resolve(Promise.resolve(123)), 20)) }); }); const result = await callWithBindings((root: any) => { @@ -1486,23 +1349,14 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings(async (root: any) => { - return [ - root.example.getNumber(), - root.example.getString(), - root.example.getBoolean(), - await root.example.getPromise() - ]; + return [root.example.getNumber(), root.example.getString(), root.example.getBoolean(), await root.example.getPromise()]; }); expect(result).to.deep.equal([123, 'help', false, 'promise']); }); it('should proxy functions', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld( - 1004, - 'example', - () => 'return-value' - ); + contextBridge.exposeInIsolatedWorld(1004, 'example', () => 'return-value'); }); const result = await callWithBindings(async (root: any) => { return root.example(); @@ -1512,11 +1366,7 @@ describe('contextBridge', () => { it('should not double-proxy functions when they are returned to their origin side of the bridge', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld( - 1004, - 'example', - (fn: any) => fn - ); + contextBridge.exposeInIsolatedWorld(1004, 'example', (fn: any) => fn); }); const result = await callWithBindings(async (root: any) => { const fn = () => null; @@ -1548,11 +1398,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings(async (root: any) => { - return [ - root.example.doThing(), - root.example.doThing(), - root.example.doThing() - ]; + return [root.example.doThing(), root.example.doThing(), root.example.doThing()]; }); expect(result).to.deep.equal([123, 123, 123]); }); @@ -1576,9 +1422,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings((root: any) => { - return root.example.getPromiseValue( - Promise.resolve('my-proxied-value') - ); + return root.example.getPromiseValue(Promise.resolve('my-proxied-value')); }); expect(result).to.equal('my-proxied-value'); }); @@ -1592,12 +1436,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings(async (root: any) => { - return [ - root.example[1], - root.example[2], - root.example[3], - Array.isArray(root.example) - ]; + return [root.example[1], root.example[2], root.example[3], Array.isArray(root.example)]; }); expect(result).to.deep.equal([123, 456, 789, false]); }); @@ -1650,10 +1489,7 @@ describe('contextBridge', () => { const result = await callWithBindings((root: any) => { return root.isSymbol(root.symbol); }); - expect(result).to.equal( - true, - 'symbols should be equal across contexts' - ); + expect(result).to.equal(true, 'symbols should be equal across contexts'); }); it('should proxy symbols such that symbol equality works', async () => { @@ -1667,10 +1503,7 @@ describe('contextBridge', () => { const result = await callWithBindings((root: any) => { return root.example.isSymbol(root.example.getSymbol()); }); - expect(result).to.equal( - true, - 'symbols should be equal across contexts' - ); + expect(result).to.equal(true, 'symbols should be equal across contexts'); }); it('should proxy symbols such that symbol key lookup works', async () => { @@ -1684,19 +1517,12 @@ describe('contextBridge', () => { const result = await callWithBindings((root: any) => { return root.example.getObject()[root.example.getSymbol()]; }); - expect(result).to.equal( - 123, - 'symbols key lookup should work across contexts' - ); + expect(result).to.equal(123, 'symbols key lookup should work across contexts'); }); it('should proxy typed arrays', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld( - 1004, - 'example', - new Uint8Array(100) - ); + contextBridge.exposeInIsolatedWorld(1004, 'example', new Uint8Array(100)); }); const result = await callWithBindings((root: any) => { return Object.getPrototypeOf(root.example) === Uint8Array.prototype; @@ -1739,11 +1565,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings((root: any) => { - return [ - root.example.o.value, - root.example.o.o.value, - root.example.o.o.o.value - ]; + return [root.example.o.value, root.example.o.o.value, root.example.o.o.o.value]; }); expect(result).to.deep.equal([135, 135, 135]); }); @@ -1755,11 +1577,7 @@ describe('contextBridge', () => { }); }); const result = await callWithBindings((root: any) => { - return [ - root.example.getElem().tagName, - root.example.getElem().constructor.name, - typeof root.example.getElem().querySelector - ]; + return [root.example.getElem().tagName, root.example.getElem().constructor.name, typeof root.example.getElem().querySelector]; }); expect(result).to.deep.equal(['BODY', 'HTMLBodyElement', 'function']); }); @@ -1769,11 +1587,7 @@ describe('contextBridge', () => { contextBridge.exposeInIsolatedWorld(1004, 'example', { getElemInfo: (fn: Function) => { const elem = fn(); - return [ - elem.tagName, - elem.constructor.name, - typeof elem.querySelector - ]; + return [elem.tagName, elem.constructor.name, typeof elem.querySelector]; } }); }); @@ -1815,12 +1629,7 @@ describe('contextBridge', () => { it('should release the global hold on methods sent across contexts', async () => { await makeBindingWindow(() => { const trackedValues: WeakRef[] = []; - require('electron').ipcRenderer.on('get-gc-info', (e) => - e.sender.send('gc-info', { - trackedValues: trackedValues.filter((value) => value.deref()) - .length - }) - ); + require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length })); contextBridge.exposeInIsolatedWorld(1004, 'example', { getFunction: () => () => 123, track: (value: object) => { @@ -1850,12 +1659,7 @@ describe('contextBridge', () => { it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => { await makeBindingWindow(() => { const trackedValues: WeakRef[] = []; - require('electron').ipcRenderer.on('get-gc-info', (e) => - e.sender.send('gc-info', { - trackedValues: trackedValues.filter((value) => value.deref()) - .length - }) - ); + require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length })); contextBridge.exposeInIsolatedWorld(1004, 'example', { getFunction: () => () => 123, track: (value: object) => { @@ -1864,10 +1668,7 @@ describe('contextBridge', () => { }); require('electron').ipcRenderer.send('window-ready-for-tasking'); }); - const loadPromise = emittedOnce( - ipcMain, - 'window-ready-for-tasking' - ); + const loadPromise = emittedOnce(ipcMain, 'window-ready-for-tasking'); expect((await getGCInfo()).trackedValues).to.equal(0); await callWithBindings((root: any) => { root.example.track(root.example.getFunction()); @@ -1909,10 +1710,9 @@ describe('contextBridge', () => { it('should work with complex nested methods and promises', async () => { await makeBindingWindow(() => { contextBridge.exposeInIsolatedWorld(1004, 'example', { - first: (second: Function) => - second((fourth: Function) => { - return fourth(); - }) + first: (second: Function) => second((fourth: Function) => { + return fourth(); + }) }); }); const result = await callWithBindings((root: any) => { @@ -1925,13 +1725,10 @@ describe('contextBridge', () => { it('should work with complex nested methods and promises attached directly to the global', async () => { await makeBindingWindow(() => { - contextBridge.exposeInIsolatedWorld( - 1004, - 'example', - (second: Function) => - second((fourth: Function) => { - return fourth(); - }) + contextBridge.exposeInIsolatedWorld(1004, 'example', + (second: Function) => second((fourth: Function) => { + return fourth(); + }) ); }); const result = await callWithBindings((root: any) => { @@ -1988,7 +1785,8 @@ describe('contextBridge', () => { throwNotClonable: () => { return Object(Symbol('foo')); }, - argumentConvert: () => {} + argumentConvert: () => { + } }); }); const result = await callWithBindings((root: any) => { @@ -2000,32 +1798,13 @@ describe('contextBridge', () => { } return null; }; - const normalIsError = - Object.getPrototypeOf(getError(root.example.throwNormal)) === - Error.prototype; - const weirdIsError = - Object.getPrototypeOf(getError(root.example.throwWeird)) === - Error.prototype; - const notClonableIsError = - Object.getPrototypeOf(getError(root.example.throwNotClonable)) === - Error.prototype; - const argumentConvertIsError = - Object.getPrototypeOf( - getError(() => - root.example.argumentConvert(Object(Symbol('test'))) - ) - ) === Error.prototype; - return [ - normalIsError, - weirdIsError, - notClonableIsError, - argumentConvertIsError - ]; + const normalIsError = Object.getPrototypeOf(getError(root.example.throwNormal)) === Error.prototype; + const weirdIsError = Object.getPrototypeOf(getError(root.example.throwWeird)) === Error.prototype; + const notClonableIsError = Object.getPrototypeOf(getError(root.example.throwNotClonable)) === Error.prototype; + const argumentConvertIsError = Object.getPrototypeOf(getError(() => root.example.argumentConvert(Object(Symbol('test'))))) === Error.prototype; + return [normalIsError, weirdIsError, notClonableIsError, argumentConvertIsError]; }); - expect(result).to.deep.equal( - [true, true, true, true], - 'should all be errors in the current context' - ); + expect(result).to.deep.equal([true, true, true, true], 'should all be errors in the current context'); }); it('should not leak prototypes', async () => { @@ -2078,10 +1857,8 @@ describe('contextBridge', () => { arg = o; }); const protoChecks = [ - ...Object.keys(example).map((key) => [key, String]), - ...Object.getOwnPropertySymbols(example.symbolKeyed).map( - (key) => [key, Symbol] - ), + ...Object.keys(example).map(key => [key, String]), + ...Object.getOwnPropertySymbols(example.symbolKeyed).map(key => [key, Symbol]), [example, Object], [example.number, Number], [example.string, String], @@ -2149,16 +1926,11 @@ describe('contextBridge', () => { [example.getBlob(), Blob] ]; return { - protoMatches: protoChecks.map( - ([a, Constructor]) => - Object.getPrototypeOf(a) === Constructor.prototype - ) + protoMatches: protoChecks.map(([a, Constructor]) => Object.getPrototypeOf(a) === Constructor.prototype) }; }); // Every protomatch should be true - expect(result.protoMatches).to.deep.equal( - result.protoMatches.map(() => true) - ); + expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true)); }); it('should not leak prototypes when attaching directly to the global', async () => { @@ -2228,10 +2000,8 @@ describe('contextBridge', () => { arg = o; }); const protoChecks = [ - ...Object.keys(cleanedRoot).map((key) => [key, String]), - ...Object.getOwnPropertySymbols(cleanedRoot.symbolKeyed).map( - (key) => [key, Symbol] - ), + ...Object.keys(cleanedRoot).map(key => [key, String]), + ...Object.getOwnPropertySymbols(cleanedRoot.symbolKeyed).map(key => [key, Symbol]), [cleanedRoot, Object], [cleanedRoot.number, Number], [cleanedRoot.string, String], @@ -2301,186 +2071,149 @@ describe('contextBridge', () => { [arg.key, String] ]; return { - protoMatches: protoChecks.map( - ([a, Constructor]) => - Object.getPrototypeOf(a) === Constructor.prototype - ) + protoMatches: protoChecks.map(([a, Constructor]) => Object.getPrototypeOf(a) === Constructor.prototype) }; }); // Every protomatch should be true - expect(result.protoMatches).to.deep.equal( - result.protoMatches.map(() => true) - ); + expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true)); }); }); - }); - describe('internalContextBridge', () => { - const makeBindingWindow = async (bindingCreator: Function) => { - const preloadContent = `const renderer_1 = require('electron'); - ${ - useSandbox - ? '' - : `require('v8').setFlagsFromString('--expose_gc'); + + describe('internalContextBridge', () => { + const makeBindingWindow = async (bindingCreator: Function) => { + const preloadContent = `const renderer_1 = require('electron'); + ${useSandbox ? '' : `require('v8').setFlagsFromString('--expose_gc'); const gc=require('vm').runInNewContext('gc'); renderer_1.contextBridge.exposeInMainWorld('GCRunner', { run: () => gc() - });` - } + });`} (${bindingCreator.toString()})();`; - const tmpDir = await fs.mkdtemp( - path.resolve(os.tmpdir(), 'electron-spec-preload-') - ); - dir = tmpDir; - await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent); - w = new BrowserWindow({ - show: false, - webPreferences: { - contextIsolation: true, - nodeIntegration: true, - sandbox: useSandbox, - preload: path.resolve(tmpDir, 'preload.js'), - additionalArguments: [ - '--unsafely-expose-electron-internals-for-testing' - ] - } - }); - await w.loadURL( - `http://127.0.0.1:${(server.address() as AddressInfo).port}` - ); - }; - const callWithBindings = (fn: Function) => - w.webContents.executeJavaScript(`(${fn.toString()})(window)`); - describe('overrideGlobalValueFromIsolatedWorld', () => { - it('should override top level properties', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalValueFromIsolatedWorld( - ['open'], - () => ({ you: 'are a wizard' }) - ); - }); - const result = await callWithBindings(async (root: any) => { - return root.open(); + const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-spec-preload-')); + dir = tmpDir; + await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent); + w = new BrowserWindow({ + show: false, + webPreferences: { + contextIsolation: true, + nodeIntegration: true, + sandbox: useSandbox, + preload: path.resolve(tmpDir, 'preload.js'), + additionalArguments: ['--unsafely-expose-electron-internals-for-testing'] + } }); - expect(result).to.deep.equal({ you: 'are a wizard' }); - }); + await w.loadURL(`http://127.0.0.1:${(server.address() as AddressInfo).port}`); + }; - it('should override deep properties', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalValueFromIsolatedWorld( - ['document', 'foo'], - () => 'I am foo' - ); - }); - const result = await callWithBindings(async (root: any) => { - return root.document.foo(); - }); - expect(result).to.equal('I am foo'); - }); - }); + const callWithBindings = (fn: Function) => + w.webContents.executeJavaScript(`(${fn.toString()})(window)`); - describe('overrideGlobalPropertyFromIsolatedWorld', () => { - it('should call the getter correctly', async () => { - await makeBindingWindow(() => { - let callCount = 0; - const getter = () => { - callCount++; - return true; - }; - contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld( - ['isFun'], - getter - ); - contextBridge.exposeInMainWorld('foo', { - callCount: () => callCount + describe('overrideGlobalValueFromIsolatedWorld', () => { + it('should override top level properties', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalValueFromIsolatedWorld(['open'], () => ({ you: 'are a wizard' })); + }); + const result = await callWithBindings(async (root: any) => { + return root.open(); }); + expect(result).to.deep.equal({ you: 'are a wizard' }); }); - const result = await callWithBindings(async (root: any) => { - return [root.isFun, root.foo.callCount()]; + + it('should override deep properties', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalValueFromIsolatedWorld(['document', 'foo'], () => 'I am foo'); + }); + const result = await callWithBindings(async (root: any) => { + return root.document.foo(); + }); + expect(result).to.equal('I am foo'); }); - expect(result[0]).to.equal(true); - expect(result[1]).to.equal(1); }); - it('should not make a setter if none is provided', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld( - ['isFun'], - () => true - ); - }); - const result = await callWithBindings(async (root: any) => { - root.isFun = 123; - return root.isFun; + describe('overrideGlobalPropertyFromIsolatedWorld', () => { + it('should call the getter correctly', async () => { + await makeBindingWindow(() => { + let callCount = 0; + const getter = () => { + callCount++; + return true; + }; + contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld(['isFun'], getter); + contextBridge.exposeInMainWorld('foo', { + callCount: () => callCount + }); + }); + const result = await callWithBindings(async (root: any) => { + return [root.isFun, root.foo.callCount()]; + }); + expect(result[0]).to.equal(true); + expect(result[1]).to.equal(1); }); - expect(result).to.equal(true); - }); - it('should call the setter correctly', async () => { - await makeBindingWindow(() => { - const callArgs: any[] = []; - const setter = (...args: any[]) => { - callArgs.push(args); - return true; - }; - contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld( - ['isFun'], - () => true, - setter - ); - contextBridge.exposeInMainWorld('foo', { - callArgs: () => callArgs + it('should not make a setter if none is provided', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld(['isFun'], () => true); }); + const result = await callWithBindings(async (root: any) => { + root.isFun = 123; + return root.isFun; + }); + expect(result).to.equal(true); }); - const result = await callWithBindings(async (root: any) => { - root.isFun = 123; - return root.foo.callArgs(); + + it('should call the setter correctly', async () => { + await makeBindingWindow(() => { + const callArgs: any[] = []; + const setter = (...args: any[]) => { + callArgs.push(args); + return true; + }; + contextBridge.internalContextBridge!.overrideGlobalPropertyFromIsolatedWorld(['isFun'], () => true, setter); + contextBridge.exposeInMainWorld('foo', { + callArgs: () => callArgs + }); + }); + const result = await callWithBindings(async (root: any) => { + root.isFun = 123; + return root.foo.callArgs(); + }); + expect(result).to.have.lengthOf(1); + expect(result[0]).to.have.lengthOf(1); + expect(result[0][0]).to.equal(123); }); - expect(result).to.have.lengthOf(1); - expect(result[0]).to.have.lengthOf(1); - expect(result[0][0]).to.equal(123); }); - }); - describe('overrideGlobalValueWithDynamicPropsFromIsolatedWorld', () => { - it('should not affect normal values', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld( - ['thing'], - { + describe('overrideGlobalValueWithDynamicPropsFromIsolatedWorld', () => { + it('should not affect normal values', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], { a: 123, b: () => 2, c: () => ({ d: 3 }) - } - ); - }); - const result = await callWithBindings(async (root: any) => { - return [root.thing.a, root.thing.b(), root.thing.c()]; + }); + }); + const result = await callWithBindings(async (root: any) => { + return [root.thing.a, root.thing.b(), root.thing.c()]; + }); + expect(result).to.deep.equal([123, 2, { d: 3 }]); }); - expect(result).to.deep.equal([123, 2, { d: 3 }]); - }); - it('should work with getters', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld( - ['thing'], - { + it('should work with getters', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], { get foo () { return 'hi there'; } - } - ); - }); - const result = await callWithBindings(async (root: any) => { - return root.thing.foo; + }); + }); + const result = await callWithBindings(async (root: any) => { + return root.thing.foo; + }); + expect(result).to.equal('hi there'); }); - expect(result).to.equal('hi there'); - }); - it('should work with nested getters', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld( - ['thing'], - { + it('should work with nested getters', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], { get foo () { return { get bar () { @@ -2488,43 +2221,37 @@ describe('contextBridge', () => { } }; } - } - ); - }); - const result = await callWithBindings(async (root: any) => { - return root.thing.foo.bar; + }); + }); + const result = await callWithBindings(async (root: any) => { + return root.thing.foo.bar; + }); + expect(result).to.equal('hi there'); }); - expect(result).to.equal('hi there'); - }); - it('should work with setters', async () => { - await makeBindingWindow(() => { - let a: any = null; - contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld( - ['thing'], - { + it('should work with setters', async () => { + await makeBindingWindow(() => { + let a: any = null; + contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], { get foo () { return a; }, set foo (arg: any) { a = arg + 1; } - } - ); - }); - const result = await callWithBindings(async (root: any) => { - root.thing.foo = 123; - return root.thing.foo; + }); + }); + const result = await callWithBindings(async (root: any) => { + root.thing.foo = 123; + return root.thing.foo; + }); + expect(result).to.equal(124); }); - expect(result).to.equal(124); - }); - it('should work with nested getter / setter combos', async () => { - await makeBindingWindow(() => { - let a: any = null; - contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld( - ['thing'], - { + it('should work with nested getter / setter combos', async () => { + await makeBindingWindow(() => { + let a: any = null; + contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], { get thingy () { return { get foo () { @@ -2535,33 +2262,30 @@ describe('contextBridge', () => { } }; } - } - ); - }); - const result = await callWithBindings(async (root: any) => { - root.thing.thingy.foo = 123; - return root.thing.thingy.foo; + }); + }); + const result = await callWithBindings(async (root: any) => { + root.thing.thingy.foo = 123; + return root.thing.thingy.foo; + }); + expect(result).to.equal(124); }); - expect(result).to.equal(124); - }); - it('should work with deep properties', async () => { - await makeBindingWindow(() => { - contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld( - ['thing'], - { + it('should work with deep properties', async () => { + await makeBindingWindow(() => { + contextBridge.internalContextBridge!.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['thing'], { a: () => ({ get foo () { return 'still here'; } }) - } - ); - }); - const result = await callWithBindings(async (root: any) => { - return root.thing.a().foo; + }); + }); + const result = await callWithBindings(async (root: any) => { + return root.thing.a().foo; + }); + expect(result).to.equal('still here'); }); - expect(result).to.equal('still here'); }); }); }); @@ -2574,14 +2298,10 @@ describe('contextBridge', () => { 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 - ]); + const appProcess = cp.spawn(process.execPath, ['--enable-logging', '--enable-features=ContextBridgeMutability', appPath]); let output = ''; - appProcess.stdout.on('data', (data) => { + appProcess.stdout.on('data', data => { output += data; }); await emittedOnce(appProcess, 'exit'); @@ -2593,13 +2313,10 @@ describe('ContextBridgeMutability', () => { 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 - ]); + const appProcess = cp.spawn(process.execPath, ['--enable-logging', appPath]); let output = ''; - appProcess.stdout.on('data', (data) => { + appProcess.stdout.on('data', data => { output += data; }); await emittedOnce(appProcess, 'exit');