Skip to content

Commit

Permalink
feat: add preload-error event for webContents
Browse files Browse the repository at this point in the history
  • Loading branch information
miniak committed Jan 15, 2019
1 parent 8e2ab8b commit 1e6454d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 7 deletions.
10 changes: 10 additions & 0 deletions docs/api/web-contents.md
Expand Up @@ -663,6 +663,16 @@ Returns:
Emitted when the associated window logs a console message. Will not be emitted
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'

Returns:
Expand Down
7 changes: 6 additions & 1 deletion lib/browser/rpc-server.js
Expand Up @@ -542,10 +542,11 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
try {
preloadSrc = fs.readFileSync(preloadPath).toString()
} catch (err) {
preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack }
preloadError = errorUtils.serialize(err)
}
}
event.returnValue = {
preloadPath,
preloadSrc,
preloadError,
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
Expand All @@ -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))
})
8 changes: 6 additions & 2 deletions lib/renderer/init.js
Expand Up @@ -147,13 +147,17 @@ if (nodeIntegration) {
})
}

const errorUtils = require('@electron/internal/common/error-utils')

// Load the preload scripts.
for (const preloadScript of preloadScripts) {
try {
require(preloadScript)
} catch (error) {
console.error('Unable to load preload script: ' + preloadScript)
console.error(error.stack || error.message)
console.error(`Unable to load preload script: ${preloadScript}`)
console.error(`${error}`)

ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, errorUtils.serialize(error))
}
}

Expand Down
22 changes: 18 additions & 4 deletions lib/sandboxed_renderer/init.js
Expand Up @@ -29,7 +29,7 @@ Object.setPrototypeOf(process, EventEmitter.prototype)
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')

const {
preloadSrc, preloadError, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
preloadPath, preloadSrc, preloadError, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')

process.isRemoteModuleEnabled = isRemoteModuleEnabled
Expand Down Expand Up @@ -132,6 +132,8 @@ if (!process.guestInstanceId && isWebViewTagEnabled) {
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
// access to the current scope, so we'll expose a few objects as arguments:
//
Expand All @@ -151,17 +153,29 @@ if (!process.guestInstanceId && isWebViewTagEnabled) {
// 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
// to the `preloadRequire` function above.
if (preloadSrc) {
function runPreloadScript (preloadSrc) {
const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate) {
${preloadSrc}
})`

// eval in window scope
const preloadFn = binding.createPreloadScript(preloadWrapperSrc)
const { setImmediate, clearImmediate } = require('timers')

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
Expand Down
69 changes: 69 additions & 0 deletions spec/api-web-contents-spec.js
Expand Up @@ -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()', () => {
it('works with sandboxed renderers', async () => {
w.destroy()
Expand Down
1 change: 1 addition & 0 deletions spec/fixtures/module/preload-error-exception.js
@@ -0,0 +1 @@
throw new Error('Hello World!')
2 changes: 2 additions & 0 deletions spec/fixtures/module/preload-error-syntax.js
@@ -0,0 +1,2 @@
// eslint-disable-next-line
foobar

0 comments on commit 1e6454d

Please sign in to comment.