Make executeJavaScript return a Promise so that caught errors can be sent to the caller

This commit is contained in:
Samuel Attard 2016-10-11 16:47:09 +11:00 committed by Kevin Sawicki
parent 75b010ce63
commit 857e1da6a3
5 changed files with 59 additions and 7 deletions

View file

@ -620,12 +620,26 @@ Injects CSS into the current web page.
* `callback` Function (optional) - Called after script has been executed. * `callback` Function (optional) - Called after script has been executed.
* `result` Any * `result` Any
Returns `Promise` - A promise that resolves with the result of the executed code
or is rejected if the result of the code is a rejected promise
Evaluates `code` in page. Evaluates `code` in page.
In the browser window some HTML APIs like `requestFullScreen` can only be In the browser window some HTML APIs like `requestFullScreen` can only be
invoked by a gesture from the user. Setting `userGesture` to `true` will remove invoked by a gesture from the user. Setting `userGesture` to `true` will remove
this limitation. this limitation.
If the result of the executed code is a promise the callback result will be the
resolved value of the promise. We recommend that you use the returned Promise
to handle code that results in a Promise.
```js
contents.executeJavaScript('fetch("https://jsonplaceholder.typicode.com/users/1").then(resp => resp.json())', true)
.then((result) => {
console.log(result) // Will be the JSON object from the fetch call
})
```
#### `contents.setAudioMuted(muted)` #### `contents.setAudioMuted(muted)`
* `muted` Boolean * `muted` Boolean

View file

@ -110,9 +110,15 @@ const webFrameMethodsWithResult = [
] ]
const asyncWebFrameMethods = function (requestId, method, callback, ...args) { const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args) return new Promise((resolve, reject) => {
ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) { this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
if (callback) callback(result) ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) {
if (callback) callback(result)
resolve(result)
})
ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_ERROR_${requestId}`, (event, error) => {
reject(error)
})
}) })
} }
@ -146,10 +152,12 @@ WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callba
hasUserGesture = false hasUserGesture = false
} }
if (this.getURL() && !this.isLoadingMainFrame()) { if (this.getURL() && !this.isLoadingMainFrame()) {
asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
} else { } else {
this.once('did-finish-load', () => { return new Promise((resolve, reject) => {
asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) this.once('did-finish-load', () => {
asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture).then(resolve).catch(reject)
})
}) })
} }
} }

View file

@ -44,7 +44,7 @@ electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (ev
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedResult) event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedResult)
}) })
.catch((resolvedError) => { .catch((resolvedError) => {
console.error(`An async web frame method (${method}) returned a promise that threw an error: `, resolvedError) event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_ERROR_${requestId}`, resolvedError)
}) })
} }
args.push(responseCallback) args.push(responseCallback)

View file

@ -1462,8 +1462,10 @@ describe('browser-window module', function () {
describe('window.webContents.executeJavaScript', function () { describe('window.webContents.executeJavaScript', function () {
var expected = 'hello, world!' var expected = 'hello, world!'
var expectedErrorMsg = 'woops!'
var code = '(() => "' + expected + '")()' var code = '(() => "' + expected + '")()'
var asyncCode = '(() => new Promise(r => setTimeout(() => r("' + expected + '"), 500)))()' var asyncCode = '(() => new Promise(r => setTimeout(() => r("' + expected + '"), 500)))()'
var badAsyncCode = '(() => new Promise((r, e) => setTimeout(() => e("' + expectedErrorMsg + '"), 500)))()'
it('doesnt throw when no calback is provided', function () { it('doesnt throw when no calback is provided', function () {
const result = ipcRenderer.sendSync('executeJavaScript', code, false) const result = ipcRenderer.sendSync('executeJavaScript', code, false)
@ -1486,6 +1488,30 @@ describe('browser-window module', function () {
}) })
}) })
it('resolves the returned promise with the result', function (done) {
ipcRenderer.send('executeJavaScript', code, true)
ipcRenderer.once('executeJavaScript-promise-response', function (event, result) {
assert.equal(result, expected)
done()
})
})
it('resolves the returned promise with the result if the code returns an asyncronous promise', function (done) {
ipcRenderer.send('executeJavaScript', asyncCode, true)
ipcRenderer.once('executeJavaScript-promise-response', function (event, result) {
assert.equal(result, expected)
done()
})
})
it('rejects the returned promise if an async error is thrown', function (done) {
ipcRenderer.send('executeJavaScript', badAsyncCode, true)
ipcRenderer.once('executeJavaScript-promise-error', function (event, error) {
assert.equal(error, expectedErrorMsg)
done()
})
})
it('works after page load and during subframe load', function (done) { it('works after page load and during subframe load', function (done) {
w.webContents.once('did-finish-load', function () { w.webContents.once('did-finish-load', function () {
// initiate a sub-frame load, then try and execute script during it // initiate a sub-frame load, then try and execute script during it

View file

@ -173,6 +173,10 @@ app.on('ready', function () {
if (hasCallback) { if (hasCallback) {
window.webContents.executeJavaScript(code, (result) => { window.webContents.executeJavaScript(code, (result) => {
window.webContents.send('executeJavaScript-response', result) window.webContents.send('executeJavaScript-response', result)
}).then((result) => {
window.webContents.send('executeJavaScript-promise-response', result)
}).catch((err) => {
window.webContents.send('executeJavaScript-promise-error', err)
}) })
} else { } else {
window.webContents.executeJavaScript(code) window.webContents.executeJavaScript(code)