diff --git a/.eslintrc.js b/.eslintrc.js index 7014f72191a..6dc44d1db07 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -104,7 +104,7 @@ module.exports = { * Disabled, aspirational rules */ - "indent": [0, 4, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }], + "indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }], // brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions "brace-style": [0, "allman", { "allowSingleLine": true }], diff --git a/index.js b/index.js index 51451f9ae28..f9e23c37c19 100644 --- a/index.js +++ b/index.js @@ -15,5 +15,5 @@ */ module.exports = { - Browser: require('./lib/Browser') + Browser: require('./lib/Browser') }; diff --git a/install.js b/install.js index 588a393faca..e1ee4f319f3 100644 --- a/install.js +++ b/install.js @@ -20,28 +20,28 @@ var ProgressBar = require('progress'); // Do nothing if the revision is already downloaded. if (Downloader.revisionInfo(Downloader.currentPlatform(), revision)) - return; + return; Downloader.downloadRevision(Downloader.currentPlatform(), revision, onProgress) .catch(error => { - console.error('Download failed: ' + error.message); + console.error('Download failed: ' + error.message); }); var progressBar = null; function onProgress(bytesTotal, delta) { - if (!progressBar) { - progressBar = new ProgressBar(`Downloading Chromium - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, { - complete: '=', - incomplete: ' ', - width: 20, - total: bytesTotal, - }); - } - progressBar.tick(delta); + if (!progressBar) { + progressBar = new ProgressBar(`Downloading Chromium - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, { + complete: '=', + incomplete: ' ', + width: 20, + total: bytesTotal, + }); + } + progressBar.tick(delta); } function toMegabytes(bytes) { - var mb = bytes / 1024 / 1024; - return (Math.round(mb * 10) / 10) + ' Mb'; + var mb = bytes / 1024 / 1024; + return (Math.round(mb * 10) / 10) + ' Mb'; } diff --git a/lib/Browser.js b/lib/Browser.js index e6452462706..9812664c53d 100644 --- a/lib/Browser.js +++ b/lib/Browser.js @@ -26,125 +26,125 @@ var CHROME_PROFILE_PATH = path.resolve(__dirname, '..', '.dev_profile'); var browserId = 0; var DEFAULT_ARGS = [ - '--disable-background-timer-throttling', - '--no-first-run', + '--disable-background-timer-throttling', + '--no-first-run', ]; class Browser { - /** + /** * @param {(!Object|undefined)} options */ - constructor(options) { - options = options || {}; - ++browserId; - this._userDataDir = CHROME_PROFILE_PATH + browserId; - this._remoteDebuggingPort = 9227; - if (typeof options.remoteDebuggingPort === 'number') - this._remoteDebuggingPort = options.remoteDebuggingPort; - this._chromeArguments = DEFAULT_ARGS.concat([ - `--user-data-dir=${this._userDataDir}`, - `--remote-debugging-port=${this._remoteDebuggingPort}`, - ]); - if (typeof options.headless !== 'boolean' || options.headless) { - this._chromeArguments.push(...[ - `--headless`, - `--disable-gpu`, - ]); - } - if (typeof options.executablePath === 'string') { - this._chromeExecutable = options.executablePath; - } else { - var chromiumRevision = require('../package.json').puppeteer.chromium_revision; - var revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), chromiumRevision); - console.assert(revisionInfo, 'Chromium revision is not downloaded. Run npm install'); - this._chromeExecutable = revisionInfo.executablePath; - } - if (Array.isArray(options.args)) - this._chromeArguments.push(...options.args); - this._terminated = false; - this._chromeProcess = null; + constructor(options) { + options = options || {}; + ++browserId; + this._userDataDir = CHROME_PROFILE_PATH + browserId; + this._remoteDebuggingPort = 9227; + if (typeof options.remoteDebuggingPort === 'number') + this._remoteDebuggingPort = options.remoteDebuggingPort; + this._chromeArguments = DEFAULT_ARGS.concat([ + `--user-data-dir=${this._userDataDir}`, + `--remote-debugging-port=${this._remoteDebuggingPort}`, + ]); + if (typeof options.headless !== 'boolean' || options.headless) { + this._chromeArguments.push(...[ + `--headless`, + `--disable-gpu`, + ]); } + if (typeof options.executablePath === 'string') { + this._chromeExecutable = options.executablePath; + } else { + var chromiumRevision = require('../package.json').puppeteer.chromium_revision; + var revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), chromiumRevision); + console.assert(revisionInfo, 'Chromium revision is not downloaded. Run npm install'); + this._chromeExecutable = revisionInfo.executablePath; + } + if (Array.isArray(options.args)) + this._chromeArguments.push(...options.args); + this._terminated = false; + this._chromeProcess = null; + } - /** + /** * @return {!Promise} */ - async newPage() { - await this._ensureChromeIsRunning(); - if (!this._chromeProcess || this._terminated) - throw new Error('ERROR: this chrome instance is not alive any more!'); - var client = await Connection.create(this._remoteDebuggingPort); - var page = await Page.create(client); - return page; - } + async newPage() { + await this._ensureChromeIsRunning(); + if (!this._chromeProcess || this._terminated) + throw new Error('ERROR: this chrome instance is not alive any more!'); + var client = await Connection.create(this._remoteDebuggingPort); + var page = await Page.create(client); + return page; + } - /** + /** * @param {!Page} page */ - async closePage(page) { - if (!this._chromeProcess || this._terminated) - throw new Error('ERROR: this chrome instance is not running'); - await page.close(); - } + async closePage(page) { + if (!this._chromeProcess || this._terminated) + throw new Error('ERROR: this chrome instance is not running'); + await page.close(); + } - /** + /** * @return {string} */ - async version() { - await this._ensureChromeIsRunning(); - var version = await Connection.version(this._remoteDebuggingPort); - return version.Browser; - } + async version() { + await this._ensureChromeIsRunning(); + var version = await Connection.version(this._remoteDebuggingPort); + return version.Browser; + } - async _ensureChromeIsRunning() { - if (this._chromeProcess) - return; - this._chromeProcess = childProcess.spawn(this._chromeExecutable, this._chromeArguments, {}); - var stderr = ''; - this._chromeProcess.stderr.on('data', data => stderr += data.toString('utf8')); - // Cleanup as processes exit. - process.on('exit', () => this._chromeProcess.kill()); - this._chromeProcess.on('exit', () => { - this._terminated = true; - removeRecursive(this._userDataDir); - }); + async _ensureChromeIsRunning() { + if (this._chromeProcess) + return; + this._chromeProcess = childProcess.spawn(this._chromeExecutable, this._chromeArguments, {}); + var stderr = ''; + this._chromeProcess.stderr.on('data', data => stderr += data.toString('utf8')); + // Cleanup as processes exit. + process.on('exit', () => this._chromeProcess.kill()); + this._chromeProcess.on('exit', () => { + this._terminated = true; + removeRecursive(this._userDataDir); + }); - await waitForChromeResponsive(this._remoteDebuggingPort, () => !this._terminated); - if (this._terminated) - throw new Error('Failed to launch chrome! ' + stderr); - } + await waitForChromeResponsive(this._remoteDebuggingPort, () => !this._terminated); + if (this._terminated) + throw new Error('Failed to launch chrome! ' + stderr); + } - close() { - if (!this._chromeProcess) - return; - this._chromeProcess.kill(); - } + close() { + if (!this._chromeProcess) + return; + this._chromeProcess.kill(); + } } module.exports = Browser; function waitForChromeResponsive(remoteDebuggingPort, shouldWaitCallback) { - var fulfill; - var promise = new Promise(x => fulfill = x); - var options = { - method: 'GET', - host: 'localhost', - port: remoteDebuggingPort, - path: '/json/list' - }; - var probeTimeout = 100; - sendRequest(); - return promise; + var fulfill; + var promise = new Promise(x => fulfill = x); + var options = { + method: 'GET', + host: 'localhost', + port: remoteDebuggingPort, + path: '/json/list' + }; + var probeTimeout = 100; + sendRequest(); + return promise; - function sendRequest() { - var req = http.request(options, res => { - fulfill(); - }); - req.on('error', e => { - if (shouldWaitCallback()) - setTimeout(sendRequest, probeTimeout); - else - fulfill(); - }); - req.end(); - } + function sendRequest() { + var req = http.request(options, res => { + fulfill(); + }); + req.on('error', e => { + if (shouldWaitCallback()) + setTimeout(sendRequest, probeTimeout); + else + fulfill(); + }); + req.end(); + } } diff --git a/lib/Connection.js b/lib/Connection.js index cc1735a131e..7795e5b616e 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -20,90 +20,90 @@ var http = require('http'); const COMMAND_TIMEOUT = 10000; class Connection extends EventEmitter { - /** + /** * @param {number} port * @param {string} pageId * @param {!WebSocket} ws */ - constructor(port, pageId, ws) { - super(); - this._port = port; - this._pageId = pageId; - this._lastId = 0; - /** @type {!Map}*/ - this._callbacks = new Map(); + constructor(port, pageId, ws) { + super(); + this._port = port; + this._pageId = pageId; + this._lastId = 0; + /** @type {!Map}*/ + this._callbacks = new Map(); - this._ws = ws; - this._ws.on('message', this._onMessage.bind(this)); - this._ws.on('close', this._onClose.bind(this)); - } + this._ws = ws; + this._ws.on('message', this._onMessage.bind(this)); + this._ws.on('close', this._onClose.bind(this)); + } - /** + /** * @param {string} method * @param {(!Object|undefined)} params * @return {!Promise} */ - send(method, params = {}) { - var id = ++this._lastId; - var message = JSON.stringify({id, method, params}); - this._ws.send(message); - return new Promise((resolve, reject) => { - this._callbacks.set(id, {resolve, reject, method}); - }); - } + send(method, params = {}) { + var id = ++this._lastId; + var message = JSON.stringify({id, method, params}); + this._ws.send(message); + return new Promise((resolve, reject) => { + this._callbacks.set(id, {resolve, reject, method}); + }); + } - /** + /** * @param {string} message */ - _onMessage(message) { - var object = JSON.parse(message); - if (object.id && this._callbacks.has(object.id)) { - var callback = this._callbacks.get(object.id); - this._callbacks.delete(object.id); - if (object.error) - callback.reject(new Error(`Protocol error (${callback.method}): ${object.error.message}`)); - else - callback.resolve(object.result); - } else { - console.assert(!object.id); - this.emit(object.method, object.params); - } + _onMessage(message) { + var object = JSON.parse(message); + if (object.id && this._callbacks.has(object.id)) { + var callback = this._callbacks.get(object.id); + this._callbacks.delete(object.id); + if (object.error) + callback.reject(new Error(`Protocol error (${callback.method}): ${object.error.message}`)); + else + callback.resolve(object.result); + } else { + console.assert(!object.id); + this.emit(object.method, object.params); } + } - _onClose() { - this._ws.removeAllListeners(); - this._ws.close(); - } + _onClose() { + this._ws.removeAllListeners(); + this._ws.close(); + } - /** + /** * @return {!Promise} */ - async dispose() { - await runJsonCommand(this._port, `close/${this._pageId}`); - } + async dispose() { + await runJsonCommand(this._port, `close/${this._pageId}`); + } - /** + /** * @param {number} port * @return {!Promise} */ - static async create(port) { - var newTab = await runJsonCommand(port, 'new'); - var url = newTab.webSocketDebuggerUrl; + static async create(port) { + var newTab = await runJsonCommand(port, 'new'); + var url = newTab.webSocketDebuggerUrl; - return new Promise((resolve, reject) => { - var ws = new WebSocket(url, { perMessageDeflate: false }); - ws.on('open', () => resolve(new Connection(port, newTab.id, ws))); - ws.on('error', reject); - }); - } + return new Promise((resolve, reject) => { + var ws = new WebSocket(url, { perMessageDeflate: false }); + ws.on('open', () => resolve(new Connection(port, newTab.id, ws))); + ws.on('error', reject); + }); + } - /** + /** * @param {number} port * @return {!Promise} */ - static version(port) { - return runJsonCommand(port, 'version'); - } + static version(port) { + return runJsonCommand(port, 'version'); + } } /** @@ -112,41 +112,41 @@ class Connection extends EventEmitter { * @return {!Promise} */ function runJsonCommand(port, command) { - var request = http.get({ - hostname: 'localhost', - port: port, - path: '/json/' + command - }, onResponse); - request.setTimeout(COMMAND_TIMEOUT, onTimeout); - var resolve, reject; - return new Promise((res, rej) => { resolve = res; reject = rej; }); + var request = http.get({ + hostname: 'localhost', + port: port, + path: '/json/' + command + }, onResponse); + request.setTimeout(COMMAND_TIMEOUT, onTimeout); + var resolve, reject; + return new Promise((res, rej) => { resolve = res; reject = rej; }); - function onResponse(response) { - var data = ''; - response.on('data', chunk => data += chunk); - response.on('end', () => { - if (response.statusCode !== 200) { - reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`)); - return; - } - // In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD - if (data === 'Target is closing' || data === 'Target activated') { - resolve({message: data}); - return; - } - try { - resolve(JSON.parse(data)); - } catch (e) { - reject(e); - } - }); - } + function onResponse(response) { + var data = ''; + response.on('data', chunk => data += chunk); + response.on('end', () => { + if (response.statusCode !== 200) { + reject(new Error(`Protocol JSON API error (${command}), status: ${response.statusCode}`)); + return; + } + // In the case of 'close' & 'activate' Chromium returns a string rather than JSON: goo.gl/7v27xD + if (data === 'Target is closing' || data === 'Target activated') { + resolve({message: data}); + return; + } + try { + resolve(JSON.parse(data)); + } catch (e) { + reject(e); + } + }); + } - function onTimeout() { - request.abort(); - // Reject on error with code specifically indicating timeout in connection setup. - reject(new Error('Timeout waiting for initial Debugger Protocol connection.')); - } + function onTimeout() { + request.abort(); + // Reject on error with code specifically indicating timeout in connection setup. + reject(new Error('Timeout waiting for initial Debugger Protocol connection.')); + } } module.exports = Connection; diff --git a/lib/Dialog.js b/lib/Dialog.js index db859a370df..8f01e2ded7a 100644 --- a/lib/Dialog.js +++ b/lib/Dialog.js @@ -15,55 +15,55 @@ */ class Dialog { - /** + /** * @param {!Connection} client * @param {!Dialog.Type} type * @param {string} message */ - constructor(client, type, message) { - this._client = client; - this.type = type; - this._message = message; - this._handled = false; - } + constructor(client, type, message) { + this._client = client; + this.type = type; + this._message = message; + this._handled = false; + } - /** + /** * @return {string} */ - message() { - return this._message; - } + message() { + return this._message; + } - /** + /** * @param {string=} promptText * @return {!Promise} */ - async accept(promptText) { - console.assert(!this._handled, 'Cannot accept dialog which is already handled!'); - this._handled = true; - await this._client.send('Page.handleJavaScriptDialog', { - accept: true, - promptText: promptText - }); - } + async accept(promptText) { + console.assert(!this._handled, 'Cannot accept dialog which is already handled!'); + this._handled = true; + await this._client.send('Page.handleJavaScriptDialog', { + accept: true, + promptText: promptText + }); + } - /** + /** * @return {!Promise} */ - async dismiss() { - console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); - this._handled = true; - await this._client.send('Page.handleJavaScriptDialog', { - accept: false - }); - } + async dismiss() { + console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); + this._handled = true; + await this._client.send('Page.handleJavaScriptDialog', { + accept: false + }); + } } Dialog.Type = { - Alert: 'alert', - BeforeUnload: 'beforeunload', - Confirm: 'confirm', - Prompt: 'prompt' + Alert: 'alert', + BeforeUnload: 'beforeunload', + Confirm: 'confirm', + Prompt: 'prompt' }; module.exports = Dialog; diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 6e0e82755c0..175c632c002 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -17,229 +17,229 @@ var EventEmitter = require('events'); class FrameManager extends EventEmitter { - /** + /** * @param {!Connection} client * @return {!FrameManager} */ - static async create(client) { - var mainFramePayload = await client.send('Page.getResourceTree'); - return new FrameManager(client, mainFramePayload.frameTree); - } + static async create(client) { + var mainFramePayload = await client.send('Page.getResourceTree'); + return new FrameManager(client, mainFramePayload.frameTree); + } - /** + /** * @param {!Connection} client * @param {!Object} frameTree */ - constructor(client, frameTree) { - super(); - this._client = client; - /** @type {!Map} */ - this._frames = new Map(); - this._mainFrame = this._addFramesRecursively(null, frameTree); + constructor(client, frameTree) { + super(); + this._client = client; + /** @type {!Map} */ + this._frames = new Map(); + this._mainFrame = this._addFramesRecursively(null, frameTree); - this._client.on('Page.frameAttached', event => this._frameAttached(event.frameId, event.parentFrameId)); - this._client.on('Page.frameNavigated', event => this._frameNavigated(event.frame)); - this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId)); - } + this._client.on('Page.frameAttached', event => this._frameAttached(event.frameId, event.parentFrameId)); + this._client.on('Page.frameNavigated', event => this._frameNavigated(event.frame)); + this._client.on('Page.frameDetached', event => this._frameDetached(event.frameId)); + } - /** + /** * @return {!Frame} */ - mainFrame() { - return this._mainFrame; - } + mainFrame() { + return this._mainFrame; + } - /** + /** * @return {!Array} */ - frames() { - return Array.from(this._frames.values()); - } + frames() { + return Array.from(this._frames.values()); + } - /** + /** * @param {string} frameId * @param {?string} parentFrameId * @return {?Frame} */ - _frameAttached(frameId, parentFrameId) { - if (this._frames.has(frameId)) - return; + _frameAttached(frameId, parentFrameId) { + if (this._frames.has(frameId)) + return; - if (!parentFrameId) { - // Navigation to the new backend process. - this._navigateFrame(this._mainFrame, frameId, null); - return; - } - var parentFrame = this._frames.get(parentFrameId); - var frame = new Frame(parentFrame, frameId, null); - this._frames.set(frame._id, frame); - this.emit(FrameManager.Events.FrameAttached, frame); + if (!parentFrameId) { + // Navigation to the new backend process. + this._navigateFrame(this._mainFrame, frameId, null); + return; } + var parentFrame = this._frames.get(parentFrameId); + var frame = new Frame(parentFrame, frameId, null); + this._frames.set(frame._id, frame); + this.emit(FrameManager.Events.FrameAttached, frame); + } - /** + /** * @param {!Object} framePayload */ - _frameNavigated(framePayload) { - var frame = this._frames.get(framePayload.id); - if (!frame) { - // Simulate missed "frameAttached" for a main frame navigation to the new backend process. - console.assert(!framePayload.parentId, 'Main frame shouldn\'t have parent frame id.'); - frame = this._mainFrame; - } - this._navigateFrame(frame, framePayload.id, framePayload); + _frameNavigated(framePayload) { + var frame = this._frames.get(framePayload.id); + if (!frame) { + // Simulate missed "frameAttached" for a main frame navigation to the new backend process. + console.assert(!framePayload.parentId, 'Main frame shouldn\'t have parent frame id.'); + frame = this._mainFrame; } + this._navigateFrame(frame, framePayload.id, framePayload); + } - /** + /** * @param {string} frameId */ - _frameDetached(frameId) { - var frame = this._frames.get(frameId); - if (frame) - this._removeFramesRecursively(frame); - } + _frameDetached(frameId) { + var frame = this._frames.get(frameId); + if (frame) + this._removeFramesRecursively(frame); + } - /** + /** * @param {!Frame} frame * @param {string} newFrameId * @param {?Object} newFramePayload */ - _navigateFrame(frame, newFrameId, newFramePayload) { - // Detach all child frames first. - for (var child of frame.childFrames()) - this._removeFramesRecursively(child); - this._frames.delete(frame._id, frame); - frame._id = newFrameId; - frame._adoptPayload(newFramePayload); - this._frames.set(newFrameId, frame); - this.emit(FrameManager.Events.FrameNavigated, frame); - } + _navigateFrame(frame, newFrameId, newFramePayload) { + // Detach all child frames first. + for (var child of frame.childFrames()) + this._removeFramesRecursively(child); + this._frames.delete(frame._id, frame); + frame._id = newFrameId; + frame._adoptPayload(newFramePayload); + this._frames.set(newFrameId, frame); + this.emit(FrameManager.Events.FrameNavigated, frame); + } - /** + /** * @param {?Frame} parentFrame * @param {!Object} frameTreePayload * @return {!Frame} */ - _addFramesRecursively(parentFrame, frameTreePayload) { - var framePayload = frameTreePayload.frame; - var frame = new Frame(parentFrame, framePayload.id, framePayload); - this._frames.set(frame._id, frame); + _addFramesRecursively(parentFrame, frameTreePayload) { + var framePayload = frameTreePayload.frame; + var frame = new Frame(parentFrame, framePayload.id, framePayload); + this._frames.set(frame._id, frame); - for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i) - this._addFramesRecursively(frame, frameTreePayload.childFrames[i]); - return frame; - } + for (var i = 0; frameTreePayload.childFrames && i < frameTreePayload.childFrames.length; ++i) + this._addFramesRecursively(frame, frameTreePayload.childFrames[i]); + return frame; + } - /** + /** * @param {!Frame} frame */ - _removeFramesRecursively(frame) { - for (var child of frame.childFrames()) - this._removeFramesRecursively(child); - frame._detach(); - this._frames.delete(frame._id); - this.emit(FrameManager.Events.FrameDetached, frame); - } + _removeFramesRecursively(frame) { + for (var child of frame.childFrames()) + this._removeFramesRecursively(child); + frame._detach(); + this._frames.delete(frame._id); + this.emit(FrameManager.Events.FrameDetached, frame); + } } /** @enum {string} */ FrameManager.Events = { - FrameAttached: 'frameattached', - FrameNavigated: 'framenavigated', - FrameDetached: 'framedetached' + FrameAttached: 'frameattached', + FrameNavigated: 'framenavigated', + FrameDetached: 'framedetached' }; /** * @unrestricted */ class Frame { - /** + /** * @param {?Frame} parentFrame * @param {string} frameId * @param {?Object} payload */ - constructor(parentFrame, frameId, payload) { - this._parentFrame = parentFrame; - this._url = ''; - this._id = frameId; - this._adoptPayload(payload); + constructor(parentFrame, frameId, payload) { + this._parentFrame = parentFrame; + this._url = ''; + this._id = frameId; + this._adoptPayload(payload); - /** @type {!Set} */ - this._childFrames = new Set(); - if (this._parentFrame) - this._parentFrame._childFrames.add(this); - } + /** @type {!Set} */ + this._childFrames = new Set(); + if (this._parentFrame) + this._parentFrame._childFrames.add(this); + } - /** + /** * @return {string} */ - name() { - return this._name || ''; - } + name() { + return this._name || ''; + } - /** + /** * @return {string} */ - url() { - return this._url; - } + url() { + return this._url; + } - /** + /** * @return {string} */ - securityOrigin() { - return this._securityOrigin; - } + securityOrigin() { + return this._securityOrigin; + } - /** + /** * @return {?Frame} */ - parentFrame() { - return this._parentFrame; - } + parentFrame() { + return this._parentFrame; + } - /** + /** * @return {!Array.} */ - childFrames() { - return Array.from(this._childFrames); - } + childFrames() { + return Array.from(this._childFrames); + } - /** + /** * @return {boolean} */ - isMainFrame() { - return !this._detached && !this._parentFrame; - } + isMainFrame() { + return !this._detached && !this._parentFrame; + } - /** + /** * @return {boolean} */ - isDetached() { - return this._detached; - } + isDetached() { + return this._detached; + } - /** + /** * @param {?Object} framePayload */ - _adoptPayload(framePayload) { - framePayload = framePayload || { - name: '', - url: '', - securityOrigin: '', - mimeType: '' - }; - this._name = framePayload.name; - this._url = framePayload.url; - this._securityOrigin = framePayload.securityOrigin; - this._mimeType = framePayload.mimeType; - } + _adoptPayload(framePayload) { + framePayload = framePayload || { + name: '', + url: '', + securityOrigin: '', + mimeType: '' + }; + this._name = framePayload.name; + this._url = framePayload.url; + this._securityOrigin = framePayload.securityOrigin; + this._mimeType = framePayload.mimeType; + } - _detach() { - this._detached = true; - if (this._parentFrame) - this._parentFrame._childFrames.delete(this); - this._parentFrame = null; - } + _detach() { + this._detached = true; + if (this._parentFrame) + this._parentFrame._childFrames.delete(this); + this._parentFrame = null; + } } module.exports = FrameManager; diff --git a/lib/Navigator.js b/lib/Navigator.js index aafa6b420a5..188c145d65f 100644 --- a/lib/Navigator.js +++ b/lib/Navigator.js @@ -15,84 +15,84 @@ */ class Navigator { - /** + /** * @param {!Connection} client * @param {!Object=} options */ - constructor(client, options) { - this._client = client; - client.on('Network.requestWillBeSent', event => this._onRequestWillBeSent(event)); - client.on('Network.loadingFinished', event => this._onLoadingFinished(event)); - client.on('Network.loadingFailed', event => this._onLoadingFailed(event)); - this._minTime = options && options['minTime'] ? options['minTime'] : 0; - this._maxTime = options && options['maxTime'] ? options['maxTime'] : 30000; - this._inflightRequests = 0; - } + constructor(client, options) { + this._client = client; + client.on('Network.requestWillBeSent', event => this._onRequestWillBeSent(event)); + client.on('Network.loadingFinished', event => this._onLoadingFinished(event)); + client.on('Network.loadingFailed', event => this._onLoadingFailed(event)); + this._minTime = options && options['minTime'] ? options['minTime'] : 0; + this._maxTime = options && options['maxTime'] ? options['maxTime'] : 30000; + this._inflightRequests = 0; + } - /** + /** * @param {string} url * @param {string=} referrer */ - async navigate(url, referrer) { - this._navigationStartTime = Date.now(); - this._watchdogTimer = setTimeout(this._completeNavigation.bind(this, true), this._maxTime); - this._minimumTimer = setTimeout(this._completeNavigation.bind(this, false), this._minTime); - let onload = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)); - let networkIdle = new Promise(fulfill => this._navigationLoadCallback = fulfill); - var interstitialPromise = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false); + async navigate(url, referrer) { + this._navigationStartTime = Date.now(); + this._watchdogTimer = setTimeout(this._completeNavigation.bind(this, true), this._maxTime); + this._minimumTimer = setTimeout(this._completeNavigation.bind(this, false), this._minTime); + let onload = new Promise(fulfill => this._client.once('Page.loadEventFired', fulfill)); + let networkIdle = new Promise(fulfill => this._navigationLoadCallback = fulfill); + var interstitialPromise = new Promise(fulfill => this._client.once('Security.certificateError', fulfill)).then(() => false); - this._inflightRequests = 0; - // Await for the command to throw exception in case of illegal arguments. - try { - await this._client.send('Page.navigate', {url, referrer}); - } catch (e) { - return false; - } - return await Promise.race([Promise.all([onload, networkIdle]).then(() => true), interstitialPromise]); + this._inflightRequests = 0; + // Await for the command to throw exception in case of illegal arguments. + try { + await this._client.send('Page.navigate', {url, referrer}); + } catch (e) { + return false; } + return await Promise.race([Promise.all([onload, networkIdle]).then(() => true), interstitialPromise]); + } - /** + /** * @param {!Object} event */ - _onRequestWillBeSent(event) { - if (!event.redirectResponse) - ++this._inflightRequests; - } + _onRequestWillBeSent(event) { + if (!event.redirectResponse) + ++this._inflightRequests; + } - /** + /** * @param {!Object} event */ - _onLoadingFinished(event) { - this._onLoadingCompleted(event); - } + _onLoadingFinished(event) { + this._onLoadingCompleted(event); + } - /** + /** * @param {!Object} event */ - _onLoadingFailed(event) { - this._onLoadingCompleted(event); - } + _onLoadingFailed(event) { + this._onLoadingCompleted(event); + } - _onLoadingCompleted(event) { - --this._inflightRequests; - if (Date.now() - this._navigationStartTime < this._minTime) - return; - this._completeNavigation(false); - } + _onLoadingCompleted(event) { + --this._inflightRequests; + if (Date.now() - this._navigationStartTime < this._minTime) + return; + this._completeNavigation(false); + } - /** + /** * @param {boolean} force */ - _completeNavigation(force) { - if (!this._navigationLoadCallback) - return; - if (this._inflightRequests < 2 || force) { - clearTimeout(this._minimumTimer); - clearTimeout(this._watchdogTimer); - this._navigationLoadCallback(); - this._navigationLoadCallback = null; - } + _completeNavigation(force) { + if (!this._navigationLoadCallback) + return; + if (this._inflightRequests < 2 || force) { + clearTimeout(this._minimumTimer); + clearTimeout(this._watchdogTimer); + this._navigationLoadCallback(); + this._navigationLoadCallback = null; } + } } module.exports = Navigator; diff --git a/lib/Page.js b/lib/Page.js index d220bdadf02..a9a5809a175 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -23,526 +23,526 @@ var Dialog = require('./Dialog'); var FrameManager = require('./FrameManager'); class Page extends EventEmitter { - /** + /** * @param {!Connection} client * @return {!Promise} */ - static async create(client) { - await Promise.all([ - client.send('Network.enable', {}), - client.send('Page.enable', {}), - client.send('Runtime.enable', {}), - client.send('Security.enable', {}), - ]); - 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); - // Initialize default page size. - await page.setViewportSize({width: 400, height: 300}); - return page; - } + static async create(client) { + await Promise.all([ + client.send('Network.enable', {}), + client.send('Page.enable', {}), + client.send('Runtime.enable', {}), + client.send('Security.enable', {}), + ]); + 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); + // Initialize default page size. + await page.setViewportSize({width: 400, height: 300}); + return page; + } - /** + /** * @param {!Connection} client * @param {!FrameManager} frameManager * @param {number} screenDPI */ - constructor(client, frameManager, screenDPI) { - super(); - this._client = client; - this._frameManager = frameManager; - this._screenDPI = screenDPI; - this._extraHeaders = {}; - /** @type {!Map} */ - this._inPageCallbacks = new Map(); - /** @type {?function(!Request)} */ - this._requestInterceptor = null; + constructor(client, frameManager, screenDPI) { + super(); + this._client = client; + this._frameManager = frameManager; + this._screenDPI = screenDPI; + this._extraHeaders = {}; + /** @type {!Map} */ + this._inPageCallbacks = new Map(); + /** @type {?function(!Request)} */ + this._requestInterceptor = null; - this._screenshotTaskChain = Promise.resolve(); + this._screenshotTaskChain = Promise.resolve(); - this._frameManager.on(FrameManager.Events.FrameAttached, event => this.emit(Page.Events.FrameAttached, event)); - this._frameManager.on(FrameManager.Events.FrameDetached, event => this.emit(Page.Events.FrameDetached, event)); - this._frameManager.on(FrameManager.Events.FrameNavigated, event => this.emit(Page.Events.FrameNavigated, event)); + this._frameManager.on(FrameManager.Events.FrameAttached, event => this.emit(Page.Events.FrameAttached, event)); + this._frameManager.on(FrameManager.Events.FrameDetached, event => this.emit(Page.Events.FrameDetached, event)); + this._frameManager.on(FrameManager.Events.FrameNavigated, event => this.emit(Page.Events.FrameNavigated, event)); - client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response)); - client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event)); + client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response)); + client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event)); - client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event)); - client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)); - client.on('Page.javascriptDialogOpening', event => this._onDialog(event)); - client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)); - } + client.on('Network.requestIntercepted', event => this._onRequestIntercepted(event)); + client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)); + client.on('Page.javascriptDialogOpening', event => this._onDialog(event)); + client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)); + } - /** + /** * @return {!Frame} */ - mainFrame() { - return this._frameManager.mainFrame(); - } + mainFrame() { + return this._frameManager.mainFrame(); + } - /** + /** * @return {!Array} */ - frames() { - return this._frameManager.frames(); - } + frames() { + return this._frameManager.frames(); + } - /** + /** * @param {?function(!Request)} interceptor */ - async setRequestInterceptor(interceptor) { - this._requestInterceptor = interceptor; - await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor}); - } + async setRequestInterceptor(interceptor) { + this._requestInterceptor = interceptor; + await this._client.send('Network.enableRequestInterception', {enabled: !!interceptor}); + } - /** + /** * @param {!Object} event */ - _onRequestIntercepted(event) { - var request = new Request(this._client, event.InterceptionId, event.request); - this._requestInterceptor(request); - } + _onRequestIntercepted(event) { + var request = new Request(this._client, event.InterceptionId, event.request); + this._requestInterceptor(request); + } - /** + /** * @param {string} url * @return {!Promise} */ - async addScriptTag(url) { - return this.evaluate(addScriptTag, url); - - /** - * @param {string} url - */ - function addScriptTag(url) { - var script = document.createElement('script'); - script.src = url; - var promise = new Promise(x => script.onload = x); - document.head.appendChild(script); - return promise; - } - } + async addScriptTag(url) { + return this.evaluate(addScriptTag, url); /** + * @param {string} url + */ + function addScriptTag(url) { + var script = document.createElement('script'); + script.src = url; + var promise = new Promise(x => script.onload = x); + document.head.appendChild(script); + return promise; + } + } + + /** * @param {string} filePath * @return {!Promise} */ - async injectFile(filePath) { - 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 }); - } + async injectFile(filePath) { + 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 }); + } - /** + /** * @param {string} name * @param {function(?)} callback */ - async setInPageCallback(name, callback) { - if (this._inPageCallbacks[name]) - throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`); - this._inPageCallbacks[name] = callback; + async setInPageCallback(name, callback) { + if (this._inPageCallbacks[name]) + throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`); + this._inPageCallbacks[name] = callback; - var expression = Page._evaluationString(inPageCallback, name); - await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression }); - await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); + var expression = Page._evaluationString(inPageCallback, name); + await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: expression }); + await this._client.send('Runtime.evaluate', { expression, returnByValue: true }); - function inPageCallback(callbackName) { - window[callbackName] = async(...args) => { - const me = window[callbackName]; - let callbacks = me['callbacks']; - if (!callbacks) { - callbacks = new Map(); - me['callbacks'] = callbacks; - } - const seq = (me['lastSeq'] || 0) + 1; - me['lastSeq'] = seq; - const promise = new Promise(fulfill => callbacks.set(seq, fulfill)); - // eslint-disable-next-line no-console - console.debug('driver:InPageCallback', JSON.stringify({name: callbackName, seq, args})); - return promise; - }; + function inPageCallback(callbackName) { + window[callbackName] = async(...args) => { + const me = window[callbackName]; + let callbacks = me['callbacks']; + if (!callbacks) { + callbacks = new Map(); + me['callbacks'] = callbacks; } + const seq = (me['lastSeq'] || 0) + 1; + me['lastSeq'] = seq; + const promise = new Promise(fulfill => callbacks.set(seq, fulfill)); + // eslint-disable-next-line no-console + console.debug('driver:InPageCallback', JSON.stringify({name: callbackName, seq, args})); + return promise; + }; } + } - /** + /** * @param {!Object} headers * @return {!Promise} */ - async setExtraHTTPHeaders(headers) { - this._extraHeaders = {}; - // Note: header names are case-insensitive. - for (var key of Object.keys(headers)) - this._extraHeaders[key.toLowerCase()] = headers[key]; - return this._client.send('Network.setExtraHTTPHeaders', { headers }); - } + async setExtraHTTPHeaders(headers) { + this._extraHeaders = {}; + // Note: header names are case-insensitive. + for (var key of Object.keys(headers)) + this._extraHeaders[key.toLowerCase()] = headers[key]; + return this._client.send('Network.setExtraHTTPHeaders', { headers }); + } - /** + /** * @return {!Object} */ - extraHTTPHeaders() { - return Object.assign({}, this._extraHeaders); - } + extraHTTPHeaders() { + return Object.assign({}, this._extraHeaders); + } - /** + /** * @param {string} userAgent * @return {!Promise} */ - async setUserAgentOverride(userAgent) { - this._userAgent = userAgent; - return this._client.send('Network.setUserAgentOverride', { userAgent }); - } + async setUserAgentOverride(userAgent) { + this._userAgent = userAgent; + return this._client.send('Network.setUserAgentOverride', { userAgent }); + } - /** + /** * @return {string} */ - userAgentOverride() { - return this._userAgent; - } + userAgentOverride() { + return this._userAgent; + } - /** + /** * @param {!Object} exceptionDetails */ - async _handleException(exceptionDetails) { - var message = await this._getExceptionMessage(exceptionDetails); - this.emit(Page.Events.Error, new Error(message)); + async _handleException(exceptionDetails) { + var message = await this._getExceptionMessage(exceptionDetails); + this.emit(Page.Events.Error, new Error(message)); + } + + async _onConsoleAPI(event) { + 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 = Page._evaluationString(deliverResult, name, seq, result); + this._client.send('Runtime.evaluate', { expression }); + + function deliverResult(name, seq, result) { + window[name]['callbacks'].get(seq)(result); + window[name]['callbacks'].delete(seq); + } + return; } + var values = event.args.map(arg => arg.value || arg.description || ''); + this.emit(Page.Events.ConsoleMessage, values.join(' ')); + } - async _onConsoleAPI(event) { - 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 = Page._evaluationString(deliverResult, name, seq, result); - this._client.send('Runtime.evaluate', { expression }); + _onDialog(event) { + var dialogType = null; + if (event.type === 'alert') + dialogType = Dialog.Type.Alert; + else if (event.type === 'confirm') + dialogType = Dialog.Type.Confirm; + else if (event.type === 'prompt') + dialogType = Dialog.Type.Prompt; + else if (event.type === 'beforeunload') + dialogType = Dialog.Type.BeforeUnload; + console.assert(dialogType, 'Unknown javascript dialog type: ' + event.type); + var dialog = new Dialog(this._client, dialogType, event.message); + this.emit(Page.Events.Dialog, dialog); + } - function deliverResult(name, seq, result) { - window[name]['callbacks'].get(seq)(result); - window[name]['callbacks'].delete(seq); - } - return; - } - var values = event.args.map(arg => arg.value || arg.description || ''); - this.emit(Page.Events.ConsoleMessage, values.join(' ')); - } - - _onDialog(event) { - var dialogType = null; - if (event.type === 'alert') - dialogType = Dialog.Type.Alert; - else if (event.type === 'confirm') - dialogType = Dialog.Type.Confirm; - else if (event.type === 'prompt') - dialogType = Dialog.Type.Prompt; - else if (event.type === 'beforeunload') - dialogType = Dialog.Type.BeforeUnload; - console.assert(dialogType, 'Unknown javascript dialog type: ' + event.type); - var dialog = new Dialog(this._client, dialogType, event.message); - this.emit(Page.Events.Dialog, dialog); - } - - /** + /** * @return {!Promise} */ - async url() { - return this.evaluate(() => window.location.href); - } + async url() { + return this.evaluate(() => window.location.href); + } - /** + /** * @param {string} html * @return {!Promise} */ - async setContent(html) { - this.evaluate(() => { - document.open(); - document.write(html); - document.close(); - }, html); - } + async setContent(html) { + this.evaluate(() => { + document.open(); + document.write(html); + document.close(); + }, html); + } - /** + /** * @param {string} html * @param {!Object=} options * @return {!Promise} */ - navigate(url, options) { - return new Navigator(this._client, options).navigate(url, this._extraHeaders.referer); - } + navigate(url, options) { + return new Navigator(this._client, options).navigate(url, this._extraHeaders.referer); + } - /** + /** * @param {!{width: number, height: number}} size * @return {!Promise} */ - async setViewportSize(size) { - this._viewportSize = size; - var width = size.width; - var height = size.height; - var zoom = this._screenDPI; - return Promise.all([ - this._client.send('Emulation.setDeviceMetricsOverride', { - width, - height, - deviceScaleFactor: 1, - scale: 1 / zoom, - mobile: false, - fitWindow: false - }), - this._client.send('Emulation.setVisibleSize', { - width: width / zoom, - height: height / zoom, - }) - ]); - } + async setViewportSize(size) { + this._viewportSize = size; + var width = size.width; + var height = size.height; + var zoom = this._screenDPI; + return Promise.all([ + this._client.send('Emulation.setDeviceMetricsOverride', { + width, + height, + deviceScaleFactor: 1, + scale: 1 / zoom, + mobile: false, + fitWindow: false + }), + this._client.send('Emulation.setVisibleSize', { + width: width / zoom, + height: height / zoom, + }) + ]); + } - /** + /** * @return {!{width: number, height: number}} */ - viewportSize() { - return this._viewportSize; - } + viewportSize() { + return this._viewportSize; + } - /** + /** * @param {function()} fun * @param {!Array<*>} args * @return {!Promise<(!Object|undefined)>} */ - async evaluate(fun, ...args) { - 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); - } - return remoteObject.value; + async evaluate(fun, ...args) { + 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); } + return remoteObject.value; + } - /** + /** * @param {!Object} exceptionDetails * @return {string} */ - async _getExceptionMessage(exceptionDetails) { - var message = ''; - var exception = exceptionDetails.exception; - if (exception) { - var response = await this._client.send('Runtime.callFunctionOn', { - objectId: exception.objectId, - functionDeclaration: 'function() { return this.message; }', - returnByValue: true, - }); - message = response.result.value; - } else { - message = exceptionDetails.text; - } - - if (exceptionDetails.stackTrace) { - for (var callframe of exceptionDetails.stackTrace.callFrames) { - var location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; - var functionName = callframe.functionName || ''; - message += `\n at ${functionName} (${location})`; - } - } - return message; + async _getExceptionMessage(exceptionDetails) { + var message = ''; + var exception = exceptionDetails.exception; + if (exception) { + var response = await this._client.send('Runtime.callFunctionOn', { + objectId: exception.objectId, + functionDeclaration: 'function() { return this.message; }', + returnByValue: true, + }); + message = response.result.value; + } else { + message = exceptionDetails.text; } - /** + if (exceptionDetails.stackTrace) { + for (var callframe of exceptionDetails.stackTrace.callFrames) { + var location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; + var functionName = callframe.functionName || ''; + message += `\n at ${functionName} (${location})`; + } + } + return message; + } + + /** * @param {function()} fun * @param {!Array<*>} args * @return {!Promise} */ - async evaluateOnInitialized(fun, ...args) { - var scriptSource = Page._evaluationString(fun, ...args); - await this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource }); - } + async evaluateOnInitialized(fun, ...args) { + 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(',')})`; - } + static _evaluationString(fun, ...args) { + return `(${fun})(${args.map(x => JSON.stringify(x)).join(',')})`; + } - /** + /** * @param {!Object=} options * @return {!Promise} */ - async screenshot(options) { - options = options || {}; - var screenshotType = null; - if (options.path) { - var mimeType = mime.lookup(options.path); - if (mimeType === 'image/png') - screenshotType = 'png'; - else if (mimeType === 'image/jpeg') - screenshotType = 'jpeg'; - console.assert(screenshotType, 'Unsupported screenshot mime type: ' + mimeType); - } - if (options.type) { - console.assert(!screenshotType || options.type === screenshotType, `Passed screenshot type '${options.type}' does not match the type inferred from the file path: '${screenshotType}'`); - console.assert(options.type === 'png' || options.type === 'jpeg', 'Unknown options.type value: ' + options.type); - screenshotType = options.type; - } - if (!screenshotType) - screenshotType = 'png'; - - if (options.quality) { - console.assert(screenshotType === 'jpeg', 'options.quality is unsupported for the ' + screenshotType + ' screenshots'); - console.assert(typeof options.quality === 'number', 'Expected options.quality to be a number but found ' + (typeof options.quality)); - console.assert(Number.isInteger(options.quality), 'Expected options.quality to be an integer'); - console.assert(options.quality >= 0 && options.quality <= 100, 'Expected options.quality to be between 0 and 100 (inclusive), got ' + options.quality); - } - console.assert(!options.clip || !options.fullPage, 'options.clip and options.fullPage are exclusive'); - if (options.clip) { - console.assert(typeof options.clip.x === 'number', 'Expected options.clip.x to be a number but found ' + (typeof options.clip.x)); - console.assert(typeof options.clip.y === 'number', 'Expected options.clip.y to be a number but found ' + (typeof options.clip.y)); - console.assert(typeof options.clip.width === 'number', 'Expected options.clip.width to be a number but found ' + (typeof options.clip.width)); - console.assert(typeof options.clip.height === 'number', 'Expected options.clip.height to be a number but found ' + (typeof options.clip.height)); - } - this._screenshotTaskChain = this._screenshotTaskChain.then(this._screenshotTask.bind(this, screenshotType, options)); - return this._screenshotTaskChain; + async screenshot(options) { + options = options || {}; + var screenshotType = null; + if (options.path) { + var mimeType = mime.lookup(options.path); + if (mimeType === 'image/png') + screenshotType = 'png'; + else if (mimeType === 'image/jpeg') + screenshotType = 'jpeg'; + console.assert(screenshotType, 'Unsupported screenshot mime type: ' + mimeType); } + if (options.type) { + console.assert(!screenshotType || options.type === screenshotType, `Passed screenshot type '${options.type}' does not match the type inferred from the file path: '${screenshotType}'`); + console.assert(options.type === 'png' || options.type === 'jpeg', 'Unknown options.type value: ' + options.type); + screenshotType = options.type; + } + if (!screenshotType) + screenshotType = 'png'; - /** + if (options.quality) { + console.assert(screenshotType === 'jpeg', 'options.quality is unsupported for the ' + screenshotType + ' screenshots'); + console.assert(typeof options.quality === 'number', 'Expected options.quality to be a number but found ' + (typeof options.quality)); + console.assert(Number.isInteger(options.quality), 'Expected options.quality to be an integer'); + console.assert(options.quality >= 0 && options.quality <= 100, 'Expected options.quality to be between 0 and 100 (inclusive), got ' + options.quality); + } + console.assert(!options.clip || !options.fullPage, 'options.clip and options.fullPage are exclusive'); + if (options.clip) { + console.assert(typeof options.clip.x === 'number', 'Expected options.clip.x to be a number but found ' + (typeof options.clip.x)); + console.assert(typeof options.clip.y === 'number', 'Expected options.clip.y to be a number but found ' + (typeof options.clip.y)); + console.assert(typeof options.clip.width === 'number', 'Expected options.clip.width to be a number but found ' + (typeof options.clip.width)); + console.assert(typeof options.clip.height === 'number', 'Expected options.clip.height to be a number but found ' + (typeof options.clip.height)); + } + this._screenshotTaskChain = this._screenshotTaskChain.then(this._screenshotTask.bind(this, screenshotType, options)); + return this._screenshotTaskChain; + } + + /** * @param {string} screenshotType * @param {!Object=} options * @return {!Promise} */ - async _screenshotTask(screenshotType, options) { - if (options.clip) { - await Promise.all([ - this._client.send('Emulation.setVisibleSize', { - width: Math.ceil(options.clip.width / this._screenDPI), - height: Math.ceil(options.clip.height / this._screenDPI), - }), - this._client.send('Emulation.forceViewport', { - x: options.clip.x / this._screenDPI, - y: options.clip.y / this._screenDPI, - scale: 1, - }) - ]); - } else if (options.fullPage) { - var response = await this._client.send('Page.getLayoutMetrics'); - await Promise.all([ - this._client.send('Emulation.setVisibleSize', { - width: Math.ceil(response.contentSize.width / this._screenDPI), - height: Math.ceil(response.contentSize.height / this._screenDPI), - }), - this._client.send('Emulation.forceViewport', { - x: 0, - y: 0, - scale: 1, - }) - ]); - } - var result = await this._client.send('Page.captureScreenshot', { - fromSurface: true, - format: screenshotType, - quality: options.quality - }); - if (options.clip || options.fullPage) { - await Promise.all([ - this.setViewportSize(this.viewportSize()), - this._client.send('Emulation.resetViewport') - ]); - } - var buffer = new Buffer(result.data, 'base64'); - if (options.path) - fs.writeFileSync(options.path, buffer); - return buffer; + async _screenshotTask(screenshotType, options) { + if (options.clip) { + await Promise.all([ + this._client.send('Emulation.setVisibleSize', { + width: Math.ceil(options.clip.width / this._screenDPI), + height: Math.ceil(options.clip.height / this._screenDPI), + }), + this._client.send('Emulation.forceViewport', { + x: options.clip.x / this._screenDPI, + y: options.clip.y / this._screenDPI, + scale: 1, + }) + ]); + } else if (options.fullPage) { + var response = await this._client.send('Page.getLayoutMetrics'); + await Promise.all([ + this._client.send('Emulation.setVisibleSize', { + width: Math.ceil(response.contentSize.width / this._screenDPI), + height: Math.ceil(response.contentSize.height / this._screenDPI), + }), + this._client.send('Emulation.forceViewport', { + x: 0, + y: 0, + scale: 1, + }) + ]); } + var result = await this._client.send('Page.captureScreenshot', { + fromSurface: true, + format: screenshotType, + quality: options.quality + }); + if (options.clip || options.fullPage) { + await Promise.all([ + this.setViewportSize(this.viewportSize()), + this._client.send('Emulation.resetViewport') + ]); + } + var buffer = new Buffer(result.data, 'base64'); + if (options.path) + fs.writeFileSync(options.path, buffer); + return buffer; + } - /** + /** * @param {string} filePath * @param {!Object=} options * @return {!Promise} */ - async printToPDF(filePath, options) { - options = options || {}; + async printToPDF(filePath, options) { + options = options || {}; - var scale = options.scale || 1; - var displayHeaderFooter = options.displayHeaderFooter || false; - var printBackground = options.printBackground || true; - var landscape = options.landscape || false; - var pageRanges = options.pageRanges || ''; + var scale = options.scale || 1; + var displayHeaderFooter = options.displayHeaderFooter || false; + var printBackground = options.printBackground || true; + var landscape = options.landscape || false; + var pageRanges = options.pageRanges || ''; - var paperWidth = 8.5; - var paperHeight = 11; - if (options.format) { - var format = Page.PaperFormats[options.format]; - console.assert(format, 'Unknown paper format: ' + options.format); - paperWidth = format.width; - paperHeight = format.height; - } else { - paperWidth = convertPrintParameterToInches(options.width) || paperWidth; - paperHeight = convertPrintParameterToInches(options.height) || paperHeight; - } - - var marginOptions = options.margin || {}; - var marginTop = convertPrintParameterToInches(marginOptions.top) || 0; - var marginLeft = convertPrintParameterToInches(marginOptions.left) || 0; - var marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0; - var marginRight = convertPrintParameterToInches(marginOptions.right) || 0; - - var result = await this._client.send('Page.printToPDF', { - landscape: landscape, - displayHeaderFooter: displayHeaderFooter, - printBackground: printBackground, - scale: scale, - paperWidth: paperWidth, - paperHeight: paperHeight, - marginTop: marginTop, - marginBottom: marginBottom, - marginLeft: marginLeft, - marginRight: marginRight, - pageRanges: pageRanges - }); - var buffer = new Buffer(result.data, 'base64'); - fs.writeFileSync(filePath, buffer); + var paperWidth = 8.5; + var paperHeight = 11; + if (options.format) { + var format = Page.PaperFormats[options.format]; + console.assert(format, 'Unknown paper format: ' + options.format); + paperWidth = format.width; + paperHeight = format.height; + } else { + paperWidth = convertPrintParameterToInches(options.width) || paperWidth; + paperHeight = convertPrintParameterToInches(options.height) || paperHeight; } - /** + var marginOptions = options.margin || {}; + var marginTop = convertPrintParameterToInches(marginOptions.top) || 0; + var marginLeft = convertPrintParameterToInches(marginOptions.left) || 0; + var marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0; + var marginRight = convertPrintParameterToInches(marginOptions.right) || 0; + + var result = await this._client.send('Page.printToPDF', { + landscape: landscape, + displayHeaderFooter: displayHeaderFooter, + printBackground: printBackground, + scale: scale, + paperWidth: paperWidth, + paperHeight: paperHeight, + marginTop: marginTop, + marginBottom: marginBottom, + marginLeft: marginLeft, + marginRight: marginRight, + pageRanges: pageRanges + }); + var buffer = new Buffer(result.data, 'base64'); + fs.writeFileSync(filePath, buffer); + } + + /** * @return {!Promise} */ - async plainText() { - return this.evaluate(() => document.body.innerText); - } + async plainText() { + return this.evaluate(() => document.body.innerText); + } - /** + /** * @return {!Promise} */ - async title() { - return this.evaluate(() => document.title); - } + async title() { + return this.evaluate(() => document.title); + } - /** + /** * @return {!Promise} */ - async close() { - await this._client.dispose(); - } + async close() { + await this._client.dispose(); + } } /** @enum {string} */ Page.PaperFormats = { - Letter: {width: 8.5, height: 11}, - Legal: {width: 8.5, height: 14}, - Tabloid: {width: 11, height: 17}, - Ledger: {width: 17, height: 11}, - A0: {width: 33.1, height: 46.8 }, - A1: {width: 23.4, height: 33.1 }, - A2: {width: 16.5, height: 23.4 }, - A3: {width: 11.7, height: 16.5 }, - A4: {width: 8.27, height: 11.7 }, - A5: {width: 5.83, height: 8.27 }, + Letter: {width: 8.5, height: 11}, + Legal: {width: 8.5, height: 14}, + Tabloid: {width: 11, height: 17}, + Ledger: {width: 17, height: 11}, + A0: {width: 33.1, height: 46.8 }, + A1: {width: 23.4, height: 33.1 }, + A2: {width: 16.5, height: 23.4 }, + A3: {width: 11.7, height: 16.5 }, + A4: {width: 8.27, height: 11.7 }, + A5: {width: 5.83, height: 8.27 }, }; var unitToPixels = { - 'px': 1, - 'in': 96, - 'cm': 37.8, - 'mm': 3.78 + 'px': 1, + 'in': 96, + 'cm': 37.8, + 'mm': 3.78 }; /** @@ -550,42 +550,42 @@ var unitToPixels = { * @return {(number|undefined)} */ function convertPrintParameterToInches(parameter) { - if (typeof parameter === 'undefined') - return undefined; - var pixels; - if (typeof parameter === 'number') { - // Treat numbers as pixel values to be aligned with phantom's paperSize. - pixels = /** @type {number} */ (parameter); - } else if (typeof parameter === 'string') { - var text = parameter; - var unit = text.substring(text.length - 2).toLowerCase(); - var valueText = ''; - if (unitToPixels.hasOwnProperty(unit)) { - valueText = text.substring(0, text.length - 2); - } else { - // In case of unknown unit try to parse the whole parameter as number of pixels. - // This is consistent with phantom's paperSize behavior. - unit = 'px'; - valueText = text; - } - var value = Number(valueText); - console.assert(!isNaN(value), 'Failed to parse parameter value: ' + text); - pixels = value * unitToPixels[unit]; + if (typeof parameter === 'undefined') + return undefined; + var pixels; + if (typeof parameter === 'number') { + // Treat numbers as pixel values to be aligned with phantom's paperSize. + pixels = /** @type {number} */ (parameter); + } else if (typeof parameter === 'string') { + var text = parameter; + var unit = text.substring(text.length - 2).toLowerCase(); + var valueText = ''; + if (unitToPixels.hasOwnProperty(unit)) { + valueText = text.substring(0, text.length - 2); } else { - throw new Error('printToPDF Cannot handle parameter type: ' + (typeof parameter)); + // In case of unknown unit try to parse the whole parameter as number of pixels. + // This is consistent with phantom's paperSize behavior. + unit = 'px'; + valueText = text; } - return pixels / 96; + var value = Number(valueText); + console.assert(!isNaN(value), 'Failed to parse parameter value: ' + text); + pixels = value * unitToPixels[unit]; + } else { + throw new Error('printToPDF Cannot handle parameter type: ' + (typeof parameter)); + } + return pixels / 96; } Page.Events = { - ConsoleMessage: 'consolemessage', - Dialog: 'dialog', - Error: 'error', - ResourceLoadingFailed: 'resourceloadingfailed', - ResponseReceived: 'responsereceived', - FrameAttached: 'frameattached', - FrameDetached: 'framedetached', - FrameNavigated: 'framenavigated', + ConsoleMessage: 'consolemessage', + Dialog: 'dialog', + Error: 'error', + ResourceLoadingFailed: 'resourceloadingfailed', + ResponseReceived: 'responsereceived', + FrameAttached: 'frameattached', + FrameDetached: 'framedetached', + FrameNavigated: 'framenavigated', }; module.exports = Page; diff --git a/lib/Request.js b/lib/Request.js index f8797a9dcba..6f663dbfeaf 100644 --- a/lib/Request.js +++ b/lib/Request.js @@ -15,112 +15,112 @@ */ class Request { - /** + /** * @param {!Connection} client * @param {string} interceptionId * @param {!Object} payload */ - constructor(client, interceptionId, payload) { - this._client = client; - this._interceptionId = interceptionId; - this._url = payload.url; - this._method = payload.method; - this._headers = payload.headers; - this._postData = payload.postData; + constructor(client, interceptionId, payload) { + this._client = client; + this._interceptionId = interceptionId; + this._url = payload.url; + this._method = payload.method; + this._headers = payload.headers; + this._postData = payload.postData; - this._urlOverride = undefined; - this._methodOverride = undefined; - this._postDataOverride = undefined; + this._urlOverride = undefined; + this._methodOverride = undefined; + this._postDataOverride = undefined; - this._handled = false; - } + this._handled = false; + } - /** + /** * @return {string} */ - url() { - return this._urlOverride || this._url; - } + url() { + return this._urlOverride || this._url; + } - /** + /** * @param {string} url */ - setUrl(url) { - this._urlOverride = url; - } + setUrl(url) { + this._urlOverride = url; + } - /** + /** * @return {string} */ - method() { - return this._methodOverride || this._method; - } + method() { + return this._methodOverride || this._method; + } - /** + /** * @param {string} method */ - setMethod(method) { - this._methodOverride = method; - } + setMethod(method) { + this._methodOverride = method; + } - /** + /** * @return {!Object} */ - headers() { - return Object.assign({}, this._headersOverride || this._headers); - } + headers() { + return Object.assign({}, this._headersOverride || this._headers); + } - /** + /** * @param {string} key * @param {string} value */ - setHeader(key, value) { - if (!this._headersOverride) - this._headersOverride = Object.assign({}, this._headers); - this._headersOverride[key] = value; - } + setHeader(key, value) { + if (!this._headersOverride) + this._headersOverride = Object.assign({}, this._headers); + this._headersOverride[key] = value; + } - /** + /** * @return {(string|undefined)} */ - postData() { - return this._postDataOverride || this._postData; - } + postData() { + return this._postDataOverride || this._postData; + } - /** + /** * @return {(string|undefined)} */ - setPostData(data) { - this._postDataOverride = data; - } + setPostData(data) { + this._postDataOverride = data; + } - abort() { - console.assert(!this._handled, 'This request is already handled!'); - this._handled = true; - this._client.send('Network.continueInterceptedRequest', { - interceptionId: this._interceptionId, - errorReason: 'Aborted' - }); - } + abort() { + console.assert(!this._handled, 'This request is already handled!'); + this._handled = true; + this._client.send('Network.continueInterceptedRequest', { + interceptionId: this._interceptionId, + errorReason: 'Aborted' + }); + } - continue() { - console.assert(!this._handled, 'This request is already handled!'); - this._handled = true; - this._client.send('Network.continueInterceptedRequest', { - interceptionId: this._interceptionId, - url: this._urlOverride, - method: this._methodOverride, - postData: this._postDataOverride, - headers: this._headersOverride - }); - } + continue() { + console.assert(!this._handled, 'This request is already handled!'); + this._handled = true; + this._client.send('Network.continueInterceptedRequest', { + interceptionId: this._interceptionId, + url: this._urlOverride, + method: this._methodOverride, + postData: this._postDataOverride, + headers: this._headersOverride + }); + } - /** + /** * @return {boolean} */ - handled() { - return this._handled; - } + handled() { + return this._handled; + } } module.exports = Request; diff --git a/phantom_shim/FileSystem.js b/phantom_shim/FileSystem.js index f25cf7f72a7..6ddda7877b3 100644 --- a/phantom_shim/FileSystem.js +++ b/phantom_shim/FileSystem.js @@ -21,355 +21,355 @@ var removeRecursive = require('rimraf').sync; var copyRecursive = deasync(require('ncp').ncp); class FileSystem { - constructor() { - this.separator = path.sep; - } + constructor() { + this.separator = path.sep; + } - /** + /** * @return {string} */ - get workingDirectory() { - return process.cwd(); - } + get workingDirectory() { + return process.cwd(); + } - /** + /** * @param {string} directoryPath */ - changeWorkingDirectory(directoryPath) { - try { - process.chdir(directoryPath); - return true; - } catch (e){ - return false; - } + changeWorkingDirectory(directoryPath) { + try { + process.chdir(directoryPath); + return true; + } catch (e){ + return false; } + } - /** + /** * @param {string} relativePath * @return {string} */ - absolute(relativePath) { - relativePath = path.normalize(relativePath); - if (path.isAbsolute(relativePath)) - return relativePath; - return path.resolve(path.join(process.cwd(), relativePath)); - } + absolute(relativePath) { + relativePath = path.normalize(relativePath); + if (path.isAbsolute(relativePath)) + return relativePath; + return path.resolve(path.join(process.cwd(), relativePath)); + } - /** + /** * @param {string} filePath * @return {boolean} */ - exists(filePath) { - return fs.existsSync(filePath); - } + exists(filePath) { + return fs.existsSync(filePath); + } - /** + /** * @param {string} fromPath * @param {string} toPath */ - copy(fromPath, toPath) { - var content = fs.readFileSync(fromPath); - fs.writeFileSync(toPath, content); - } + copy(fromPath, toPath) { + var content = fs.readFileSync(fromPath); + fs.writeFileSync(toPath, content); + } - /** + /** * @param {string} fromPath * @param {string} toPath */ - move(fromPath, toPath) { - var content = fs.readFileSync(fromPath); - fs.writeFileSync(toPath, content); - fs.unlinkSync(fromPath); - } + move(fromPath, toPath) { + var content = fs.readFileSync(fromPath); + fs.writeFileSync(toPath, content); + fs.unlinkSync(fromPath); + } - /** + /** * @param {string} filePath * @return {number} */ - size(filePath) { - return fs.statSync(filePath).size; - } + size(filePath) { + return fs.statSync(filePath).size; + } - /** + /** * @param {string} filePath */ - touch(filePath) { - fs.closeSync(fs.openSync(filePath, 'a')); - } + touch(filePath) { + fs.closeSync(fs.openSync(filePath, 'a')); + } - /** + /** * @param {string} filePath */ - remove(filePath) { - fs.unlinkSync(filePath); - } + remove(filePath) { + fs.unlinkSync(filePath); + } - /** + /** * @param {string} filePath * @return {boolean} */ - lastModified(filePath) { - return fs.statSync(filePath).mtime; - } + lastModified(filePath) { + return fs.statSync(filePath).mtime; + } - /** + /** * @param {string} dirPath * @return {boolean} */ - makeDirectory(dirPath) { - try { - fs.mkdirSync(dirPath); - return true; - } catch (e) { - return false; - } + makeDirectory(dirPath) { + try { + fs.mkdirSync(dirPath); + return true; + } catch (e) { + return false; } + } - /** + /** * @param {string} dirPath * @return {boolean} */ - makeTree(dirPath) { - return this.makeDirectory(dirPath); - } + makeTree(dirPath) { + return this.makeDirectory(dirPath); + } - /** + /** * @param {string} dirPath */ - removeTree(dirPath) { - removeRecursive(dirPath); - } + removeTree(dirPath) { + removeRecursive(dirPath); + } - /** + /** * @param {string} fromPath * @param {string} toPath */ - copyTree(fromPath, toPath) { - copyRecursive(fromPath, toPath); - } + copyTree(fromPath, toPath) { + copyRecursive(fromPath, toPath); + } - /** + /** * @param {string} dirPath * @return {!Array} */ - list(dirPath) { - return fs.readdirSync(dirPath); - } + list(dirPath) { + return fs.readdirSync(dirPath); + } - /** + /** * @param {string} linkPath * @return {string} */ - readLink(linkPath) { - return fs.readlinkSync(linkPath); - } + readLink(linkPath) { + return fs.readlinkSync(linkPath); + } - /** + /** * @param {string} filePath * @param {Object} data * @param {string} mode */ - write(filePath, data, mode) { - var fd = new FileDescriptor(filePath, mode, 'utf8'); - fd.write(data); - fd.close(); - } + write(filePath, data, mode) { + var fd = new FileDescriptor(filePath, mode, 'utf8'); + fd.write(data); + fd.close(); + } - /** + /** * @param {string} somePath * @return {boolean} */ - isAbsolute(somePath) { - return path.isAbsolute(somePath); - } + isAbsolute(somePath) { + return path.isAbsolute(somePath); + } - /** + /** * @return {string} */ - read(filePath) { - return fs.readFileSync(filePath, 'utf8'); - } + read(filePath) { + return fs.readFileSync(filePath, 'utf8'); + } - /** + /** * @param {string} filePath * @return {boolean} */ - isFile(filePath) { - return fs.existsSync(filePath) && fs.lstatSync(filePath).isFile(); - } + isFile(filePath) { + return fs.existsSync(filePath) && fs.lstatSync(filePath).isFile(); + } - /** + /** * @param {string} dirPath * @return {boolean} */ - isDirectory(dirPath) { - return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory(); - } + isDirectory(dirPath) { + return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory(); + } - /** + /** * @param {string} filePath * @return {boolean} */ - isLink(filePath) { - return fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink(); - } + isLink(filePath) { + return fs.existsSync(filePath) && fs.lstatSync(filePath).isSymbolicLink(); + } - /** + /** * @param {string} filePath * @return {boolean} */ - isReadable(filePath) { - try { - fs.accessSync(filePath, fs.constants.R_OK); - return true; - } catch (e) { - return false; - } + isReadable(filePath) { + try { + fs.accessSync(filePath, fs.constants.R_OK); + return true; + } catch (e) { + return false; } + } - /** + /** * @param {string} filePath * @return {boolean} */ - isWritable(filePath) { - try { - fs.accessSync(filePath, fs.constants.W_OK); - return true; - } catch (e) { - return false; - } + isWritable(filePath) { + try { + fs.accessSync(filePath, fs.constants.W_OK); + return true; + } catch (e) { + return false; } + } - /** + /** * @param {string} filePath * @return {boolean} */ - isExecutable(filePath) { - try { - fs.accessSync(filePath, fs.constants.X_OK); - return true; - } catch (e) { - return false; - } + isExecutable(filePath) { + try { + fs.accessSync(filePath, fs.constants.X_OK); + return true; + } catch (e) { + return false; } + } - /** + /** * @param {string} somePath * @return {!Array} */ - split(somePath) { - somePath = path.normalize(somePath); - if (somePath.endsWith(path.sep)) - somePath = somePath.substring(0, somePath.length - path.sep.length); - return somePath.split(path.sep); - } + split(somePath) { + somePath = path.normalize(somePath); + if (somePath.endsWith(path.sep)) + somePath = somePath.substring(0, somePath.length - path.sep.length); + return somePath.split(path.sep); + } - /** + /** * @param {string} path1 * @param {string} path2 * @return {string} */ - join(...args) { - if (args[0] === '' && args.length > 1) - args[0] = path.sep; - args = args.filter(part => typeof part === 'string'); - return path.join.apply(path, args); - } + join(...args) { + if (args[0] === '' && args.length > 1) + args[0] = path.sep; + args = args.filter(part => typeof part === 'string'); + return path.join.apply(path, args); + } - /** + /** * @param {string} filePath * @param {(string|!Object)} option * @return {!FileDescriptor} */ - open(filePath, option) { - if (typeof option === 'string') - return new FileDescriptor(filePath, option); - return new FileDescriptor(filePath, option.mode); - } + open(filePath, option) { + if (typeof option === 'string') + return new FileDescriptor(filePath, option); + return new FileDescriptor(filePath, option.mode); + } } var fdwrite = deasync(fs.write); var fdread = deasync(fs.read); class FileDescriptor { - /** + /** * @param {string} filePath * @param {string} mode */ - constructor(filePath, mode) { - this._position = 0; - this._encoding = 'utf8'; - if (mode === 'rb') { - this._mode = 'r'; - this._encoding = 'latin1'; - } else if (mode === 'wb' || mode === 'b') { - this._mode = 'w'; - this._encoding = 'latin1'; - } else if (mode === 'rw+') { - this._mode = 'a+'; - this._position = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0; - } else { - this._mode = mode; - } - this._fd = fs.openSync(filePath, this._mode); + constructor(filePath, mode) { + this._position = 0; + this._encoding = 'utf8'; + if (mode === 'rb') { + this._mode = 'r'; + this._encoding = 'latin1'; + } else if (mode === 'wb' || mode === 'b') { + this._mode = 'w'; + this._encoding = 'latin1'; + } else if (mode === 'rw+') { + this._mode = 'a+'; + this._position = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0; + } else { + this._mode = mode; } + this._fd = fs.openSync(filePath, this._mode); + } - /** + /** * @param {string} data */ - write(data) { - var buffer = Buffer.from(data, this._encoding); - var written = fdwrite(this._fd, buffer, 0, buffer.length, this._position); - this._position += written; - } + write(data) { + var buffer = Buffer.from(data, this._encoding); + var written = fdwrite(this._fd, buffer, 0, buffer.length, this._position); + this._position += written; + } - getEncoding() { - return 'UTF-8'; - } + getEncoding() { + return 'UTF-8'; + } - /** + /** * @param {string} data */ - writeLine(data) { - this.write(data + '\n'); - } + writeLine(data) { + this.write(data + '\n'); + } - /** + /** * @param {number=} size * @return {string} */ - read(size) { - var position = this._position; - if (!size) { - size = fs.fstatSync(this._fd).size; - position = 0; - } - var buffer = new Buffer(size); - var bytesRead = fdread(this._fd, buffer, 0, size, position); - this._position += bytesRead; - return buffer.toString(this._encoding); + read(size) { + var position = this._position; + if (!size) { + size = fs.fstatSync(this._fd).size; + position = 0; } + var buffer = new Buffer(size); + var bytesRead = fdread(this._fd, buffer, 0, size, position); + this._position += bytesRead; + return buffer.toString(this._encoding); + } - flush() { - // noop. - } + flush() { + // noop. + } - /** + /** * @param {number} position */ - seek(position) { - this._position = position; - } + seek(position) { + this._position = position; + } - close() { - fs.closeSync(this._fd); - } + close() { + fs.closeSync(this._fd); + } - /** + /** * @return {boolean} */ - atEnd() { - } + atEnd() { + } } module.exports = FileSystem; diff --git a/phantom_shim/Phantom.js b/phantom_shim/Phantom.js index e1fdec631dc..fa7c9165632 100644 --- a/phantom_shim/Phantom.js +++ b/phantom_shim/Phantom.js @@ -24,116 +24,116 @@ var url = require('url'); * @param {string} scriptPath */ module.exports.create = function(context, scriptPath) { - var phantom = { - page: { - onConsoleMessage: null, - }, + var phantom = { + page: { + onConsoleMessage: null, + }, - /** + /** * @param {string} relative * @param {string} base * @return {string} */ - resolveRelativeUrl: function(relative, base) { - return url.resolve(base, relative); - }, + resolveRelativeUrl: function(relative, base) { + return url.resolve(base, relative); + }, - /** + /** * @param {string} url * @return {string} */ - fullyDecodeUrl: function(url) { - return decodeURI(url); - }, + fullyDecodeUrl: function(url) { + return decodeURI(url); + }, - libraryPath: path.dirname(scriptPath), + libraryPath: path.dirname(scriptPath), - onError: null, + onError: null, - /** + /** * @return {string} */ - get outputEncoding() { - return 'UTF-8'; - }, + get outputEncoding() { + return 'UTF-8'; + }, - /** + /** * @param {string} value */ - set outputEncoding(value) { - throw new Error('Phantom.outputEncoding setter is not implemented'); - }, + set outputEncoding(value) { + throw new Error('Phantom.outputEncoding setter is not implemented'); + }, - /** + /** * @return {boolean} */ - get cookiesEnabled() { - return true; - }, + get cookiesEnabled() { + return true; + }, - /** + /** * @param {boolean} value */ - set cookiesEnabled(value) { - throw new Error('Phantom.cookiesEnabled setter is not implemented'); - }, + set cookiesEnabled(value) { + throw new Error('Phantom.cookiesEnabled setter is not implemented'); + }, - /** + /** * @return {!{major: number, minor: number, patch: number}} */ - get version() { - var versionParts = require('../package.json').version.split('.'); - return { - major: parseInt(versionParts[0], 10), - minor: parseInt(versionParts[1], 10), - patch: parseInt(versionParts[2], 10), - }; - }, + get version() { + var versionParts = require('../package.json').version.split('.'); + return { + major: parseInt(versionParts[0], 10), + minor: parseInt(versionParts[1], 10), + patch: parseInt(versionParts[2], 10), + }; + }, - /** + /** * @param {number=} code */ - exit: function(code) { - process.exit(code); - }, + exit: function(code) { + process.exit(code); + }, - /** + /** * @param {string} filePath * @return {boolean} */ - injectJs: function(filePath) { - filePath = path.resolve(phantom.libraryPath, filePath); - if (!fs.existsSync(filePath)) - return false; - var code = fs.readFileSync(filePath, 'utf8'); - if (code.startsWith('#!')) - code = code.substring(code.indexOf('\n')); - vm.runInContext(code, context, { - filename: filePath, - displayErrors: true - }); - return true; - }, + injectJs: function(filePath) { + filePath = path.resolve(phantom.libraryPath, filePath); + if (!fs.existsSync(filePath)) + return false; + var code = fs.readFileSync(filePath, 'utf8'); + if (code.startsWith('#!')) + code = code.substring(code.indexOf('\n')); + vm.runInContext(code, context, { + filename: filePath, + displayErrors: true + }); + return true; + }, - /** + /** * @param {string} moduleSource * @param {string} filename */ - loadModule: function(moduleSource, filename) { - var code = [ - '(function(require, exports, module) {\n', - moduleSource, - '\n}.call({},', - 'require.cache[\'' + filename + '\']._getRequire(),', - 'require.cache[\'' + filename + '\'].exports,', - 'require.cache[\'' + filename + '\']', - '));' - ].join(''); - vm.runInContext(code, context, { - filename: filename, - displayErrors: true - }); - }, - }; - return phantom; + loadModule: function(moduleSource, filename) { + var code = [ + '(function(require, exports, module) {\n', + moduleSource, + '\n}.call({},', + 'require.cache[\'' + filename + '\']._getRequire(),', + 'require.cache[\'' + filename + '\'].exports,', + 'require.cache[\'' + filename + '\']', + '));' + ].join(''); + vm.runInContext(code, context, { + filename: filename, + displayErrors: true + }); + }, + }; + return phantom; }; diff --git a/phantom_shim/System.js b/phantom_shim/System.js index c6187267991..88c49dc12d2 100644 --- a/phantom_shim/System.js +++ b/phantom_shim/System.js @@ -19,100 +19,100 @@ var await = require('./utilities').await; var os = require('os'); class System { - /** + /** * @param {!Array} args */ - constructor(args) { - this.args = args; - this.env = {}; - Object.assign(this.env, process.env); - this.stdin = new StandardInput(process.stdin); - this.stdout = new StandardOutput(process.stdout); - this.stderr = new StandardOutput(process.stderr); - this.platform = 'phantomjs'; - this.pid = process.pid; - this.isSSLSupported = false; - this.os = { - architecture: os.arch(), - name: os.type(), - version: os.release() - }; - } + constructor(args) { + this.args = args; + this.env = {}; + Object.assign(this.env, process.env); + this.stdin = new StandardInput(process.stdin); + this.stdout = new StandardOutput(process.stdout); + this.stderr = new StandardOutput(process.stderr); + this.platform = 'phantomjs'; + this.pid = process.pid; + this.isSSLSupported = false; + this.os = { + architecture: os.arch(), + name: os.type(), + version: os.release() + }; + } } class StandardInput { - /** + /** * @param {!Readable} readableStream */ - constructor(readableStream) { - this._readline = readline.createInterface({ - input: readableStream - }); - this._lines = []; - this._closed = false; - this._readline.on('line', line => this._lines.push(line)); - this._readline.on('close', () => this._closed = true); - } + constructor(readableStream) { + this._readline = readline.createInterface({ + input: readableStream + }); + this._lines = []; + this._closed = false; + this._readline.on('line', line => this._lines.push(line)); + this._readline.on('close', () => this._closed = true); + } - /** + /** * @return {string} */ - readLine() { - if (this._closed && !this._lines.length) - return ''; - if (!this._lines.length) { - var linePromise = new Promise(fulfill => this._readline.once('line', fulfill)); - await(linePromise); - } - return this._lines.shift(); + readLine() { + if (this._closed && !this._lines.length) + return ''; + if (!this._lines.length) { + var linePromise = new Promise(fulfill => this._readline.once('line', fulfill)); + await(linePromise); } + return this._lines.shift(); + } - /** + /** * @return {string} */ - read() { - if (!this._closed) { - var closePromise = new Promise(fulfill => this._readline.once('close', fulfill)); - await(closePromise); - } - var text = this._lines.join('\n'); - this._lines = []; - return text; + read() { + if (!this._closed) { + var closePromise = new Promise(fulfill => this._readline.once('close', fulfill)); + await(closePromise); } + var text = this._lines.join('\n'); + this._lines = []; + return text; + } - close() { - this._readline.close(); - } + close() { + this._readline.close(); + } } class StandardOutput { - /** + /** * @param {!Writable} writableStream */ - constructor(writableStream) { - this._stream = writableStream; - } + constructor(writableStream) { + this._stream = writableStream; + } - /** + /** * @param {string} data */ - write(data) { - this._stream.write(data); - } + write(data) { + this._stream.write(data); + } - /** + /** * @param {string} data */ - writeLine(data) { - this._stream.write(data + '\n'); - } + writeLine(data) { + this._stream.write(data + '\n'); + } - flush() { - } + flush() { + } - close() { - this._stream.end(); - } + close() { + this._stream.end(); + } } module.exports = System; diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index 6d07a5a5483..31cbcbc50a3 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -23,319 +23,319 @@ var PageEvents = require('../lib/Page').Events; var noop = function() { }; class WebPage { - /** + /** * @param {!Browser} browser * @param {string} scriptPath * @param {!Object=} options */ - constructor(browser, scriptPath, options) { - this._page = await(browser.newPage()); - this.settings = new WebPageSettings(this._page); + constructor(browser, scriptPath, options) { + this._page = await(browser.newPage()); + this.settings = new WebPageSettings(this._page); - options = options || {}; - options.settings = options.settings || {}; - if (options.settings.userAgent) - this.settings.userAgent = options.settings.userAgent; - if (options.viewportSize) - await(this._page.setViewportSize(options.viewportSize)); + options = options || {}; + options.settings = options.settings || {}; + if (options.settings.userAgent) + this.settings.userAgent = options.settings.userAgent; + if (options.viewportSize) + await(this._page.setViewportSize(options.viewportSize)); - this.clipRect = options.clipRect || {left: 0, top: 0, width: 0, height: 0}; - this.onConsoleMessage = null; - this.onLoadFinished = null; - this.onResourceError = null; - this.onResourceReceived = null; - this.onInitialized = null; - this._deferEvaluate = false; + this.clipRect = options.clipRect || {left: 0, top: 0, width: 0, height: 0}; + this.onConsoleMessage = null; + this.onLoadFinished = null; + this.onResourceError = null; + this.onResourceReceived = null; + this.onInitialized = null; + this._deferEvaluate = false; - this.libraryPath = path.dirname(scriptPath); + this.libraryPath = path.dirname(scriptPath); - this._onResourceRequestedCallback = undefined; - this._onConfirmCallback = undefined; - this._onAlertCallback = undefined; - this._onError = noop; + this._onResourceRequestedCallback = undefined; + this._onConfirmCallback = undefined; + this._onAlertCallback = undefined; + this._onError = noop; - this._pageEvents = new AsyncEmitter(this._page); - this._pageEvents.on(PageEvents.ResponseReceived, response => this._onResponseReceived(response)); - this._pageEvents.on(PageEvents.ResourceLoadingFailed, event => (this.onResourceError || noop).call(null, event)); - this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg)); - this._pageEvents.on(PageEvents.Confirm, message => this._onConfirm(message)); - this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message)); - this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog)); - this._pageEvents.on(PageEvents.Error, error => (this._onError || noop).call(null, error.message, error.stack)); - } + this._pageEvents = new AsyncEmitter(this._page); + this._pageEvents.on(PageEvents.ResponseReceived, response => this._onResponseReceived(response)); + this._pageEvents.on(PageEvents.ResourceLoadingFailed, event => (this.onResourceError || noop).call(null, event)); + this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg)); + this._pageEvents.on(PageEvents.Confirm, message => this._onConfirm(message)); + this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message)); + this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog)); + this._pageEvents.on(PageEvents.Error, error => (this._onError || noop).call(null, error.message, error.stack)); + } - /** + /** * @return {?function(!Object, !Request)} */ - get onResourceRequested() { - return this._onResourceRequestedCallback; - } + get onResourceRequested() { + return this._onResourceRequestedCallback; + } - /** + /** * @return {?function(!Object, !Request)} callback */ - set onResourceRequested(callback) { - this._onResourceRequestedCallback = callback; - this._page.setRequestInterceptor(callback ? resourceInterceptor : null); - - /** - * @param {!Request} request - */ - function resourceInterceptor(request) { - var requestData = { - url: request.url(), - headers: request.headers() - }; - callback(requestData, request); - if (!request.handled()) - request.continue(); - } - } - - _onResponseReceived(response) { - if (!this.onResourceReceived) - return; - var headers = []; - for (var key in response.headers) { - headers.push({ - name: key, - value: response.headers[key] - }); - } - response.headers = headers; - this.onResourceReceived.call(null, response); - } + set onResourceRequested(callback) { + this._onResourceRequestedCallback = callback; + this._page.setRequestInterceptor(callback ? resourceInterceptor : null); /** + * @param {!Request} request + */ + function resourceInterceptor(request) { + var requestData = { + url: request.url(), + headers: request.headers() + }; + callback(requestData, request); + if (!request.handled()) + request.continue(); + } + } + + _onResponseReceived(response) { + if (!this.onResourceReceived) + return; + var headers = []; + for (var key in response.headers) { + headers.push({ + name: key, + value: response.headers[key] + }); + } + response.headers = headers; + this.onResourceReceived.call(null, response); + } + + /** * @param {string} url * @param {function()} callback */ - includeJs(url, callback) { - this._page.addScriptTag(url).then(callback); - } + includeJs(url, callback) { + this._page.addScriptTag(url).then(callback); + } - /** + /** * @return {!{width: number, height: number}} */ - get viewportSize() { - return this._page.viewportSize(); - } + get viewportSize() { + return this._page.viewportSize(); + } - /** + /** * @return {!Object} */ - get customHeaders() { - return this._page.extraHTTPHeaders(); - } + get customHeaders() { + return this._page.extraHTTPHeaders(); + } - /** + /** * @param {!Object} value */ - set customHeaders(value) { - await(this._page.setExtraHTTPHeaders(value)); - } + set customHeaders(value) { + await(this._page.setExtraHTTPHeaders(value)); + } - /** + /** * @param {string} filePath */ - injectJs(filePath) { - if (!fs.existsSync(filePath)) - filePath = path.resolve(this.libraryPath, filePath); - if (!fs.existsSync(filePath)) - return false; - await(this._page.injectFile(filePath)); - return true; - } + injectJs(filePath) { + if (!fs.existsSync(filePath)) + filePath = path.resolve(this.libraryPath, filePath); + if (!fs.existsSync(filePath)) + return false; + await(this._page.injectFile(filePath)); + return true; + } - /** + /** * @return {string} */ - get plainText() { - return await(this._page.plainText()); - } + get plainText() { + return await(this._page.plainText()); + } - /** + /** * @return {string} */ - get title() { - return await(this._page.title()); - } + get title() { + return await(this._page.title()); + } - /** + /** * @return {(function()|undefined)} */ - get onError() { - return this._onError; - } + get onError() { + return this._onError; + } - /** + /** * @param {(function()|undefined)} handler */ - set onError(handler) { - if (typeof handler !== 'function') - handler = undefined; - this._onError = handler; - } + set onError(handler) { + if (typeof handler !== 'function') + handler = undefined; + this._onError = handler; + } - /** + /** * @return {(function()|undefined)} */ - get onConfirm() { - return this._onConfirmCallback; - } + get onConfirm() { + return this._onConfirmCallback; + } - /** + /** * @param {function()} handler */ - set onConfirm(handler) { - if (typeof handler !== 'function') - handler = undefined; - this._onConfirmCallback = handler; - } + set onConfirm(handler) { + if (typeof handler !== 'function') + handler = undefined; + this._onConfirmCallback = handler; + } - /** + /** * @return {(function()|undefined)} */ - get onAlert() { - return this._onAlertCallback; - } + get onAlert() { + return this._onAlertCallback; + } - /** + /** * @param {function()} handler */ - set onAlert(handler) { - if (typeof handler !== 'function') - handler = undefined; - this._onAlertCallback = handler; - } + set onAlert(handler) { + if (typeof handler !== 'function') + handler = undefined; + this._onAlertCallback = handler; + } - /** + /** * @param {!Dialog} dialog */ - _onDialog(dialog) { - if (dialog.type === 'alert' && this._onAlertCallback) { - this._onAlertCallback.call(null, dialog.message()); - await(dialog.accept()); - } else if (dialog.type === 'confirm' && this._onConfirmCallback) { - var result = this._onConfirmCallback.call(null, dialog.message()); - await(result ? dialog.accept() : dialog.dismiss()); - } + _onDialog(dialog) { + if (dialog.type === 'alert' && this._onAlertCallback) { + this._onAlertCallback.call(null, dialog.message()); + await(dialog.accept()); + } else if (dialog.type === 'confirm' && this._onConfirmCallback) { + var result = this._onConfirmCallback.call(null, dialog.message()); + await(result ? dialog.accept() : dialog.dismiss()); } + } - /** + /** * @return {string} */ - get url() { - return await(this._page.url()); - } + get url() { + return await(this._page.url()); + } - /** + /** * @param {string} html */ - set content(html) { - await(this._page.setContent(html)); - } + set content(html) { + await(this._page.setContent(html)); + } - /** + /** * @param {string} html * @param {function()=} callback */ - open(url, callback) { - console.assert(arguments.length <= 2, 'WebPage.open does not support METHOD and DATA arguments'); - this._deferEvaluate = true; - if (typeof this.onInitialized === 'function') - this.onInitialized(); - this._deferEvaluate = false; - this._page.navigate(url).then(result => { - var status = result ? 'success' : 'fail'; - if (!result) { - this.onResourceError.call(null, { - url, - errorString: 'SSL handshake failed' - }); - } - if (this.onLoadFinished) - this.onLoadFinished.call(null, status); - if (callback) - callback.call(null, status); + open(url, callback) { + console.assert(arguments.length <= 2, 'WebPage.open does not support METHOD and DATA arguments'); + this._deferEvaluate = true; + if (typeof this.onInitialized === 'function') + this.onInitialized(); + this._deferEvaluate = false; + this._page.navigate(url).then(result => { + var status = result ? 'success' : 'fail'; + if (!result) { + this.onResourceError.call(null, { + url, + errorString: 'SSL handshake failed' }); - } + } + if (this.onLoadFinished) + this.onLoadFinished.call(null, status); + if (callback) + callback.call(null, status); + }); + } - /** + /** * @param {!{width: number, height: number}} options */ - set viewportSize(options) { - await(this._page.setViewportSize(options)); - } + set viewportSize(options) { + await(this._page.setViewportSize(options)); + } - /** + /** * @param {function()} fun * @param {!Array} args */ - evaluate(fun, ...args) { - if (this._deferEvaluate) - return await(this._page.evaluateOnInitialized(fun, ...args)); - return await(this._page.evaluate(fun, ...args)); - } + evaluate(fun, ...args) { + if (this._deferEvaluate) + return await(this._page.evaluateOnInitialized(fun, ...args)); + return await(this._page.evaluate(fun, ...args)); + } - /** + /** * {string} fileName */ - render(fileName) { - if (fileName.endsWith('pdf')) { - var options = {}; - var paperSize = this.paperSize || {}; - options.margin = paperSize.margin; - options.format = paperSize.format; - options.landscape = paperSize.orientation === 'landscape'; - options.width = paperSize.width; - options.height = paperSize.height; - await(this._page.printToPDF(fileName, options)); - } else { - var options = {}; - if (this.clipRect && (this.clipRect.left || this.clipRect.top || this.clipRect.width || this.clipRect.height)) { - options.clip = { - x: this.clipRect.left, - y: this.clipRect.top, - width: this.clipRect.width, - height: this.clipRect.height - }; - } - options.path = fileName; - await(this._page.screenshot(options)); - } + render(fileName) { + if (fileName.endsWith('pdf')) { + var options = {}; + var paperSize = this.paperSize || {}; + options.margin = paperSize.margin; + options.format = paperSize.format; + options.landscape = paperSize.orientation === 'landscape'; + options.width = paperSize.width; + options.height = paperSize.height; + await(this._page.printToPDF(fileName, options)); + } else { + var options = {}; + if (this.clipRect && (this.clipRect.left || this.clipRect.top || this.clipRect.width || this.clipRect.height)) { + options.clip = { + x: this.clipRect.left, + y: this.clipRect.top, + width: this.clipRect.width, + height: this.clipRect.height + }; + } + options.path = fileName; + await(this._page.screenshot(options)); } + } - release() { - this._page.close(); - } + release() { + this._page.close(); + } - close() { - this._page.close(); - } + close() { + this._page.close(); + } } class WebPageSettings { - /** + /** * @param {!Page} page */ - constructor(page) { - this._page = page; - } + constructor(page) { + this._page = page; + } - /** + /** * @param {string} value */ - set userAgent(value) { - await(this._page.setUserAgentOverride(value)); - } + set userAgent(value) { + await(this._page.setUserAgentOverride(value)); + } - /** + /** * @return {string} */ - get userAgent() { - return this._page.userAgentOverride(); - } + get userAgent() { + return this._page.userAgentOverride(); + } } // To prevent reenterability, eventemitters should emit events @@ -349,29 +349,29 @@ class WebPageSettings { // This class is a wrapper around EventEmitter which re-emits events asynchronously, // helping to overcome the issue. class AsyncEmitter extends EventEmitter { - /** + /** * @param {!Page} page */ - constructor(page) { - super(); - this._page = page; - this._symbol = Symbol('AsyncEmitter'); - this.on('newListener', this._onListenerAdded); - this.on('removeListener', this._onListenerRemoved); - } + constructor(page) { + super(); + this._page = page; + this._symbol = Symbol('AsyncEmitter'); + this.on('newListener', this._onListenerAdded); + this.on('removeListener', this._onListenerRemoved); + } - _onListenerAdded(event, listener) { - // Async listener calls original listener on next tick. - var asyncListener = (...args) => { - process.nextTick(() => listener.apply(null, args)); - }; - listener[this._symbol] = asyncListener; - this._page.on(event, asyncListener); - } + _onListenerAdded(event, listener) { + // Async listener calls original listener on next tick. + var asyncListener = (...args) => { + process.nextTick(() => listener.apply(null, args)); + }; + listener[this._symbol] = asyncListener; + this._page.on(event, asyncListener); + } - _onListenerRemoved(event, listener) { - this._page.removeListener(event, listener[this._symbol]); - } + _onListenerRemoved(event, listener) { + this._page.removeListener(event, listener[this._symbol]); + } } module.exports = WebPage; diff --git a/phantom_shim/WebServer.js b/phantom_shim/WebServer.js index 320243f46d6..ac95d396bf9 100644 --- a/phantom_shim/WebServer.js +++ b/phantom_shim/WebServer.js @@ -18,66 +18,66 @@ var http = require('http'); var await = require('./utilities').await; class WebServer { - constructor() { - this._server = http.createServer(); - this.objectName = 'WebServer'; - this.listenOnPort = this.listen; - this.newRequest = function(req, res) { }; - Object.defineProperty(this, 'port', { - get: () => { - if (!this._server.listening) - return ''; - return this._server.address().port + ''; - }, - enumerable: true, - configurable: false - }); - } + constructor() { + this._server = http.createServer(); + this.objectName = 'WebServer'; + this.listenOnPort = this.listen; + this.newRequest = function(req, res) { }; + Object.defineProperty(this, 'port', { + get: () => { + if (!this._server.listening) + return ''; + return this._server.address().port + ''; + }, + enumerable: true, + configurable: false + }); + } - close() { - this._server.close(); - } + close() { + this._server.close(); + } - /** + /** * @param {nubmer} port * @return {boolean} */ - listen(port, callback) { - if (this._server.listening) - return false; - this.newRequest = callback; - this._server.listen(port); - var errorPromise = new Promise(x => this._server.once('error', x)); - var successPromise = new Promise(x => this._server.once('listening', x)); - await(Promise.race([errorPromise, successPromise])); - if (!this._server.listening) - return false; + listen(port, callback) { + if (this._server.listening) + return false; + this.newRequest = callback; + this._server.listen(port); + var errorPromise = new Promise(x => this._server.once('error', x)); + var successPromise = new Promise(x => this._server.once('listening', x)); + await(Promise.race([errorPromise, successPromise])); + if (!this._server.listening) + return false; - this._server.on('request', (req, res) => { - res.close = res.end.bind(res); - var headers = res.getHeaders(); - res.headers = []; - for (var key in headers) { - res.headers.push({ - name: key, - value: headers[key] - }); - } - res.header = res.getHeader; - res.setHeaders = headers => { - for (var key in headers) - res.setHeader(key, headers[key]); - }; - Object.defineProperty(res, 'statusCode', { - enumerable: true, - configurable: true, - writable: true, - value: res.statusCode - }); - this.newRequest.call(null, req, res); + this._server.on('request', (req, res) => { + res.close = res.end.bind(res); + var headers = res.getHeaders(); + res.headers = []; + for (var key in headers) { + res.headers.push({ + name: key, + value: headers[key] }); - return true; - } + } + res.header = res.getHeader; + res.setHeaders = headers => { + for (var key in headers) + res.setHeader(key, headers[key]); + }; + Object.defineProperty(res, 'statusCode', { + enumerable: true, + configurable: true, + writable: true, + value: res.statusCode + }); + this.newRequest.call(null, req, res); + }); + return true; + } } module.exports = WebServer; diff --git a/phantom_shim/runner.js b/phantom_shim/runner.js index 87d25fdd0d7..fc83eacb52b 100755 --- a/phantom_shim/runner.js +++ b/phantom_shim/runner.js @@ -27,39 +27,39 @@ var child_process = require('child_process'); var Browser = require('..').Browser; var version = require('../package.json').version; var argv = require('minimist')(process.argv.slice(2), { - alias: { v: 'version' }, - boolean: ['headless'], - default: {'headless': true }, + alias: { v: 'version' }, + boolean: ['headless'], + default: {'headless': true }, }); if (argv.version) { - console.log('Puppeteer v' + version); - return; + console.log('Puppeteer v' + version); + return; } if (argv['ssl-certificates-path']) { - console.error('Flag --ssl-certificates-path is not supported.'); - process.exit(1); - return; + console.error('Flag --ssl-certificates-path is not supported.'); + process.exit(1); + return; } var scriptArguments = argv._; if (!scriptArguments.length) { - console.log(__filename.split('/').pop() + ' [scriptfile]'); - return; + console.log(__filename.split('/').pop() + ' [scriptfile]'); + return; } var scriptPath = path.resolve(process.cwd(), scriptArguments[0]); if (!fs.existsSync(scriptPath)) { - console.error(`script not found: ${scriptPath}`); - process.exit(1); - return; + console.error(`script not found: ${scriptPath}`); + process.exit(1); + return; } var browser = new Browser({ - remoteDebuggingPort: 9229, - headless: argv.headless, - args: ['--no-sandbox'] + remoteDebuggingPort: 9229, + headless: argv.headless, + args: ['--no-sandbox'] }); var context = createPhantomContext(browser, scriptPath, argv); @@ -73,38 +73,38 @@ vm.runInContext(scriptContent, context); * @return {!Object} */ function createPhantomContext(browser, scriptPath, argv) { - var context = {}; - context.setInterval = setInterval; - context.setTimeout = setTimeout; - context.clearInterval = clearInterval; - context.clearTimeout = clearTimeout; + var context = {}; + context.setInterval = setInterval; + context.setTimeout = setTimeout; + context.clearInterval = clearInterval; + context.clearTimeout = clearTimeout; - context.phantom = Phantom.create(context, scriptPath); - context.console = console; - context.window = context; - context.WebPage = options => new WebPage(browser, scriptPath, options); + context.phantom = Phantom.create(context, scriptPath); + context.console = console; + context.window = context; + context.WebPage = options => new WebPage(browser, scriptPath, options); - vm.createContext(context); + vm.createContext(context); - var nativeExports = { - fs: new FileSystem(), - system: new System(argv._), - webpage: { - create: context.WebPage, - }, - webserver: { - create: () => new WebServer(), - }, - cookiejar: { - create: () => {}, - }, - child_process: child_process - }; - var bootstrapPath = path.join(__dirname, '..', 'third_party', 'phantomjs', 'bootstrap.js'); - var bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8'); - vm.runInContext(bootstrapCode, context, { - filename: 'bootstrap.js' - })(nativeExports); - return context; + var nativeExports = { + fs: new FileSystem(), + system: new System(argv._), + webpage: { + create: context.WebPage, + }, + webserver: { + create: () => new WebServer(), + }, + cookiejar: { + create: () => {}, + }, + child_process: child_process + }; + var bootstrapPath = path.join(__dirname, '..', 'third_party', 'phantomjs', 'bootstrap.js'); + var bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8'); + vm.runInContext(bootstrapCode, context, { + filename: 'bootstrap.js' + })(nativeExports); + return context; } diff --git a/phantom_shim/utilities.js b/phantom_shim/utilities.js index 46ee945ce7f..487ef4be71b 100644 --- a/phantom_shim/utilities.js +++ b/phantom_shim/utilities.js @@ -17,16 +17,16 @@ var loopWhile = require('deasync').loopWhile; module.exports = { - await: function(promise) { - var error; - var result; - var done = false; - promise.then(r => result = r) - .catch(err => error = err) - .then(() => done = true); - loopWhile(() => !done); - if (error) - throw error; - return result; - } + await: function(promise) { + var error; + var result; + var done = false; + promise.then(r => result = r) + .catch(err => error = err) + .then(() => done = true); + loopWhile(() => !done); + if (error) + throw error; + return result; + } }; diff --git a/test/StaticServer.js b/test/StaticServer.js index 6f16616c464..1b28a9eb92c 100644 --- a/test/StaticServer.js +++ b/test/StaticServer.js @@ -21,35 +21,35 @@ var path = require('path'); var mime = require('mime'); class StaticServer { - /** + /** * @param {string} dirPath * @param {number} port */ - constructor(dirPath, port) { - this._server = http.createServer(this._onRequest.bind(this)); - this._server.listen(port); - this._dirPath = dirPath; - } + constructor(dirPath, port) { + this._server = http.createServer(this._onRequest.bind(this)); + this._server.listen(port); + this._dirPath = dirPath; + } - stop() { - this._server.close(); - } + stop() { + this._server.close(); + } - _onRequest(request, response) { - var pathName = url.parse(request.url).path; - if (pathName === '/') - pathName = '/index.html'; - pathName = path.join(this._dirPath, pathName.substring(1)); - fs.readFile(pathName, function(err, data) { - if (err) { - response.statusCode = 404; - response.end(`File not found: ${pathName}`); - return; - } - response.setHeader('Content-Type', mime.lookup(pathName)); - response.end(data); - }); - } + _onRequest(request, response) { + var pathName = url.parse(request.url).path; + if (pathName === '/') + pathName = '/index.html'; + pathName = path.join(this._dirPath, pathName.substring(1)); + fs.readFile(pathName, function(err, data) { + if (err) { + response.statusCode = 404; + response.end(`File not found: ${pathName}`); + return; + } + response.setHeader('Content-Type', mime.lookup(pathName)); + response.end(data); + }); + } } module.exports = StaticServer; diff --git a/test/frame-utils.js b/test/frame-utils.js index 148f7748573..efc14d8e55e 100644 --- a/test/frame-utils.js +++ b/test/frame-utils.js @@ -1,62 +1,62 @@ var utils = module.exports = { - /** + /** * @param {!Page} page * @param {string} frameId * @param {string} url * @return {!Promise} */ - attachFrame: async function(page, frameId, url) { - await page.evaluate(attachFrame, frameId, url); + attachFrame: async function(page, frameId, url) { + await page.evaluate(attachFrame, frameId, url); - function attachFrame(frameId, url) { - var frame = document.createElement('iframe'); - frame.src = url; - frame.id = frameId; - document.body.appendChild(frame); - return new Promise(x => frame.onload = x); - } - }, + function attachFrame(frameId, url) { + var frame = document.createElement('iframe'); + frame.src = url; + frame.id = frameId; + document.body.appendChild(frame); + return new Promise(x => frame.onload = x); + } + }, - /** + /** * @param {!Page} page * @param {string} frameId * @return {!Promise} */ - detachFrame: async function(page, frameId) { - await page.evaluate(detachFrame, frameId); + detachFrame: async function(page, frameId) { + await page.evaluate(detachFrame, frameId); - function detachFrame(frameId) { - var frame = document.getElementById(frameId); - frame.remove(); - } - }, + function detachFrame(frameId) { + var frame = document.getElementById(frameId); + frame.remove(); + } + }, - /** + /** * @param {!Page} page * @param {string} frameId * @param {string} url * @return {!Promise} */ - navigateFrame: async function(page, frameId, url) { - await page.evaluate(navigateFrame, frameId, url); + navigateFrame: async function(page, frameId, url) { + await page.evaluate(navigateFrame, frameId, url); - function navigateFrame(frameId, url) { - var frame = document.getElementById(frameId); - frame.src = url; - return new Promise(x => frame.onload = x); - } - }, + function navigateFrame(frameId, url) { + var frame = document.getElementById(frameId); + frame.src = url; + return new Promise(x => frame.onload = x); + } + }, - /** + /** * @param {!Frame} frame * @param {string=} indentation * @return {string} */ - dumpFrames: function(frame, indentation) { - indentation = indentation || ''; - var result = indentation + frame.url(); - for (var child of frame.childFrames()) - result += '\n' + utils.dumpFrames(child, ' ' + indentation); - return result; - }, + dumpFrames: function(frame, indentation) { + indentation = indentation || ''; + var result = indentation + frame.url(); + for (var child of frame.childFrames()) + result += '\n' + utils.dumpFrames(child, ' ' + indentation); + return result; + }, }; diff --git a/test/golden-utils.js b/test/golden-utils.js index 55379286aa2..7859c4847e7 100644 --- a/test/golden-utils.js +++ b/test/golden-utils.js @@ -25,19 +25,19 @@ var GOLDEN_DIR = path.join(__dirname, 'golden'); var OUTPUT_DIR = path.join(__dirname, 'output'); module.exports = { - addMatchers: function(jasmine) { - jasmine.addMatchers(customMatchers); - }, + addMatchers: function(jasmine) { + jasmine.addMatchers(customMatchers); + }, - removeOutputDir: function() { - if (fs.existsSync(OUTPUT_DIR)) - rm(OUTPUT_DIR); - }, + removeOutputDir: function() { + if (fs.existsSync(OUTPUT_DIR)) + rm(OUTPUT_DIR); + }, }; var GoldenComparators = { - 'image/png': compareImages, - 'text/plain': compareText + 'image/png': compareImages, + 'text/plain': compareText }; /** @@ -46,19 +46,19 @@ var GoldenComparators = { * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} */ function compareImages(actualBuffer, expectedBuffer) { - if (!actualBuffer || !(actualBuffer instanceof Buffer)) - return { errorMessage: 'Actual result should be Buffer.' }; + if (!actualBuffer || !(actualBuffer instanceof Buffer)) + return { errorMessage: 'Actual result should be Buffer.' }; - var actual = PNG.sync.read(actualBuffer); - var expected = PNG.sync.read(expectedBuffer); - if (expected.width !== actual.width || expected.height !== actual.height) { - return { - errorMessage: `Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px. ` - }; - } - var diff = new PNG({width: expected.width, height: expected.height}); - var count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.1}); - return count > 0 ? { diff: PNG.sync.write(diff) } : null; + var actual = PNG.sync.read(actualBuffer); + var expected = PNG.sync.read(expectedBuffer); + if (expected.width !== actual.width || expected.height !== actual.height) { + return { + errorMessage: `Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px. ` + }; + } + var diff = new PNG({width: expected.width, height: expected.height}); + var count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.1}); + return count > 0 ? { diff: PNG.sync.write(diff) } : null; } /** @@ -67,80 +67,80 @@ function compareImages(actualBuffer, expectedBuffer) { * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} */ function compareText(actual, expectedBuffer) { - if (typeof actual !== 'string') - return { errorMessage: 'Actual result should be string' }; - var expected = expectedBuffer.toString('utf-8'); - if (expected === actual) - return null; - var diff = new Diff(); - var result = diff.main(expected, actual); - diff.cleanupSemantic(result); - var html = diff.prettyHtml(result); - var diffStylePath = path.join(__dirname, 'diffstyle.css'); - html = `` + html; - return { - diff: html, - diffExtension: '.html' - }; + if (typeof actual !== 'string') + return { errorMessage: 'Actual result should be string' }; + var expected = expectedBuffer.toString('utf-8'); + if (expected === actual) + return null; + var diff = new Diff(); + var result = diff.main(expected, actual); + diff.cleanupSemantic(result); + var html = diff.prettyHtml(result); + var diffStylePath = path.join(__dirname, 'diffstyle.css'); + html = `` + html; + return { + diff: html, + diffExtension: '.html' + }; } var customMatchers = { - toBeGolden: function(util, customEqualityTesters) { - return { - /** + toBeGolden: function(util, customEqualityTesters) { + return { + /** * @param {?Object} actual * @param {string} goldenName * @return {!{pass: boolean, message: (undefined|string)}} */ - compare: function(actual, goldenName) { - var expectedPath = path.join(GOLDEN_DIR, goldenName); - var actualPath = path.join(OUTPUT_DIR, goldenName); + compare: function(actual, goldenName) { + var expectedPath = path.join(GOLDEN_DIR, goldenName); + var actualPath = path.join(OUTPUT_DIR, goldenName); - var messageSuffix = 'Output is saved in "' + path.basename(OUTPUT_DIR + '" directory'); + var messageSuffix = 'Output is saved in "' + path.basename(OUTPUT_DIR + '" directory'); - if (!fs.existsSync(expectedPath)) { - ensureOutputDir(); - fs.writeFileSync(actualPath, actual); - return { - pass: false, - message: goldenName + ' is missing in golden results. ' + messageSuffix - }; - } - var expected = fs.readFileSync(expectedPath); - var comparator = GoldenComparators[mime.lookup(goldenName)]; - if (!comparator) { - return { - pass: false, - message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName - }; - } - var result = comparator(actual, expected); - if (!result) - return { pass: true }; - ensureOutputDir(); - fs.writeFileSync(actualPath, actual); - // Copy expected to the output/ folder for convenience. - fs.writeFileSync(addSuffix(actualPath, '-expected'), expected); - if (result.diff) { - var diffPath = addSuffix(actualPath, '-diff', result.diffExtension); - fs.writeFileSync(diffPath, result.diff); - } + if (!fs.existsSync(expectedPath)) { + ensureOutputDir(); + fs.writeFileSync(actualPath, actual); + return { + pass: false, + message: goldenName + ' is missing in golden results. ' + messageSuffix + }; + } + var expected = fs.readFileSync(expectedPath); + var comparator = GoldenComparators[mime.lookup(goldenName)]; + if (!comparator) { + return { + pass: false, + message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName + }; + } + var result = comparator(actual, expected); + if (!result) + return { pass: true }; + ensureOutputDir(); + fs.writeFileSync(actualPath, actual); + // Copy expected to the output/ folder for convenience. + fs.writeFileSync(addSuffix(actualPath, '-expected'), expected); + if (result.diff) { + var diffPath = addSuffix(actualPath, '-diff', result.diffExtension); + fs.writeFileSync(diffPath, result.diff); + } - var message = goldenName + ' mismatch!'; - if (result.errorMessage) - message += ' ' + result.errorMessage; - return { - pass: false, - message: message + ' ' + messageSuffix - }; - - function ensureOutputDir() { - if (!fs.existsSync(OUTPUT_DIR)) - fs.mkdirSync(OUTPUT_DIR); - } - } + var message = goldenName + ' mismatch!'; + if (result.errorMessage) + message += ' ' + result.errorMessage; + return { + pass: false, + message: message + ' ' + messageSuffix }; - }, + + function ensureOutputDir() { + if (!fs.existsSync(OUTPUT_DIR)) + fs.mkdirSync(OUTPUT_DIR); + } + } + }; + }, }; /** @@ -150,8 +150,8 @@ var customMatchers = { * @return {string} */ function addSuffix(filePath, suffix, customExtension) { - var dirname = path.dirname(filePath); - var ext = path.extname(filePath); - var name = path.basename(filePath, ext); - return path.join(dirname, name + suffix + (customExtension || ext)); + var dirname = path.dirname(filePath); + var ext = path.extname(filePath); + var name = path.basename(filePath, ext); + return path.join(dirname, name + suffix + (customExtension || ext)); } diff --git a/test/test.js b/test/test.js index 5eb330dcccf..c0154b6ae5c 100644 --- a/test/test.js +++ b/test/test.js @@ -26,308 +26,308 @@ var EMPTY_PAGE = STATIC_PREFIX + '/empty.html'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 10 * 1000; describe('Puppeteer', function() { - var browser; - var staticServer; - var page; + var browser; + var staticServer; + var page; - beforeAll(function() { - browser = new Browser({args: ['--no-sandbox']}); - staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT); - GoldenUtils.removeOutputDir(); - }); + beforeAll(function() { + browser = new Browser({args: ['--no-sandbox']}); + staticServer = new StaticServer(path.join(__dirname, 'assets'), PORT); + GoldenUtils.removeOutputDir(); + }); - afterAll(function() { - browser.close(); - staticServer.stop(); - }); + afterAll(function() { + browser.close(); + staticServer.stop(); + }); - beforeEach(SX(async function() { - page = await browser.newPage(); - GoldenUtils.addMatchers(jasmine); + beforeEach(SX(async function() { + page = await browser.newPage(); + GoldenUtils.addMatchers(jasmine); + })); + + afterEach(function() { + page.close(); + }); + + describe('Page.evaluate', function() { + it('should work', SX(async function() { + var result = await page.evaluate(() => 7 * 3); + expect(result).toBe(21); })); - - afterEach(function() { - page.close(); - }); - - describe('Page.evaluate', function() { - it('should work', SX(async function() { - var result = await page.evaluate(() => 7 * 3); - expect(result).toBe(21); - })); - it('should await promise', SX(async function() { - var result = await page.evaluate(() => Promise.resolve(8 * 7)); - expect(result).toBe(56); - })); - it('should work from-inside inPageCallback', SX(async function() { - // Setup inpage callback, which calls Page.evaluate - await page.setInPageCallback('callController', async function(a, b) { - return await page.evaluate((a, b) => a * b, a, b); - }); - var result = await page.evaluate(async function() { - return await callController(9, 3); - }); - expect(result).toBe(27); - })); - it('should reject promise with exception', SX(async function() { - var error = null; - try { - await page.evaluate(() => not.existing.object.property); - } catch (e) { - error = e; - } - expect(error).toBeTruthy(); - expect(error.message).toContain('not is not defined'); - })); - }); - - it('Page Events: ConsoleMessage', SX(async function() { - var msgs = []; - page.on('consolemessage', msg => msgs.push(msg)); - await page.evaluate(() => console.log('Message!')); - expect(msgs).toEqual(['Message!']); + it('should await promise', SX(async function() { + var result = await page.evaluate(() => Promise.resolve(8 * 7)); + expect(result).toBe(56); })); + it('should work from-inside inPageCallback', SX(async function() { + // Setup inpage callback, which calls Page.evaluate + await page.setInPageCallback('callController', async function(a, b) { + return await page.evaluate((a, b) => a * b, a, b); + }); + var result = await page.evaluate(async function() { + return await callController(9, 3); + }); + expect(result).toBe(27); + })); + it('should reject promise with exception', SX(async function() { + var error = null; + try { + await page.evaluate(() => not.existing.object.property); + } catch (e) { + error = e; + } + expect(error).toBeTruthy(); + expect(error.message).toContain('not is not defined'); + })); + }); - describe('Page.navigate', function() { - it('should fail when navigating to bad url', SX(async function() { - var success = await page.navigate('asdfasdf'); - expect(success).toBe(false); - })); - it('should succeed when navigating to good url', SX(async function() { - var success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); - })); + it('Page Events: ConsoleMessage', SX(async function() { + var msgs = []; + page.on('consolemessage', msg => msgs.push(msg)); + await page.evaluate(() => console.log('Message!')); + expect(msgs).toEqual(['Message!']); + })); + + describe('Page.navigate', function() { + it('should fail when navigating to bad url', SX(async function() { + var success = await page.navigate('asdfasdf'); + expect(success).toBe(false); + })); + it('should succeed when navigating to good url', SX(async function() { + var success = await page.navigate(EMPTY_PAGE); + expect(success).toBe(true); + })); + }); + + describe('Page.setInPageCallback', function() { + it('should work', SX(async function() { + await page.setInPageCallback('callController', function(a, b) { + return a * b; + }); + var result = await page.evaluate(async function() { + return await callController(9, 4); + }); + expect(result).toBe(36); + })); + it('should survive navigation', SX(async function() { + await page.setInPageCallback('callController', function(a, b) { + return a * b; + }); + + await page.navigate(EMPTY_PAGE); + var result = await page.evaluate(async function() { + return await callController(9, 4); + }); + expect(result).toBe(36); + })); + it('should await returned promise', SX(async function() { + await page.setInPageCallback('callController', function(a, b) { + return Promise.resolve(a * b); + }); + + var result = await page.evaluate(async function() { + return await callController(3, 5); + }); + expect(result).toBe(15); + })); + }); + + describe('Page.setRequestInterceptor', function() { + it('should intercept', SX(async function() { + page.setRequestInterceptor(request => { + expect(request.url()).toContain('empty.html'); + expect(request.headers()['User-Agent']).toBeTruthy(); + expect(request.method()).toBe('GET'); + expect(request.postData()).toBe(undefined); + request.continue(); + }); + var success = await page.navigate(EMPTY_PAGE); + expect(success).toBe(true); + })); + it('should show extraHTTPHeaders', SX(async function() { + await page.setExtraHTTPHeaders({ + foo: 'bar' + }); + page.setRequestInterceptor(request => { + expect(request.headers()['foo']).toBe('bar'); + request.continue(); + }); + var success = await page.navigate(EMPTY_PAGE); + expect(success).toBe(true); + })); + it('should be abortable', SX(async function() { + page.setRequestInterceptor(request => { + if (request.url().endsWith('.css')) + request.abort(); + else + request.continue(); + }); + var failedResources = 0; + page.on('resourceloadingfailed', event => ++failedResources); + var success = await page.navigate(STATIC_PREFIX + '/one-style.html'); + expect(success).toBe(true); + expect(failedResources).toBe(1); + })); + }); + + describe('Page.Events.Dialog', function() { + it('should fire', function(done) { + page.on('dialog', dialog => { + expect(dialog.type).toBe('alert'); + expect(dialog.message()).toBe('yo'); + done(); + }); + page.evaluate(() => alert('yo')); }); + // TODO Enable this when crbug.com/718235 is fixed. + xit('should allow accepting prompts', SX(async function(done) { + page.on('dialog', dialog => { + expect(dialog.type).toBe('prompt'); + expect(dialog.message()).toBe('question?'); + dialog.accept('answer!'); + }); + var result = await page.evaluate(() => prompt('question?')); + expect(result).toBe('answer!'); + })); + }); - describe('Page.setInPageCallback', function() { - it('should work', SX(async function() { - await page.setInPageCallback('callController', function(a, b) { - return a * b; - }); - var result = await page.evaluate(async function() { - return await callController(9, 4); - }); - expect(result).toBe(36); - })); - it('should survive navigation', SX(async function() { - await page.setInPageCallback('callController', function(a, b) { - return a * b; - }); - - await page.navigate(EMPTY_PAGE); - var result = await page.evaluate(async function() { - return await callController(9, 4); - }); - expect(result).toBe(36); - })); - it('should await returned promise', SX(async function() { - await page.setInPageCallback('callController', function(a, b) { - return Promise.resolve(a * b); - }); - - var result = await page.evaluate(async function() { - return await callController(3, 5); - }); - expect(result).toBe(15); - })); + describe('Page.Events.Error', function() { + it('should fire', function(done) { + page.on('error', error => { + expect(error.message).toContain('Fancy'); + done(); + }); + page.navigate(STATIC_PREFIX + '/error.html'); }); + }); - describe('Page.setRequestInterceptor', function() { - it('should intercept', SX(async function() { - page.setRequestInterceptor(request => { - expect(request.url()).toContain('empty.html'); - expect(request.headers()['User-Agent']).toBeTruthy(); - expect(request.method()).toBe('GET'); - expect(request.postData()).toBe(undefined); - request.continue(); - }); - var success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); + describe('Page.screenshot', function() { + it('should work', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot(); + expect(screenshot).toBeGolden('screenshot-sanity.png'); + })); + it('should clip rect', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot({ + clip: { + x: 50, + y: 100, + width: 150, + height: 100 + } + }); + expect(screenshot).toBeGolden('screenshot-clip-rect.png'); + })); + it('should work for offscreen clip', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot({ + clip: { + x: 50, + y: 600, + width: 100, + height: 100 + } + }); + expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); + })); + it('should run in parallel', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var promises = []; + for (var i = 0; i < 3; ++i) { + promises.push(page.screenshot({ + clip: { + x: 50 * i, + y: 0, + width: 50, + height: 50 + } })); - it('should show extraHTTPHeaders', SX(async function() { - await page.setExtraHTTPHeaders({ - foo: 'bar' - }); - page.setRequestInterceptor(request => { - expect(request.headers()['foo']).toBe('bar'); - request.continue(); - }); - var success = await page.navigate(EMPTY_PAGE); - expect(success).toBe(true); - })); - it('should be abortable', SX(async function() { - page.setRequestInterceptor(request => { - if (request.url().endsWith('.css')) - request.abort(); - else - request.continue(); - }); - var failedResources = 0; - page.on('resourceloadingfailed', event => ++failedResources); - var success = await page.navigate(STATIC_PREFIX + '/one-style.html'); - expect(success).toBe(true); - expect(failedResources).toBe(1); - })); - }); + } + var screenshot = await promises[1]; + expect(screenshot).toBeGolden('screenshot-parallel-calls.png'); + })); + it('should take fullPage screenshots', SX(async function() { + await page.setViewportSize({width: 500, height: 500}); + await page.navigate(STATIC_PREFIX + '/grid.html'); + var screenshot = await page.screenshot({ + fullPage: true + }); + expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); + })); + }); - describe('Page.Events.Dialog', function() { - it('should fire', function(done) { - page.on('dialog', dialog => { - expect(dialog.type).toBe('alert'); - expect(dialog.message()).toBe('yo'); - done(); - }); - page.evaluate(() => alert('yo')); - }); - // TODO Enable this when crbug.com/718235 is fixed. - xit('should allow accepting prompts', SX(async function(done) { - page.on('dialog', dialog => { - expect(dialog.type).toBe('prompt'); - expect(dialog.message()).toBe('question?'); - dialog.accept('answer!'); - }); - var result = await page.evaluate(() => prompt('question?')); - expect(result).toBe('answer!'); - })); - }); + describe('Frame Management', function() { + var FrameUtils = require('./frame-utils'); + it('should handle nested frames', SX(async function() { + await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); + expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt'); + })); + it('should send events when frames are manipulated dynamically', SX(async function() { + await page.navigate(EMPTY_PAGE); + // validate frameattached events + var attachedFrames = []; + page.on('frameattached', frame => attachedFrames.push(frame)); + await FrameUtils.attachFrame(page, 'frame1', './assets/frame.html'); + expect(attachedFrames.length).toBe(1); + expect(attachedFrames[0].url()).toContain('/assets/frame.html'); - describe('Page.Events.Error', function() { - it('should fire', function(done) { - page.on('error', error => { - expect(error.message).toContain('Fancy'); - done(); - }); - page.navigate(STATIC_PREFIX + '/error.html'); - }); - }); + // validate framenavigated events + var navigatedFrames = []; + page.on('framenavigated', frame => navigatedFrames.push(frame)); + await FrameUtils.navigateFrame(page, 'frame1', './empty.html'); + expect(navigatedFrames.length).toBe(1); + expect(navigatedFrames[0].url()).toContain('/empty.html'); - describe('Page.screenshot', function() { - it('should work', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot(); - expect(screenshot).toBeGolden('screenshot-sanity.png'); - })); - it('should clip rect', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot({ - clip: { - x: 50, - y: 100, - width: 150, - height: 100 - } - }); - expect(screenshot).toBeGolden('screenshot-clip-rect.png'); - })); - it('should work for offscreen clip', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot({ - clip: { - x: 50, - y: 600, - width: 100, - height: 100 - } - }); - expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); - })); - it('should run in parallel', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var promises = []; - for (var i = 0; i < 3; ++i) { - promises.push(page.screenshot({ - clip: { - x: 50 * i, - y: 0, - width: 50, - height: 50 - } - })); - } - var screenshot = await promises[1]; - expect(screenshot).toBeGolden('screenshot-parallel-calls.png'); - })); - it('should take fullPage screenshots', SX(async function() { - await page.setViewportSize({width: 500, height: 500}); - await page.navigate(STATIC_PREFIX + '/grid.html'); - var screenshot = await page.screenshot({ - fullPage: true - }); - expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); - })); - }); + // validate framedetached events + var detachedFrames = []; + page.on('framedetached', frame => detachedFrames.push(frame)); + await FrameUtils.detachFrame(page, 'frame1'); + expect(detachedFrames.length).toBe(1); + expect(detachedFrames[0].isDetached()).toBe(true); + })); + it('should persist mainFrame on cross-process navigation', SX(async function() { + await page.navigate(EMPTY_PAGE); + var mainFrame = page.mainFrame(); + await page.navigate('http://127.0.0.1:' + PORT + '/empty.html'); + expect(page.mainFrame() === mainFrame).toBeTruthy(); + })); + it('should not send attach/detach events for main frame', SX(async function() { + var hasEvents = false; + page.on('frameattached', frame => hasEvents = true); + page.on('framedetached', frame => hasEvents = true); + await page.navigate(EMPTY_PAGE); + expect(hasEvents).toBe(false); + })); + it('should detach child frames on navigation', SX(async function() { + var attachedFrames = []; + var detachedFrames = []; + var navigatedFrames = []; + page.on('frameattached', frame => attachedFrames.push(frame)); + page.on('framedetached', frame => detachedFrames.push(frame)); + page.on('framenavigated', frame => navigatedFrames.push(frame)); + await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); + expect(attachedFrames.length).toBe(4); + expect(detachedFrames.length).toBe(0); + expect(navigatedFrames.length).toBe(5); - describe('Frame Management', function() { - var FrameUtils = require('./frame-utils'); - it('should handle nested frames', SX(async function() { - await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); - expect(FrameUtils.dumpFrames(page.mainFrame())).toBeGolden('nested-frames.txt'); - })); - it('should send events when frames are manipulated dynamically', SX(async function() { - await page.navigate(EMPTY_PAGE); - // validate frameattached events - var attachedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - await FrameUtils.attachFrame(page, 'frame1', './assets/frame.html'); - expect(attachedFrames.length).toBe(1); - expect(attachedFrames[0].url()).toContain('/assets/frame.html'); - - // validate framenavigated events - var navigatedFrames = []; - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await FrameUtils.navigateFrame(page, 'frame1', './empty.html'); - expect(navigatedFrames.length).toBe(1); - expect(navigatedFrames[0].url()).toContain('/empty.html'); - - // validate framedetached events - var detachedFrames = []; - page.on('framedetached', frame => detachedFrames.push(frame)); - await FrameUtils.detachFrame(page, 'frame1'); - expect(detachedFrames.length).toBe(1); - expect(detachedFrames[0].isDetached()).toBe(true); - })); - it('should persist mainFrame on cross-process navigation', SX(async function() { - await page.navigate(EMPTY_PAGE); - var mainFrame = page.mainFrame(); - await page.navigate('http://127.0.0.1:' + PORT + '/empty.html'); - expect(page.mainFrame() === mainFrame).toBeTruthy(); - })); - it('should not send attach/detach events for main frame', SX(async function() { - var hasEvents = false; - page.on('frameattached', frame => hasEvents = true); - page.on('framedetached', frame => hasEvents = true); - await page.navigate(EMPTY_PAGE); - expect(hasEvents).toBe(false); - })); - it('should detach child frames on navigation', SX(async function() { - var attachedFrames = []; - var detachedFrames = []; - var navigatedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - page.on('framedetached', frame => detachedFrames.push(frame)); - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await page.navigate(STATIC_PREFIX + '/frames/nested-frames.html'); - expect(attachedFrames.length).toBe(4); - expect(detachedFrames.length).toBe(0); - expect(navigatedFrames.length).toBe(5); - - var attachedFrames = []; - var detachedFrames = []; - var navigatedFrames = []; - await page.navigate(EMPTY_PAGE); - expect(attachedFrames.length).toBe(0); - expect(detachedFrames.length).toBe(4); - expect(navigatedFrames.length).toBe(1); - })); - }); + var attachedFrames = []; + var detachedFrames = []; + var navigatedFrames = []; + await page.navigate(EMPTY_PAGE); + expect(attachedFrames.length).toBe(0); + expect(detachedFrames.length).toBe(4); + expect(navigatedFrames.length).toBe(1); + })); + }); }); // Since Jasmine doesn't like async functions, they should be wrapped // in a SX function. function SX(fun) { - return done => Promise.resolve(fun()).then(done).catch(done.fail); + return done => Promise.resolve(fun()).then(done).catch(done.fail); } diff --git a/utils/ChromiumDownloader.js b/utils/ChromiumDownloader.js index 5b5510b220a..3fc42b1c151 100644 --- a/utils/ChromiumDownloader.js +++ b/utils/ChromiumDownloader.js @@ -25,107 +25,107 @@ var URL = require('url'); var CHROMIUM_PATH = path.join(__dirname, '..', '.local-chromium'); var downloadURLs = { - linux: 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.zip', - mac: 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/%d/chrome-mac.zip', - win32: 'https://storage.googleapis.com/chromium-browser-snapshots/Win/%d/chrome-win32.zip', - win64: 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip', + linux: 'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/%d/chrome-linux.zip', + mac: 'https://storage.googleapis.com/chromium-browser-snapshots/Mac/%d/chrome-mac.zip', + win32: 'https://storage.googleapis.com/chromium-browser-snapshots/Win/%d/chrome-win32.zip', + win64: 'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/%d/chrome-win32.zip', }; module.exports = { - /** + /** * @return {!Array} */ - supportedPlatforms: function() { - return Object.keys(downloadURLs); - }, + supportedPlatforms: function() { + return Object.keys(downloadURLs); + }, - /** + /** * @return {string} */ - currentPlatform: function() { - var platform = os.platform(); - if (platform === 'darwin') - return 'mac'; - if (platform === 'linux') - return 'linux'; - if (platform === 'win32') - return os.arch() === 'x64' ? 'win64' : 'win32'; - return ''; - }, + currentPlatform: function() { + var platform = os.platform(); + if (platform === 'darwin') + return 'mac'; + if (platform === 'linux') + return 'linux'; + if (platform === 'win32') + return os.arch() === 'x64' ? 'win64' : 'win32'; + return ''; + }, - /** + /** * @param {string} platform * @param {string} revision * @return {!Promise} */ - canDownloadRevision: function(platform, revision) { - console.assert(downloadURLs[platform], 'Unknown platform: ' + platform); - var url = URL.parse(util.format(downloadURLs[platform], revision)); - var options = { - method: 'HEAD', - host: url.host, - path: url.pathname, - }; - var resolve; - var promise = new Promise(x => resolve = x); - var request = https.request(options, response => { - resolve(response.statusCode === 200); - }); - request.on('error', error => { - console.error(error); - resolve(false); - }); - request.end(); - return promise; - }, + canDownloadRevision: function(platform, revision) { + console.assert(downloadURLs[platform], 'Unknown platform: ' + platform); + var url = URL.parse(util.format(downloadURLs[platform], revision)); + var options = { + method: 'HEAD', + host: url.host, + path: url.pathname, + }; + var resolve; + var promise = new Promise(x => resolve = x); + var request = https.request(options, response => { + resolve(response.statusCode === 200); + }); + request.on('error', error => { + console.error(error); + resolve(false); + }); + request.end(); + return promise; + }, - /** + /** * @param {string} platform * @param {string} revision * @param {?function(number, number)} progressCallback * @return {!Promise} */ - downloadRevision: async function(platform, revision, progressCallback) { - var url = downloadURLs[platform]; - console.assert(url, `Unsupported platform: ${platform}`); - url = util.format(url, revision); - var zipPath = path.join(CHROMIUM_PATH, `download-${platform}-${revision}.zip`); - var folderPath = getFolderPath(platform, revision); - if (fs.existsSync(folderPath)) - return; - try { - if (!fs.existsSync(CHROMIUM_PATH)) - fs.mkdirSync(CHROMIUM_PATH); - await downloadFile(url, zipPath, progressCallback); - await extractZip(zipPath, folderPath); - } finally { - if (fs.existsSync(zipPath)) - fs.unlinkSync(zipPath); - } - }, + downloadRevision: async function(platform, revision, progressCallback) { + var url = downloadURLs[platform]; + console.assert(url, `Unsupported platform: ${platform}`); + url = util.format(url, revision); + var zipPath = path.join(CHROMIUM_PATH, `download-${platform}-${revision}.zip`); + var folderPath = getFolderPath(platform, revision); + if (fs.existsSync(folderPath)) + return; + try { + if (!fs.existsSync(CHROMIUM_PATH)) + fs.mkdirSync(CHROMIUM_PATH); + await downloadFile(url, zipPath, progressCallback); + await extractZip(zipPath, folderPath); + } finally { + if (fs.existsSync(zipPath)) + fs.unlinkSync(zipPath); + } + }, - /** + /** * @param {string} platform * @param {string} revision * @return {?{executablePath: string}} */ - revisionInfo: function(platform, revision) { - var folderPath = getFolderPath(platform, revision); - if (!fs.existsSync(folderPath)) - return null; - var executablePath = ''; - if (platform === 'mac') - executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'); - else if (platform === 'linux') - executablePath = path.join(folderPath, 'chrome-linux', 'chrome'); - else if (platform === 'win32' || platform === 'win64') - executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe'); - else - throw 'Unsupported platfrom: ' + platfrom; - return { - executablePath: executablePath - }; - }, + revisionInfo: function(platform, revision) { + var folderPath = getFolderPath(platform, revision); + if (!fs.existsSync(folderPath)) + return null; + var executablePath = ''; + if (platform === 'mac') + executablePath = path.join(folderPath, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'); + else if (platform === 'linux') + executablePath = path.join(folderPath, 'chrome-linux', 'chrome'); + else if (platform === 'win32' || platform === 'win64') + executablePath = path.join(folderPath, 'chrome-win32', 'chrome.exe'); + else + throw 'Unsupported platfrom: ' + platfrom; + return { + executablePath: executablePath + }; + }, }; /** @@ -134,7 +134,7 @@ module.exports = { * @return {string} */ function getFolderPath(platform, revision) { - return path.join(CHROMIUM_PATH, platform + '-' + revision); + return path.join(CHROMIUM_PATH, platform + '-' + revision); } /** @@ -144,30 +144,30 @@ function getFolderPath(platform, revision) { * @return {!Promise} */ function downloadFile(url, destinationPath, progressCallback) { - var fulfill, reject; - var promise = new Promise((x, y) => { fulfill = x; reject = y; }); - var request = https.get(url, response => { - if (response.statusCode !== 200) { - var error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`); - // consume response data to free up memory - response.resume(); - reject(error); - return; - } - var file = fs.createWriteStream(destinationPath); - file.on('finish', () => fulfill()); - file.on('error', error => reject(error)); - response.pipe(file); - var totalBytes = parseInt(response.headers['content-length'], 10); - if (progressCallback) - response.on('data', onData.bind(null, totalBytes)); - }); - request.on('error', error => reject(error)); - return promise; - - function onData(totalBytes, chunk) { - progressCallback(totalBytes, chunk.length); + var fulfill, reject; + var promise = new Promise((x, y) => { fulfill = x; reject = y; }); + var request = https.get(url, response => { + if (response.statusCode !== 200) { + var error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`); + // consume response data to free up memory + response.resume(); + reject(error); + return; } + var file = fs.createWriteStream(destinationPath); + file.on('finish', () => fulfill()); + file.on('error', error => reject(error)); + response.pipe(file); + var totalBytes = parseInt(response.headers['content-length'], 10); + if (progressCallback) + response.on('data', onData.bind(null, totalBytes)); + }); + request.on('error', error => reject(error)); + return promise; + + function onData(totalBytes, chunk) { + progressCallback(totalBytes, chunk.length); + } } /** @@ -176,5 +176,5 @@ function downloadFile(url, destinationPath, progressCallback) { * @return {!Promise} */ function extractZip(zipPath, folderPath) { - return new Promise(fulfill => extract(zipPath, {dir: folderPath}, fulfill)); + return new Promise(fulfill => extract(zipPath, {dir: folderPath}, fulfill)); } diff --git a/utils/check_availability.js b/utils/check_availability.js index 8a3501beec3..df496885247 100755 --- a/utils/check_availability.js +++ b/utils/check_availability.js @@ -20,43 +20,43 @@ var https = require('https'); var OMAHA_PROXY = 'https://omahaproxy.appspot.com/all.json'; var colors = { - reset: '\x1b[0m', - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m' + reset: '\x1b[0m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m' }; class Table { - /** + /** * @param {!Array} columnWidths */ - constructor(columnWidths) { - this.widths = columnWidths; - } + constructor(columnWidths) { + this.widths = columnWidths; + } - /** + /** * @param {!Array} values */ - drawRow(values) { - console.assert(values.length === this.widths.length); - var row = ''; - for (var i = 0; i < values.length; ++i) - row += padCenter(values[i], this.widths[i]); - console.log(row); - } + drawRow(values) { + console.assert(values.length === this.widths.length); + var row = ''; + for (var i = 0; i < values.length; ++i) + row += padCenter(values[i], this.widths[i]); + console.log(row); + } } if (process.argv.length === 2) { - checkOmahaProxyAvailability(); - return; + checkOmahaProxyAvailability(); + return; } if (process.argv.length !== 4) { - console.log(` + console.log(` Usage: node check_revisions.js [fromRevision] [toRevision] This script checks availability of different prebuild chromium revisions. Running command without arguments will check against omahaproxy revisions.`); - return; + return; } var fromRevision = parseInt(process.argv[2], 10); @@ -67,27 +67,27 @@ checkRangeAvailability(fromRevision, toRevision); * @return {!Promise} */ async function checkOmahaProxyAvailability() { - console.log('Fetching revisions from ' + OMAHA_PROXY); - var platforms = await loadJSON(OMAHA_PROXY); - if (!platforms) { - console.error('ERROR: failed to fetch chromium revisions from omahaproxy.'); - return; - } - var table = new Table([27, 7, 7, 7, 7]); - table.drawRow([''].concat(Downloader.supportedPlatforms())); - for (var platform of platforms) { - // Trust only to the main platforms. - if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux') - continue; - var osName = platform.os === 'win' ? 'win32' : platform.os; - for (var version of platform.versions) { - if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable') - continue; - var revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15); - var revision = parseInt(version.branch_base_position, 10); - await checkAndDrawRevisionAvailability(table, revisionName, revision); - } + console.log('Fetching revisions from ' + OMAHA_PROXY); + var platforms = await loadJSON(OMAHA_PROXY); + if (!platforms) { + console.error('ERROR: failed to fetch chromium revisions from omahaproxy.'); + return; + } + var table = new Table([27, 7, 7, 7, 7]); + table.drawRow([''].concat(Downloader.supportedPlatforms())); + for (var platform of platforms) { + // Trust only to the main platforms. + if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux') + continue; + var osName = platform.os === 'win' ? 'win32' : platform.os; + for (var version of platform.versions) { + if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable') + continue; + var revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15); + var revision = parseInt(version.branch_base_position, 10); + await checkAndDrawRevisionAvailability(table, revisionName, revision); } + } } /** @@ -96,11 +96,11 @@ async function checkOmahaProxyAvailability() { * @return {!Promise} */ async function checkRangeAvailability(fromRevision, toRevision) { - var table = new Table([10, 7, 7, 7, 7]); - table.drawRow([''].concat(Downloader.supportedPlatforms())); - var inc = fromRevision < toRevision ? 1 : -1; - for (var revision = fromRevision; revision !== toRevision; revision += inc) - await checkAndDrawRevisionAvailability(table, '', revision); + var table = new Table([10, 7, 7, 7, 7]); + table.drawRow([''].concat(Downloader.supportedPlatforms())); + var inc = fromRevision < toRevision ? 1 : -1; + for (var revision = fromRevision; revision !== toRevision; revision += inc) + await checkAndDrawRevisionAvailability(table, '', revision); } /** @@ -110,18 +110,18 @@ async function checkRangeAvailability(fromRevision, toRevision) { * @return {!Promise} */ async function checkAndDrawRevisionAvailability(table, name, revision) { - var promises = []; - for (var platform of Downloader.supportedPlatforms()) - promises.push(Downloader.canDownloadRevision(platform, revision)); - var availability = await Promise.all(promises); - var allAvailable = availability.every(e => !!e); - var values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)]; - for (var i = 0; i < availability.length; ++i) { - var decoration = availability[i] ? '+' : '-'; - var color = availability[i] ? colors.green : colors.red; - values.push(color + decoration + colors.reset); - } - table.drawRow(values); + var promises = []; + for (var platform of Downloader.supportedPlatforms()) + promises.push(Downloader.canDownloadRevision(platform, revision)); + var availability = await Promise.all(promises); + var allAvailable = availability.every(e => !!e); + var values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)]; + for (var i = 0; i < availability.length; ++i) { + var decoration = availability[i] ? '+' : '-'; + var color = availability[i] ? colors.green : colors.red; + values.push(color + decoration + colors.reset); + } + table.drawRow(values); } /** @@ -129,26 +129,26 @@ async function checkAndDrawRevisionAvailability(table, name, revision) { * @return {!Promise} */ function loadJSON(url) { - var resolve; - var promise = new Promise(x => resolve = x); - https.get(url, response => { - if (response.statusCode !== 200) { - resolve(null); - return; - } - var body = ''; - response.on('data', function(chunk){ - body += chunk; - }); - response.on('end', function(){ - var json = JSON.parse(body); - resolve(json); - }); - }).on('error', function(e){ - console.error('Error fetching json: ' + e); - resolve(null); + var resolve; + var promise = new Promise(x => resolve = x); + https.get(url, response => { + if (response.statusCode !== 200) { + resolve(null); + return; + } + var body = ''; + response.on('data', function(chunk){ + body += chunk; }); - return promise; + response.on('end', function(){ + var json = JSON.parse(body); + resolve(json); + }); + }).on('error', function(e){ + console.error('Error fetching json: ' + e); + resolve(null); + }); + return promise; } /** @@ -156,7 +156,7 @@ function loadJSON(url) { * @return {string} */ function spaceString(size) { - return new Array(size).fill(' ').join(''); + return new Array(size).fill(' ').join(''); } /** @@ -164,11 +164,11 @@ function spaceString(size) { * @return {string} */ function filterOutColors(text) { - for (var colorName in colors) { - var color = colors[colorName]; - text = text.replace(color, ''); - } - return text; + for (var colorName in colors) { + var color = colors[colorName]; + text = text.replace(color, ''); + } + return text; } /** @@ -177,8 +177,8 @@ function filterOutColors(text) { * @return {string} */ function padLeft(text, length) { - var printableCharacters = filterOutColors(text); - return printableCharacters.length >= length ? text : spaceString(length - text.length) + text; + var printableCharacters = filterOutColors(text); + return printableCharacters.length >= length ? text : spaceString(length - text.length) + text; } /** @@ -187,10 +187,10 @@ function padLeft(text, length) { * @return {string} */ function padCenter(text, length) { - var printableCharacters = filterOutColors(text); - if (printableCharacters.length >= length) - return text; - var left = Math.floor((length - printableCharacters.length) / 2); - var right = Math.ceil((length - printableCharacters.length) / 2); - return spaceString(left) + text + spaceString(right); + var printableCharacters = filterOutColors(text); + if (printableCharacters.length >= length) + return text; + var left = Math.floor((length - printableCharacters.length) / 2); + var right = Math.ceil((length - printableCharacters.length) / 2); + return spaceString(left) + text + spaceString(right); }