diff --git a/lib/Page.js b/lib/Page.js index c3763aee..c21cab76 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -16,7 +16,6 @@ var fs = require('fs'); var EventEmitter = require('events'); -var helpers = require('./helpers'); var mime = require('mime'); var Request = require('./Request'); var Dialog = require('./Dialog'); @@ -34,7 +33,7 @@ class Page extends EventEmitter { client.send('Runtime.enable', {}), client.send('Security.enable', {}), ]); - var expression = helpers.evaluationString(() => window.devicePixelRatio, []); + var expression = Page._evaluationString(() => window.devicePixelRatio); var {result:{value: screenDPI}} = await client.send('Runtime.evaluate', { expression, returnByValue: true }); var frameManager = await FrameManager.create(client); var page = new Page(client, frameManager, screenDPI); @@ -127,8 +126,11 @@ class Page extends EventEmitter { * @return {!Promise} */ async injectFile(filePath) { - var expression = fs.readFileSync(filePath, 'utf8'); - await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); + var callback; + var promise = new Promise(fulfill => callback = fulfill); + var expression = fs.readFile(filePath, 'utf8', (err, data) => callback({err, data})); + await promise; + return this._client.send('Runtime.evaluate', { expression, returnByValue: true }); } /** @@ -140,7 +142,7 @@ class Page extends EventEmitter { throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`); this._inPageCallbacks[name] = callback; - var expression = helpers.evaluationString(inPageCallback, [name]); + var expression = Page._evaluationString(inPageCallback, name); await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression }); await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); @@ -209,7 +211,7 @@ class Page extends EventEmitter { if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:InPageCallback') { var {name, seq, args} = JSON.parse(event.args[1].value); var result = await this._inPageCallbacks[name](...args); - var expression = helpers.evaluationString(deliverResult, [name, seq, result]); + var expression = Page._evaluationString(deliverResult, name, seq, result); this._client.send('Runtime.evaluate', { expression }); function deliverResult(name, seq, result) { @@ -241,9 +243,7 @@ class Page extends EventEmitter { * @return {!Promise} */ async url() { - return this.evaluate(function() { - return window.location.href; - }); + return this.evaluate(() => window.location.href); } /** @@ -251,11 +251,11 @@ class Page extends EventEmitter { * @return {!Promise} */ async setContent(html) { - var resourceTree = await this._client.send('Page.getResourceTree', {}); - await this._client.send('Page.setDocumentContent', { - frameId: resourceTree.frameTree.frame.id, - html: html - }); + this.evaluate(() => { + document.open(); + document.write(html); + document.close(); + }, html); } /** @@ -280,7 +280,7 @@ class Page extends EventEmitter { * @return {!Promise} */ async setViewportSize(size) { - this._size = size; + this._viewportSize = size; var width = size.width; var height = size.height; var zoom = this._screenDPI; @@ -304,7 +304,7 @@ class Page extends EventEmitter { * @return {!{width: number, height: number}} */ viewportSize() { - return this._size; + return this._viewportSize; } /** @@ -313,34 +313,14 @@ class Page extends EventEmitter { * @return {!Promise<(!Object|undefined)>} */ async evaluate(fun, ...args) { - var code = helpers.evaluationString(fun, args, false /* wrapInPromise */); - var response = await this._client.send('Runtime.evaluate', { - expression: code - }); - if (response.exceptionDetails) { - var message = await this._getExceptionMessage(response.exceptionDetails); + var syncExpression = Page._evaluationString(fun, ...args); + var expression = `Promise.resolve(${syncExpression})`; + var { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true }); + if (exceptionDetails) { + var message = await this._getExceptionMessage(exceptionDetails); throw new Error('Evaluation failed: ' + message); } - - var remoteObject = response.result; - if (remoteObject.type !== 'object') - return remoteObject.value; - var isPromise = remoteObject.type === 'object' && remoteObject.subtype === 'promise'; - var response = await this._client.send('Runtime.callFunctionOn', { - objectId: remoteObject.objectId, - functionDeclaration: 'function() { return this; }', - returnByValue: true, - awaitPromise: isPromise - }); - await this._client.send('Runtime.releaseObject', { - objectId: remoteObject.objectId - }); - if (response.exceptionDetails) { - var message = await this._getExceptionMessage(response.exceptionDetails); - throw new Error('Evaluation failed with ' + message); - } - - return response.result.value; + return remoteObject.value; } /** @@ -360,6 +340,7 @@ class Page extends EventEmitter { } else { message = exceptionDetails.text; } + if (exceptionDetails.stackTrace) { for (var callframe of exceptionDetails.stackTrace.callFrames) { var location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; @@ -376,10 +357,17 @@ class Page extends EventEmitter { * @return {!Promise} */ async evaluateOnInitialized(fun, ...args) { - var code = helpers.evaluationString(fun, args, false /* wrapInPromise */); - await this._client.send('Page.addScriptToEvaluateOnLoad', { - scriptSource: code - }); + var scriptSource = Page._evaluationString(fun, ...args); + await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource }); + } + + /** + * @param {function()} fun + * @param {!Array<*>} args + * @return {string} + */ + static _evaluationString(fun, ...args) { + return `(${fun})(${args.map(x => JSON.stringify(x)).join(',')})`; } /** @@ -524,18 +512,14 @@ class Page extends EventEmitter { * @return {!Promise} */ async plainText() { - return this.evaluate(function() { - return document.body.innerText; - }); + return this.evaluate(() => document.body.innerText); } /** * @return {!Promise} */ async title() { - return this.evaluate(function() { - return document.title; - }); + return this.evaluate(() => document.title); } /** diff --git a/lib/helpers.js b/lib/helpers.js deleted file mode 100644 index af0ab1c7..00000000 --- a/lib/helpers.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module.exports = { - - /** - * @param {function()} fun - * @param {!Array<*>} args - * @param {boolean=} wrapInPromise - * @param {string=} sourceURL - * @return {string} - */ - evaluationString: function(fun, args, wrapInPromise, sourceURL) { - var argsString = args.map(x => JSON.stringify(x)).join(','); - var code = `(${fun.toString()})(${argsString})`; - if (wrapInPromise) - code = `Promise.resolve(${code})`; - if (sourceURL) - code += `\n//# sourceURL=${sourceURL}`; - return code; - } -};