diff --git a/experimental/puppeteer-firefox/lib/ExecutionContext.js b/experimental/puppeteer-firefox/lib/ExecutionContext.js index e00ee1ec2dc..828924c5122 100644 --- a/experimental/puppeteer-firefox/lib/ExecutionContext.js +++ b/experimental/puppeteer-firefox/lib/ExecutionContext.js @@ -21,9 +21,34 @@ class ExecutionContext { }); return createHandle(this, payload.result, payload.exceptionDetails); } + if (typeof pageFunction !== 'function') + throw new Error(`Expected to get |string| or |function| as the first argument, but got "${pageFunction}" instead.`); + + let functionText = pageFunction.toString(); + try { + new Function('(' + functionText + ')'); + } catch (e1) { + // This means we might have a function shorthand. Try another + // time prefixing 'function '. + if (functionText.startsWith('async ')) + functionText = 'async function ' + functionText.substring('async '.length); + else + functionText = 'function ' + functionText; + try { + new Function('(' + functionText + ')'); + } catch (e2) { + // We tried hard to serialize, but there's a weird beast here. + throw new Error('Passed function is not well-serializable!'); + } + } args = args.map(arg => { - if (arg instanceof JSHandle) + if (arg instanceof JSHandle) { + if (arg._context !== this) + throw new Error('JSHandles can be evaluated only in the context they were created!'); + if (arg._disposed) + throw new Error('JSHandle is disposed!'); return arg._protocolValue; + } if (Object.is(arg, Infinity)) return {unserializableValue: 'Infinity'}; if (Object.is(arg, -Infinity)) @@ -35,7 +60,7 @@ class ExecutionContext { return {value: arg}; }); const payload = await this._session.send('Page.evaluate', { - functionText: pageFunction.toString(), + functionText, args, executionContextId: this._executionContextId }); diff --git a/experimental/puppeteer-firefox/lib/JSHandle.js b/experimental/puppeteer-firefox/lib/JSHandle.js index fb735eff9f2..a0aa95ff27c 100644 --- a/experimental/puppeteer-firefox/lib/JSHandle.js +++ b/experimental/puppeteer-firefox/lib/JSHandle.js @@ -13,6 +13,7 @@ class JSHandle { this._objectId = payload.objectId; this._type = payload.type; this._subtype = payload.subtype; + this._disposed = false; this._protocolValue = { unserializableValue: payload.unserializableValue, value: payload.value, @@ -102,6 +103,7 @@ class JSHandle { async dispose() { if (!this._objectId) return; + this._disposed = true; await this._session.send('Page.disposeObject', { executionContextId: this._executionContextId, objectId: this._objectId, diff --git a/lib/ExecutionContext.js b/lib/ExecutionContext.js index 2a6c9ac2274..8372fbae017 100644 --- a/lib/ExecutionContext.js +++ b/lib/ExecutionContext.js @@ -82,7 +82,7 @@ class ExecutionContext { } if (typeof pageFunction !== 'function') - throw new Error('The following is not a function: ' + pageFunction); + throw new Error(`Expected to get |string| or |function| as the first argument, but got "${pageFunction}" instead.`); let functionText = pageFunction.toString(); try { diff --git a/test/evaluation.spec.js b/test/evaluation.spec.js index bc1d912f4a7..d03b4f5d205 100644 --- a/test/evaluation.spec.js +++ b/test/evaluation.spec.js @@ -68,7 +68,7 @@ module.exports.addTests = function({testRunner, expect}) { it_fails_ffox('should return undefined for objects with symbols', async({page, server}) => { expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined); }); - (asyncawait ? it_fails_ffox : xit)('should work with function shorthands', async({page, server}) => { + (asyncawait ? it : xit)('should work with function shorthands', async({page, server}) => { // trick node6 transpiler to not touch our object. // TODO(lushnikov): remove eval once Node6 is dropped. const a = eval(`({ @@ -188,7 +188,7 @@ module.exports.addTests = function({testRunner, expect}) { const text = await page.evaluate(e => e.textContent, element); expect(text).toBe('42'); }); - it_fails_ffox('should throw if underlying element was disposed', async({page, server}) => { + it('should throw if underlying element was disposed', async({page, server}) => { await page.setContent('
39
'); const element = await page.$('section'); expect(element).toBeTruthy(); @@ -197,7 +197,7 @@ module.exports.addTests = function({testRunner, expect}) { await page.evaluate(e => e.textContent, element).catch(e => error = e); expect(error.message).toContain('JSHandle is disposed'); }); - it_fails_ffox('should throw if elementHandles are from other frames', async({page, server}) => { + it('should throw if elementHandles are from other frames', async({page, server}) => { await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const bodyHandle = await page.frames()[1].$('body'); let error = null;