feat: add preload-error
event to webContents
(#16411)
This commit is contained in:
parent
98d0daa2fd
commit
7d4a1223fd
7 changed files with 112 additions and 7 deletions
|
@ -663,6 +663,16 @@ Returns:
|
||||||
Emitted when the associated window logs a console message. Will not be emitted
|
Emitted when the associated window logs a console message. Will not be emitted
|
||||||
for windows with *offscreen rendering* enabled.
|
for windows with *offscreen rendering* enabled.
|
||||||
|
|
||||||
|
#### Event: 'preload-error'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
* `event` Event
|
||||||
|
* `preloadPath` String
|
||||||
|
* `error` Error
|
||||||
|
|
||||||
|
Emitted when the preload script `preloadPath` throws an unhandled exception `error`.
|
||||||
|
|
||||||
#### Event: 'desktop-capturer-get-sources'
|
#### Event: 'desktop-capturer-get-sources'
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
|
@ -542,10 +542,11 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
|
||||||
try {
|
try {
|
||||||
preloadSrc = fs.readFileSync(preloadPath).toString()
|
preloadSrc = fs.readFileSync(preloadPath).toString()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack }
|
preloadError = errorUtils.serialize(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event.returnValue = {
|
event.returnValue = {
|
||||||
|
preloadPath,
|
||||||
preloadSrc,
|
preloadSrc,
|
||||||
preloadError,
|
preloadError,
|
||||||
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
|
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
|
||||||
|
@ -560,3 +561,7 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
|
||||||
|
event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error))
|
||||||
|
})
|
||||||
|
|
|
@ -147,13 +147,17 @@ if (nodeIntegration) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorUtils = require('@electron/internal/common/error-utils')
|
||||||
|
|
||||||
// Load the preload scripts.
|
// Load the preload scripts.
|
||||||
for (const preloadScript of preloadScripts) {
|
for (const preloadScript of preloadScripts) {
|
||||||
try {
|
try {
|
||||||
require(preloadScript)
|
require(preloadScript)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Unable to load preload script: ' + preloadScript)
|
console.error(`Unable to load preload script: ${preloadScript}`)
|
||||||
console.error(error.stack || error.message)
|
console.error(`${error}`)
|
||||||
|
|
||||||
|
ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, errorUtils.serialize(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ Object.setPrototypeOf(process, EventEmitter.prototype)
|
||||||
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
|
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
preloadSrc, preloadError, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
|
preloadPath, preloadSrc, preloadError, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
|
||||||
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
|
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
|
||||||
|
|
||||||
process.isRemoteModuleEnabled = isRemoteModuleEnabled
|
process.isRemoteModuleEnabled = isRemoteModuleEnabled
|
||||||
|
@ -132,6 +132,8 @@ if (!process.guestInstanceId && isWebViewTagEnabled) {
|
||||||
setupWebView(v8Util, webViewImpl)
|
setupWebView(v8Util, webViewImpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorUtils = require('@electron/internal/common/error-utils')
|
||||||
|
|
||||||
// Wrap the script into a function executed in global scope. It won't have
|
// Wrap the script into a function executed in global scope. It won't have
|
||||||
// access to the current scope, so we'll expose a few objects as arguments:
|
// access to the current scope, so we'll expose a few objects as arguments:
|
||||||
//
|
//
|
||||||
|
@ -151,7 +153,7 @@ if (!process.guestInstanceId && isWebViewTagEnabled) {
|
||||||
// and any `require('electron')` calls in `preload.js` will work as expected
|
// and any `require('electron')` calls in `preload.js` will work as expected
|
||||||
// since browserify won't try to include `electron` in the bundle, falling back
|
// since browserify won't try to include `electron` in the bundle, falling back
|
||||||
// to the `preloadRequire` function above.
|
// to the `preloadRequire` function above.
|
||||||
if (preloadSrc) {
|
function runPreloadScript (preloadSrc) {
|
||||||
const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate) {
|
const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate) {
|
||||||
${preloadSrc}
|
${preloadSrc}
|
||||||
})`
|
})`
|
||||||
|
@ -159,9 +161,21 @@ if (preloadSrc) {
|
||||||
// eval in window scope
|
// eval in window scope
|
||||||
const preloadFn = binding.createPreloadScript(preloadWrapperSrc)
|
const preloadFn = binding.createPreloadScript(preloadWrapperSrc)
|
||||||
const { setImmediate, clearImmediate } = require('timers')
|
const { setImmediate, clearImmediate } = require('timers')
|
||||||
|
|
||||||
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
|
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
|
||||||
} else if (preloadError) {
|
}
|
||||||
console.error(preloadError.stack)
|
|
||||||
|
try {
|
||||||
|
if (preloadSrc) {
|
||||||
|
runPreloadScript(preloadSrc)
|
||||||
|
} else if (preloadError) {
|
||||||
|
throw errorUtils.deserialize(preloadError)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Unable to load preload script: ${preloadPath}`)
|
||||||
|
console.error(`${error}`)
|
||||||
|
|
||||||
|
ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, errorUtils.serialize(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn about security issues
|
// Warn about security issues
|
||||||
|
|
|
@ -1050,6 +1050,75 @@ describe('webContents module', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('preload-error event', () => {
|
||||||
|
const generateSpecs = (description, sandbox) => {
|
||||||
|
describe(description, () => {
|
||||||
|
it('is triggered when unhandled exception is thrown', async () => {
|
||||||
|
const preload = path.join(fixtures, 'module', 'preload-error-exception.js')
|
||||||
|
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox,
|
||||||
|
preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const promise = emittedOnce(w.webContents, 'preload-error')
|
||||||
|
w.loadURL('about:blank')
|
||||||
|
|
||||||
|
const [, preloadPath, error] = await promise
|
||||||
|
expect(preloadPath).to.equal(preload)
|
||||||
|
expect(error.message).to.equal('Hello World!')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is triggered on syntax errors', async () => {
|
||||||
|
const preload = path.join(fixtures, 'module', 'preload-error-syntax.js')
|
||||||
|
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox,
|
||||||
|
preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const promise = emittedOnce(w.webContents, 'preload-error')
|
||||||
|
w.loadURL('about:blank')
|
||||||
|
|
||||||
|
const [, preloadPath, error] = await promise
|
||||||
|
expect(preloadPath).to.equal(preload)
|
||||||
|
expect(error.message).to.equal('foobar is not defined')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is triggered when preload script loading fails', async () => {
|
||||||
|
const preload = path.join(fixtures, 'module', 'preload-invalid.js')
|
||||||
|
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox,
|
||||||
|
preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const promise = emittedOnce(w.webContents, 'preload-error')
|
||||||
|
w.loadURL('about:blank')
|
||||||
|
|
||||||
|
const [, preloadPath, error] = await promise
|
||||||
|
expect(preloadPath).to.equal(preload)
|
||||||
|
expect(error.message).to.contain('preload-invalid.js')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSpecs('without sandbox', false)
|
||||||
|
generateSpecs('with sandbox', true)
|
||||||
|
})
|
||||||
|
|
||||||
describe('takeHeapSnapshot()', () => {
|
describe('takeHeapSnapshot()', () => {
|
||||||
it('works with sandboxed renderers', async () => {
|
it('works with sandboxed renderers', async () => {
|
||||||
w.destroy()
|
w.destroy()
|
||||||
|
|
1
spec/fixtures/module/preload-error-exception.js
vendored
Normal file
1
spec/fixtures/module/preload-error-exception.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
throw new Error('Hello World!')
|
2
spec/fixtures/module/preload-error-syntax.js
vendored
Normal file
2
spec/fixtures/module/preload-error-syntax.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// eslint-disable-next-line
|
||||||
|
foobar
|
Loading…
Add table
Add a link
Reference in a new issue