puppeteer-core
Advanced tools
Comparing version 3.0.0 to 3.0.1
@@ -17,41 +17,2 @@ /** | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const child_process = require('child_process'); | ||
const {promisify} = require('util'); | ||
const fsAccess = promisify(fs.access); | ||
const exec = promisify(child_process.exec); | ||
const fileExists = async filePath => fsAccess(filePath).then(() => true).catch(() => false); | ||
/* | ||
* Now Puppeteer is built with TypeScript, we need to ensure that | ||
* locally we have the generated output before trying to install. | ||
* | ||
* For users installing puppeteer this is fine, they will have the | ||
* generated lib/ directory as we ship it when we publish to npm. | ||
* | ||
* However, if you're cloning the repo to contribute, you won't have the | ||
* generated lib/ directory so this script checks if we need to run | ||
* TypeScript first to ensure the output exists and is in the right | ||
* place. | ||
*/ | ||
async function compileTypeScript() { | ||
return exec('npm run tsc').catch(err => { | ||
console.error('Error running TypeScript', err); | ||
process.exit(1); | ||
}); | ||
} | ||
async function ensureLibDirectoryExists() { | ||
const libPath = path.join(__dirname, 'lib'); | ||
const libExists = await fileExists(libPath); | ||
if (libExists) return; | ||
logPolitely('Compiling TypeScript before install...'); | ||
await compileTypeScript(); | ||
} | ||
/** | ||
@@ -66,2 +27,5 @@ * This file is part of public API. | ||
*/ | ||
const compileTypeScriptIfRequired = require('./typescript-if-required'); | ||
const supportedProducts = { | ||
@@ -73,3 +37,3 @@ 'chrome': 'Chromium', | ||
async function download() { | ||
await ensureLibDirectoryExists(); | ||
await compileTypeScriptIfRequired(); | ||
@@ -79,3 +43,3 @@ const downloadHost = process.env.PUPPETEER_DOWNLOAD_HOST || process.env.npm_config_puppeteer_download_host || process.env.npm_package_config_puppeteer_download_host; | ||
const product = process.env.PUPPETEER_PRODUCT || process.env.npm_config_puppeteer_product || process.env.npm_package_config_puppeteer_product || 'chrome'; | ||
const browserFetcher = puppeteer.createBrowserFetcher({ product, host: downloadHost }); | ||
const browserFetcher = puppeteer.createBrowserFetcher({product, host: downloadHost}); | ||
const revision = await getRevision(); | ||
@@ -82,0 +46,0 @@ await fetchBinary(revision); |
@@ -16,2 +16,8 @@ /** | ||
*/ | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { ElementHandle } = require('./JSHandle'); | ||
/** | ||
@@ -55,3 +61,3 @@ * @typedef {Object} SerializedAXNode | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
*/ | ||
@@ -62,3 +68,3 @@ constructor(client) { | ||
/** | ||
* @param {{interestingOnly?: boolean, root?: ?Puppeteer.ElementHandle}=} options | ||
* @param {{interestingOnly?: boolean, root?: ?ElementHandle}=} options | ||
* @return {!Promise<!SerializedAXNode>} | ||
@@ -65,0 +71,0 @@ */ |
@@ -21,5 +21,8 @@ /** | ||
const { Events } = require('./Events'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { Connection } = require('./Connection'); | ||
class Browser extends EventEmitter { | ||
/** | ||
* @param {!Puppeteer.Connection} connection | ||
* @param {!Connection} connection | ||
* @param {!Array<string>} contextIds | ||
@@ -37,3 +40,3 @@ * @param {boolean} ignoreHTTPSErrors | ||
/** | ||
* @param {!Puppeteer.Connection} connection | ||
* @param {!Connection} connection | ||
* @param {!Array<string>} contextIds | ||
@@ -251,3 +254,3 @@ * @param {boolean} ignoreHTTPSErrors | ||
/** | ||
* @param {!Puppeteer.Connection} connection | ||
* @param {!Connection} connection | ||
* @param {!Browser} browser | ||
@@ -254,0 +257,0 @@ * @param {?string} contextId |
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -16,6 +18,8 @@ * Copyright 2017 Google Inc. All rights reserved. | ||
*/ | ||
const { assert } = require('./helper'); | ||
const { Events } = require('./Events'); | ||
const debugProtocol = require('debug')('puppeteer:protocol'); | ||
const EventEmitter = require('events'); | ||
const helper_1 = require("./helper"); | ||
const EventsModule = require("./Events"); | ||
const { Events } = EventsModule; | ||
const debug = require("debug"); | ||
const debugProtocol = debug('puppeteer:protocol'); | ||
const EventEmitter = require("events"); | ||
class Connection extends EventEmitter { | ||
@@ -29,6 +33,7 @@ /** | ||
super(); | ||
this._url = url; | ||
this._lastId = 0; | ||
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/ | ||
this._sessions = new Map(); | ||
this._closed = false; | ||
this._callbacks = new Map(); | ||
this._url = url; | ||
this._delay = delay; | ||
@@ -38,10 +43,3 @@ this._transport = transport; | ||
this._transport.onclose = this._onClose.bind(this); | ||
/** @type {!Map<string, !CDPSession>}*/ | ||
this._sessions = new Map(); | ||
this._closed = false; | ||
} | ||
/** | ||
* @param {!CDPSession} session | ||
* @return {!Connection} | ||
*/ | ||
static fromSession(session) { | ||
@@ -57,13 +55,5 @@ return session._connection; | ||
} | ||
/** | ||
* @return {string} | ||
*/ | ||
url() { | ||
return this._url; | ||
} | ||
/** | ||
* @param {string} method | ||
* @param {!Object=} params | ||
* @return {!Promise<?Object>} | ||
*/ | ||
send(method, params = {}) { | ||
@@ -75,6 +65,2 @@ const id = this._rawSend({ method, params }); | ||
} | ||
/** | ||
* @param {*} message | ||
* @return {number} | ||
*/ | ||
_rawSend(message) { | ||
@@ -87,5 +73,2 @@ const id = ++this._lastId; | ||
} | ||
/** | ||
* @param {string} message | ||
*/ | ||
async _onMessage(message) { | ||
@@ -155,11 +138,6 @@ if (this._delay) | ||
} | ||
exports.Connection = Connection; | ||
class CDPSession extends EventEmitter { | ||
/** | ||
* @param {!Connection} connection | ||
* @param {string} targetType | ||
* @param {string} sessionId | ||
*/ | ||
constructor(connection, targetType, sessionId) { | ||
super(); | ||
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/ | ||
this._callbacks = new Map(); | ||
@@ -170,11 +148,14 @@ this._connection = connection; | ||
} | ||
/** | ||
* @param {string} method | ||
* @param {!Object=} params | ||
* @return {!Promise<?Object>} | ||
*/ | ||
send(method, params = {}) { | ||
send(method, params) { | ||
if (!this._connection) | ||
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`)); | ||
const id = this._connection._rawSend({ sessionId: this._sessionId, method, params }); | ||
const id = this._connection._rawSend({ | ||
sessionId: this._sessionId, | ||
method, | ||
/* TODO(jacktfranklin@): once this Firefox bug is solved | ||
* we no longer need the `|| {}` check | ||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1631570 | ||
*/ | ||
params: params || {} | ||
}); | ||
return new Promise((resolve, reject) => { | ||
@@ -184,5 +165,2 @@ this._callbacks.set(id, { resolve, reject, error: new Error(), method }); | ||
} | ||
/** | ||
* @param {{id?: number, method: string, params: Object, error: {message: string, data: any}, result?: *}} object | ||
*/ | ||
_onMessage(object) { | ||
@@ -198,3 +176,3 @@ if (object.id && this._callbacks.has(object.id)) { | ||
else { | ||
assert(!object.id); | ||
helper_1.assert(!object.id); | ||
this.emit(object.method, object.params); | ||
@@ -216,2 +194,3 @@ } | ||
} | ||
exports.CDPSession = CDPSession; | ||
/** | ||
@@ -238,2 +217,1 @@ * @param {!Error} error | ||
} | ||
module.exports = { Connection, CDPSession }; |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -16,14 +17,6 @@ * Copyright 2017 Google Inc. All rights reserved. | ||
*/ | ||
const { helper, debugError, assert } = require('./helper'); | ||
const { EVALUATION_SCRIPT_URL } = require('./ExecutionContext'); | ||
/** | ||
* @typedef {Object} CoverageEntry | ||
* @property {string} url | ||
* @property {string} text | ||
* @property {!Array<!{start: number, end: number}>} ranges | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const helper_1 = require("./helper"); | ||
const ExecutionContext_1 = require("./ExecutionContext"); | ||
class Coverage { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
*/ | ||
constructor(client) { | ||
@@ -33,23 +26,11 @@ this._jsCoverage = new JSCoverage(client); | ||
} | ||
/** | ||
* @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options | ||
*/ | ||
async startJSCoverage(options) { | ||
return await this._jsCoverage.start(options); | ||
} | ||
/** | ||
* @return {!Promise<!Array<!CoverageEntry>>} | ||
*/ | ||
async stopJSCoverage() { | ||
return await this._jsCoverage.stop(); | ||
} | ||
/** | ||
* @param {{resetOnNavigation?: boolean}=} options | ||
*/ | ||
async startCSSCoverage(options) { | ||
return await this._cssCoverage.start(options); | ||
} | ||
/** | ||
* @return {!Promise<!Array<!CoverageEntry>>} | ||
*/ | ||
async stopCSSCoverage() { | ||
@@ -59,9 +40,5 @@ return await this._cssCoverage.stop(); | ||
} | ||
module.exports = { Coverage }; | ||
exports.Coverage = Coverage; | ||
class JSCoverage { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
*/ | ||
constructor(client) { | ||
this._client = client; | ||
this._enabled = false; | ||
@@ -72,8 +49,7 @@ this._scriptURLs = new Map(); | ||
this._resetOnNavigation = false; | ||
this._reportAnonymousScripts = false; | ||
this._client = client; | ||
} | ||
/** | ||
* @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options | ||
*/ | ||
async start(options = {}) { | ||
assert(!this._enabled, 'JSCoverage is already enabled'); | ||
helper_1.assert(!this._enabled, 'JSCoverage is already enabled'); | ||
const { resetOnNavigation = true, reportAnonymousScripts = false } = options; | ||
@@ -86,4 +62,4 @@ this._resetOnNavigation = resetOnNavigation; | ||
this._eventListeners = [ | ||
helper.addEventListener(this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this)), | ||
helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), | ||
helper_1.helper.addEventListener(this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this)), | ||
helper_1.helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), | ||
]; | ||
@@ -103,8 +79,5 @@ await Promise.all([ | ||
} | ||
/** | ||
* @param {!Protocol.Debugger.scriptParsedPayload} event | ||
*/ | ||
async _onScriptParsed(event) { | ||
// Ignore puppeteer-injected scripts | ||
if (event.url === EVALUATION_SCRIPT_URL) | ||
if (event.url === ExecutionContext_1.EVALUATION_SCRIPT_URL) | ||
return; | ||
@@ -121,10 +94,7 @@ // Ignore other anonymous scripts unless the reportAnonymousScripts option is true. | ||
// This might happen if the page has already navigated away. | ||
debugError(e); | ||
helper_1.debugError(e); | ||
} | ||
} | ||
/** | ||
* @return {!Promise<!Array<!CoverageEntry>>} | ||
*/ | ||
async stop() { | ||
assert(this._enabled, 'JSCoverage is not enabled'); | ||
helper_1.assert(this._enabled, 'JSCoverage is not enabled'); | ||
this._enabled = false; | ||
@@ -137,5 +107,5 @@ const result = await Promise.all([ | ||
]); | ||
helper.removeEventListeners(this._eventListeners); | ||
helper_1.helper.removeEventListeners(this._eventListeners); | ||
const coverage = []; | ||
const profileResponse = /** @type Protocol.Profiler.takePreciseCoverageReturnValue */ (result[0]); | ||
const profileResponse = result[0]; | ||
for (const entry of profileResponse.result) { | ||
@@ -158,7 +128,3 @@ let url = this._scriptURLs.get(entry.scriptId); | ||
class CSSCoverage { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
*/ | ||
constructor(client) { | ||
this._client = client; | ||
this._enabled = false; | ||
@@ -169,8 +135,7 @@ this._stylesheetURLs = new Map(); | ||
this._resetOnNavigation = false; | ||
this._reportAnonymousScripts = false; | ||
this._client = client; | ||
} | ||
/** | ||
* @param {{resetOnNavigation?: boolean}=} options | ||
*/ | ||
async start(options = {}) { | ||
assert(!this._enabled, 'CSSCoverage is already enabled'); | ||
helper_1.assert(!this._enabled, 'CSSCoverage is already enabled'); | ||
const { resetOnNavigation = true } = options; | ||
@@ -182,4 +147,4 @@ this._resetOnNavigation = resetOnNavigation; | ||
this._eventListeners = [ | ||
helper.addEventListener(this._client, 'CSS.styleSheetAdded', this._onStyleSheet.bind(this)), | ||
helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), | ||
helper_1.helper.addEventListener(this._client, 'CSS.styleSheetAdded', this._onStyleSheet.bind(this)), | ||
helper_1.helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), | ||
]; | ||
@@ -198,5 +163,2 @@ await Promise.all([ | ||
} | ||
/** | ||
* @param {!Protocol.CSS.styleSheetAddedPayload} event | ||
*/ | ||
async _onStyleSheet(event) { | ||
@@ -214,10 +176,7 @@ const header = event.header; | ||
// This might happen if the page has already navigated away. | ||
debugError(e); | ||
helper_1.debugError(e); | ||
} | ||
} | ||
/** | ||
* @return {!Promise<!Array<!CoverageEntry>>} | ||
*/ | ||
async stop() { | ||
assert(this._enabled, 'CSSCoverage is not enabled'); | ||
helper_1.assert(this._enabled, 'CSSCoverage is not enabled'); | ||
this._enabled = false; | ||
@@ -229,3 +188,3 @@ const ruleTrackingResponse = await this._client.send('CSS.stopRuleUsageTracking'); | ||
]); | ||
helper.removeEventListeners(this._eventListeners); | ||
helper_1.helper.removeEventListeners(this._eventListeners); | ||
// aggregate by styleSheetId | ||
@@ -255,6 +214,2 @@ const styleSheetIdToCoverage = new Map(); | ||
} | ||
/** | ||
* @param {!Array<!{startOffset:number, endOffset:number, count:number}>} nestedRanges | ||
* @return {!Array<!{start:number, end:number}>} | ||
*/ | ||
function convertToDisjointRanges(nestedRanges) { | ||
@@ -261,0 +216,0 @@ const points = []; |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -16,40 +17,29 @@ * Copyright 2017 Google Inc. All rights reserved. | ||
*/ | ||
const { assert } = require('./helper'); | ||
const helper_1 = require("./helper"); | ||
var DialogType; | ||
(function (DialogType) { | ||
DialogType["Alert"] = "alert"; | ||
DialogType["BeforeUnload"] = "beforeunload"; | ||
DialogType["Confirm"] = "confirm"; | ||
DialogType["Prompt"] = "prompt"; | ||
})(DialogType || (DialogType = {})); | ||
class Dialog { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {string} type | ||
* @param {string} message | ||
* @param {(string|undefined)} defaultValue | ||
*/ | ||
constructor(client, type, message, defaultValue = '') { | ||
this._handled = false; | ||
this._client = client; | ||
this._type = type; | ||
this._message = message; | ||
this._handled = false; | ||
this._defaultValue = defaultValue; | ||
} | ||
/** | ||
* @return {string} | ||
*/ | ||
type() { | ||
return this._type; | ||
} | ||
/** | ||
* @return {string} | ||
*/ | ||
message() { | ||
return this._message; | ||
} | ||
/** | ||
* @return {string} | ||
*/ | ||
defaultValue() { | ||
return this._defaultValue; | ||
} | ||
/** | ||
* @param {string=} promptText | ||
*/ | ||
async accept(promptText) { | ||
assert(!this._handled, 'Cannot accept dialog which is already handled!'); | ||
helper_1.assert(!this._handled, 'Cannot accept dialog which is already handled!'); | ||
this._handled = true; | ||
@@ -62,3 +52,3 @@ await this._client.send('Page.handleJavaScriptDialog', { | ||
async dismiss() { | ||
assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); | ||
helper_1.assert(!this._handled, 'Cannot dismiss dialog which is already handled!'); | ||
this._handled = true; | ||
@@ -70,8 +60,3 @@ await this._client.send('Page.handleJavaScriptDialog', { | ||
} | ||
Dialog.Type = { | ||
Alert: 'alert', | ||
BeforeUnload: 'beforeunload', | ||
Confirm: 'confirm', | ||
Prompt: 'prompt' | ||
}; | ||
Dialog.Type = DialogType; | ||
module.exports = { Dialog }; |
@@ -20,2 +20,11 @@ /** | ||
const { TimeoutError } = require('./Errors'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { JSHandle, ElementHandle } = require('./JSHandle'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { ExecutionContext } = require('./ExecutionContext'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { TimeoutSettings } = require('./TimeoutSettings'); | ||
const readFileAsync = helper.promisify(fs.readFile); | ||
@@ -29,3 +38,3 @@ /** | ||
* @param {!Puppeteer.Frame} frame | ||
* @param {!Puppeteer.TimeoutSettings} timeoutSettings | ||
* @param {!TimeoutSettings} timeoutSettings | ||
*/ | ||
@@ -36,5 +45,5 @@ constructor(frameManager, frame, timeoutSettings) { | ||
this._timeoutSettings = timeoutSettings; | ||
/** @type {?Promise<!Puppeteer.ElementHandle>} */ | ||
/** @type {?Promise<!ElementHandle>} */ | ||
this._documentPromise = null; | ||
/** @type {!Promise<!Puppeteer.ExecutionContext>} */ | ||
/** @type {!Promise<!ExecutionContext>} */ | ||
this._contextPromise; | ||
@@ -54,3 +63,3 @@ this._contextResolveCallback = null; | ||
/** | ||
* @param {?Puppeteer.ExecutionContext} context | ||
* @param {?ExecutionContext} context | ||
*/ | ||
@@ -83,3 +92,3 @@ _setContext(context) { | ||
/** | ||
* @return {!Promise<!Puppeteer.ExecutionContext>} | ||
* @return {!Promise<!ExecutionContext>} | ||
*/ | ||
@@ -94,3 +103,3 @@ executionContext() { | ||
* @param {!Array<*>} args | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -112,3 +121,3 @@ async evaluateHandle(pageFunction, ...args) { | ||
* @param {string} selector | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -121,3 +130,3 @@ async $(selector) { | ||
/** | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -135,3 +144,3 @@ async _document() { | ||
* @param {string} expression | ||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>} | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
@@ -166,3 +175,3 @@ async $x(expression) { | ||
* @param {string} selector | ||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>} | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
@@ -211,3 +220,3 @@ async $$(selector) { | ||
* @param {!{url?: string, path?: string, content?: string, type?: string}} options | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -273,3 +282,3 @@ async addScriptTag(options) { | ||
* @param {!{url?: string, path?: string, content?: string}} options | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -394,3 +403,3 @@ async addStyleTag(options) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -403,3 +412,3 @@ waitForSelector(selector, options) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -412,3 +421,3 @@ waitForXPath(xpath, options) { | ||
* @param {!{polling?: string|number, timeout?: number}=} options | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -429,3 +438,3 @@ waitForFunction(pageFunction, options = {}, ...args) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -517,3 +526,3 @@ async _waitForSelectorOrXPath(selectorOrXPath, isXPath, options = {}) { | ||
const runCount = ++this._runCount; | ||
/** @type {?Puppeteer.JSHandle} */ | ||
/** @type {?JSHandle} */ | ||
let success = null; | ||
@@ -520,0 +529,0 @@ let error = null; |
@@ -16,5 +16,8 @@ /** | ||
*/ | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
class EmulationManager { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
*/ | ||
@@ -21,0 +24,0 @@ constructor(client) { |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -16,12 +17,8 @@ * Copyright 2017 Google Inc. All rights reserved. | ||
*/ | ||
const { helper, assert } = require('./helper'); | ||
const { createJSHandle, JSHandle } = require('./JSHandle'); | ||
const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__'; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const helper_1 = require("./helper"); | ||
const JSHandle_1 = require("./JSHandle"); | ||
exports.EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__'; | ||
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m; | ||
class ExecutionContext { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!Protocol.Runtime.ExecutionContextDescription} contextPayload | ||
* @param {?Puppeteer.DOMWorld} world | ||
*/ | ||
constructor(client, contextPayload, world) { | ||
@@ -32,35 +29,16 @@ this._client = client; | ||
} | ||
/** | ||
* @return {?Puppeteer.Frame} | ||
*/ | ||
frame() { | ||
return this._world ? this._world.frame() : null; | ||
} | ||
/** | ||
* @param {Function|string} pageFunction | ||
* @param {...*} args | ||
* @return {!Promise<*>} | ||
*/ | ||
async evaluate(pageFunction, ...args) { | ||
return await this._evaluateInternal(true /* returnByValue */, pageFunction, ...args); | ||
return await this._evaluateInternal(true, pageFunction, ...args); | ||
} | ||
/** | ||
* @param {Function|string} pageFunction | ||
* @param {...*} args | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
async evaluateHandle(pageFunction, ...args) { | ||
return this._evaluateInternal(false /* returnByValue */, pageFunction, ...args); | ||
return this._evaluateInternal(false, pageFunction, ...args); | ||
} | ||
/** | ||
* @param {boolean} returnByValue | ||
* @param {Function|string} pageFunction | ||
* @param {...*} args | ||
* @return {!Promise<*>} | ||
*/ | ||
async _evaluateInternal(returnByValue, pageFunction, ...args) { | ||
const suffix = `//# sourceURL=${EVALUATION_SCRIPT_URL}`; | ||
if (helper.isString(pageFunction)) { | ||
const suffix = `//# sourceURL=${exports.EVALUATION_SCRIPT_URL}`; | ||
if (helper_1.helper.isString(pageFunction)) { | ||
const contextId = this._contextId; | ||
const expression = /** @type {string} */ (pageFunction); | ||
const expression = pageFunction; | ||
const expressionWithSourceUrl = SOURCE_URL_REGEX.test(expression) ? expression : expression + '\n' + suffix; | ||
@@ -75,4 +53,4 @@ const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { | ||
if (exceptionDetails) | ||
throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails)); | ||
return returnByValue ? helper.valueFromRemoteObject(remoteObject) : createJSHandle(this, remoteObject); | ||
throw new Error('Evaluation failed: ' + helper_1.helper.getExceptionMessage(exceptionDetails)); | ||
return returnByValue ? helper_1.helper.valueFromRemoteObject(remoteObject) : JSHandle_1.createJSHandle(this, remoteObject); | ||
} | ||
@@ -118,4 +96,4 @@ if (typeof pageFunction !== 'function') | ||
if (exceptionDetails) | ||
throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails)); | ||
return returnByValue ? helper.valueFromRemoteObject(remoteObject) : createJSHandle(this, remoteObject); | ||
throw new Error('Evaluation failed: ' + helper_1.helper.getExceptionMessage(exceptionDetails)); | ||
return returnByValue ? helper_1.helper.valueFromRemoteObject(remoteObject) : JSHandle_1.createJSHandle(this, remoteObject); | ||
/** | ||
@@ -137,3 +115,3 @@ * @param {*} arg | ||
return { unserializableValue: 'NaN' }; | ||
const objectHandle = arg && (arg instanceof JSHandle) ? arg : null; | ||
const objectHandle = arg && (arg instanceof JSHandle_1.JSHandle) ? arg : null; | ||
if (objectHandle) { | ||
@@ -152,6 +130,2 @@ if (objectHandle._context !== this) | ||
} | ||
/** | ||
* @param {!Error} error | ||
* @return {!Protocol.Runtime.evaluateReturnValue} | ||
*/ | ||
function rewriteError(error) { | ||
@@ -167,18 +141,10 @@ if (error.message.includes('Object reference chain is too long')) | ||
} | ||
/** | ||
* @param {!JSHandle} prototypeHandle | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
async queryObjects(prototypeHandle) { | ||
assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!'); | ||
assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value'); | ||
helper_1.assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!'); | ||
helper_1.assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value'); | ||
const response = await this._client.send('Runtime.queryObjects', { | ||
prototypeObjectId: prototypeHandle._remoteObject.objectId | ||
}); | ||
return createJSHandle(this, response.objects); | ||
return JSHandle_1.createJSHandle(this, response.objects); | ||
} | ||
/** | ||
* @param {Protocol.DOM.BackendNodeId} backendNodeId | ||
* @return {Promise<Puppeteer.ElementHandle>} | ||
*/ | ||
async _adoptBackendNodeId(backendNodeId) { | ||
@@ -189,11 +155,7 @@ const { object } = await this._client.send('DOM.resolveNode', { | ||
}); | ||
return /** @type {Puppeteer.ElementHandle}*/ (createJSHandle(this, object)); | ||
return JSHandle_1.createJSHandle(this, object); | ||
} | ||
/** | ||
* @param {Puppeteer.ElementHandle} elementHandle | ||
* @return {Promise<Puppeteer.ElementHandle>} | ||
*/ | ||
async _adoptElementHandle(elementHandle) { | ||
assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context'); | ||
assert(this._world, 'Cannot adopt handle without DOMWorld'); | ||
helper_1.assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context'); | ||
helper_1.assert(this._world, 'Cannot adopt handle without DOMWorld'); | ||
const nodeInfo = await this._client.send('DOM.describeNode', { | ||
@@ -205,2 +167,2 @@ objectId: elementHandle._remoteObject.objectId, | ||
} | ||
module.exports = { ExecutionContext, EVALUATION_SCRIPT_URL }; | ||
exports.ExecutionContext = ExecutionContext; |
@@ -23,9 +23,18 @@ /** | ||
const { NetworkManager } = require('./NetworkManager'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { TimeoutSettings } = require('./TimeoutSettings'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { JSHandle, ElementHandle } = require('./JSHandle'); | ||
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; | ||
class FrameManager extends EventEmitter { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {!Puppeteer.Page} page | ||
* @param {boolean} ignoreHTTPSErrors | ||
* @param {!Puppeteer.TimeoutSettings} timeoutSettings | ||
* @param {!TimeoutSettings} timeoutSettings | ||
*/ | ||
@@ -99,3 +108,3 @@ constructor(client, page, ignoreHTTPSErrors, timeoutSettings) { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {string} url | ||
@@ -340,3 +349,3 @@ * @param {string} referrer | ||
* @param {!FrameManager} frameManager | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {?Frame} parentFrame | ||
@@ -388,3 +397,3 @@ * @param {string} frameId | ||
* @param {!Array<*>} args | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -404,3 +413,3 @@ async evaluateHandle(pageFunction, ...args) { | ||
* @param {string} selector | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -412,3 +421,3 @@ async $(selector) { | ||
* @param {string} expression | ||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>} | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
@@ -438,3 +447,3 @@ async $x(expression) { | ||
* @param {string} selector | ||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>} | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
@@ -489,3 +498,3 @@ async $$(selector) { | ||
* @param {!{url?: string, path?: string, content?: string, type?: string}} options | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -497,3 +506,3 @@ async addScriptTag(options) { | ||
* @param {!{url?: string, path?: string, content?: string}} options | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -548,3 +557,3 @@ async addStyleTag(options) { | ||
* @param {!Array<*>} args | ||
* @return {!Promise<?Puppeteer.JSHandle>} | ||
* @return {!Promise<?JSHandle>} | ||
*/ | ||
@@ -568,3 +577,3 @@ waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -583,3 +592,3 @@ async waitForSelector(selector, options) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -598,3 +607,3 @@ async waitForXPath(xpath, options) { | ||
* @param {!{polling?: string|number, timeout?: number}=} options | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -601,0 +610,0 @@ waitForFunction(pageFunction, options = {}, ...args) { |
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -16,261 +18,190 @@ * Copyright 2017 Google Inc. All rights reserved. | ||
*/ | ||
const { TimeoutError } = require('./Errors'); | ||
const debugError = require('debug')(`puppeteer:error`); | ||
const fs = require('fs'); | ||
class Helper { | ||
/** | ||
* @param {Function|string} fun | ||
* @param {!Array<*>} args | ||
* @return {string} | ||
*/ | ||
static evaluationString(fun, ...args) { | ||
if (Helper.isString(fun)) { | ||
assert(args.length === 0, 'Cannot evaluate a string with arguments'); | ||
return /** @type {string} */ (fun); | ||
const Errors = require("./Errors"); | ||
const debug = require("debug"); | ||
const fs = require("fs"); | ||
const promisify = require("./promisify"); | ||
const { TimeoutError } = Errors; | ||
const openAsync = promisify(fs.open); | ||
const writeAsync = promisify(fs.write); | ||
const closeAsync = promisify(fs.close); | ||
exports.debugError = debug('puppeteer:error'); | ||
function assert(value, message) { | ||
if (!value) | ||
throw new Error(message); | ||
} | ||
exports.assert = assert; | ||
function getExceptionMessage(exceptionDetails) { | ||
if (exceptionDetails.exception) | ||
return exceptionDetails.exception.description || exceptionDetails.exception.value; | ||
let message = exceptionDetails.text; | ||
if (exceptionDetails.stackTrace) { | ||
for (const callframe of exceptionDetails.stackTrace.callFrames) { | ||
const location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; | ||
const functionName = callframe.functionName || '<anonymous>'; | ||
message += `\n at ${functionName} (${location})`; | ||
} | ||
return `(${fun})(${args.map(serializeArgument).join(',')})`; | ||
/** | ||
* @param {*} arg | ||
* @return {string} | ||
*/ | ||
function serializeArgument(arg) { | ||
if (Object.is(arg, undefined)) | ||
return 'undefined'; | ||
return JSON.stringify(arg); | ||
} | ||
} | ||
/** | ||
* @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails | ||
* @return {string} | ||
*/ | ||
static getExceptionMessage(exceptionDetails) { | ||
if (exceptionDetails.exception) | ||
return exceptionDetails.exception.description || exceptionDetails.exception.value; | ||
let message = exceptionDetails.text; | ||
if (exceptionDetails.stackTrace) { | ||
for (const callframe of exceptionDetails.stackTrace.callFrames) { | ||
const location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber; | ||
const functionName = callframe.functionName || '<anonymous>'; | ||
message += `\n at ${functionName} (${location})`; | ||
} | ||
return message; | ||
} | ||
function valueFromRemoteObject(remoteObject) { | ||
assert(!remoteObject.objectId, 'Cannot extract value when objectId is given'); | ||
if (remoteObject.unserializableValue) { | ||
if (remoteObject.type === 'bigint' && typeof BigInt !== 'undefined') | ||
return BigInt(remoteObject.unserializableValue.replace('n', '')); | ||
switch (remoteObject.unserializableValue) { | ||
case '-0': | ||
return -0; | ||
case 'NaN': | ||
return NaN; | ||
case 'Infinity': | ||
return Infinity; | ||
case '-Infinity': | ||
return -Infinity; | ||
default: | ||
throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue); | ||
} | ||
return message; | ||
} | ||
/** | ||
* @param {!Protocol.Runtime.RemoteObject} remoteObject | ||
* @return {*} | ||
*/ | ||
static valueFromRemoteObject(remoteObject) { | ||
assert(!remoteObject.objectId, 'Cannot extract value when objectId is given'); | ||
if (remoteObject.unserializableValue) { | ||
if (remoteObject.type === 'bigint' && typeof BigInt !== 'undefined') | ||
return BigInt(remoteObject.unserializableValue.replace('n', '')); | ||
switch (remoteObject.unserializableValue) { | ||
case '-0': | ||
return -0; | ||
case 'NaN': | ||
return NaN; | ||
case 'Infinity': | ||
return Infinity; | ||
case '-Infinity': | ||
return -Infinity; | ||
default: | ||
throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue); | ||
} | ||
} | ||
return remoteObject.value; | ||
return remoteObject.value; | ||
} | ||
async function releaseObject(client, remoteObject) { | ||
if (!remoteObject.objectId) | ||
return; | ||
await client.send('Runtime.releaseObject', { objectId: remoteObject.objectId }).catch(error => { | ||
// Exceptions might happen in case of a page been navigated or closed. | ||
// Swallow these since they are harmless and we don't leak anything in this case. | ||
exports.debugError(error); | ||
}); | ||
} | ||
function installAsyncStackHooks(classType) { | ||
for (const methodName of Reflect.ownKeys(classType.prototype)) { | ||
const method = Reflect.get(classType.prototype, methodName); | ||
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction') | ||
continue; | ||
Reflect.set(classType.prototype, methodName, function (...args) { | ||
const syncStack = { | ||
stack: '' | ||
}; | ||
Error.captureStackTrace(syncStack); | ||
return method.call(this, ...args).catch(e => { | ||
const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1); | ||
const clientStack = stack.substring(stack.indexOf('\n')); | ||
if (e instanceof Error && e.stack && !e.stack.includes(clientStack)) | ||
e.stack += '\n -- ASYNC --\n' + stack; | ||
throw e; | ||
}); | ||
}); | ||
} | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!Protocol.Runtime.RemoteObject} remoteObject | ||
*/ | ||
static async releaseObject(client, remoteObject) { | ||
if (!remoteObject.objectId) | ||
} | ||
function addEventListener(emitter, eventName, handler) { | ||
emitter.on(eventName, handler); | ||
return { emitter, eventName, handler }; | ||
} | ||
function removeEventListeners(listeners) { | ||
for (const listener of listeners) | ||
listener.emitter.removeListener(listener.eventName, listener.handler); | ||
listeners.length = 0; | ||
} | ||
function isString(obj) { | ||
return typeof obj === 'string' || obj instanceof String; | ||
} | ||
function isNumber(obj) { | ||
return typeof obj === 'number' || obj instanceof Number; | ||
} | ||
async function waitForEvent(emitter, eventName, predicate, timeout, abortPromise) { | ||
let eventTimeout, resolveCallback, rejectCallback; | ||
const promise = new Promise((resolve, reject) => { | ||
resolveCallback = resolve; | ||
rejectCallback = reject; | ||
}); | ||
const listener = addEventListener(emitter, eventName, event => { | ||
if (!predicate(event)) | ||
return; | ||
await client.send('Runtime.releaseObject', { objectId: remoteObject.objectId }).catch(error => { | ||
// Exceptions might happen in case of a page been navigated or closed. | ||
// Swallow these since they are harmless and we don't leak anything in this case. | ||
debugError(error); | ||
}); | ||
resolveCallback(event); | ||
}); | ||
if (timeout) { | ||
eventTimeout = setTimeout(() => { | ||
rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); | ||
}, timeout); | ||
} | ||
/** | ||
* @param {!Object} classType | ||
*/ | ||
static installAsyncStackHooks(classType) { | ||
for (const methodName of Reflect.ownKeys(classType.prototype)) { | ||
const method = Reflect.get(classType.prototype, methodName); | ||
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction') | ||
continue; | ||
Reflect.set(classType.prototype, methodName, function (...args) { | ||
const syncStack = {}; | ||
Error.captureStackTrace(syncStack); | ||
return method.call(this, ...args).catch(e => { | ||
const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1); | ||
const clientStack = stack.substring(stack.indexOf('\n')); | ||
if (e instanceof Error && e.stack && !e.stack.includes(clientStack)) | ||
e.stack += '\n -- ASYNC --\n' + stack; | ||
throw e; | ||
}); | ||
}); | ||
} | ||
function cleanup() { | ||
removeEventListeners([listener]); | ||
clearTimeout(eventTimeout); | ||
} | ||
/** | ||
* @param {!NodeJS.EventEmitter} emitter | ||
* @param {(string|symbol)} eventName | ||
* @param {function(?):void} handler | ||
* @return {{emitter: !NodeJS.EventEmitter, eventName: (string|symbol), handler: function(?)}} | ||
*/ | ||
static addEventListener(emitter, eventName, handler) { | ||
emitter.on(eventName, handler); | ||
return { emitter, eventName, handler }; | ||
const result = await Promise.race([promise, abortPromise]).then(r => { | ||
cleanup(); | ||
return r; | ||
}, e => { | ||
cleanup(); | ||
throw e; | ||
}); | ||
if (result instanceof Error) | ||
throw result; | ||
return result; | ||
} | ||
function evaluationString(fun, ...args) { | ||
if (isString(fun)) { | ||
assert(args.length === 0, 'Cannot evaluate a string with arguments'); | ||
return fun; | ||
} | ||
/** | ||
* @param {!Array<{emitter: !NodeJS.EventEmitter, eventName: (string|symbol), handler: function(?):void}>} listeners | ||
*/ | ||
static removeEventListeners(listeners) { | ||
for (const listener of listeners) | ||
listener.emitter.removeListener(listener.eventName, listener.handler); | ||
listeners.length = 0; | ||
function serializeArgument(arg) { | ||
if (Object.is(arg, undefined)) | ||
return 'undefined'; | ||
return JSON.stringify(arg); | ||
} | ||
/** | ||
* @param {!Object} obj | ||
* @return {boolean} | ||
*/ | ||
static isString(obj) { | ||
return typeof obj === 'string' || obj instanceof String; | ||
return `(${fun})(${args.map(serializeArgument).join(',')})`; | ||
} | ||
async function waitWithTimeout(promise, taskName, timeout) { | ||
let reject; | ||
const timeoutError = new TimeoutError(`waiting for ${taskName} failed: timeout ${timeout}ms exceeded`); | ||
const timeoutPromise = new Promise((resolve, x) => reject = x); | ||
let timeoutTimer = null; | ||
if (timeout) | ||
timeoutTimer = setTimeout(() => reject(timeoutError), timeout); | ||
try { | ||
return await Promise.race([promise, timeoutPromise]); | ||
} | ||
/** | ||
* @param {!Object} obj | ||
* @return {boolean} | ||
*/ | ||
static isNumber(obj) { | ||
return typeof obj === 'number' || obj instanceof Number; | ||
finally { | ||
if (timeoutTimer) | ||
clearTimeout(timeoutTimer); | ||
} | ||
/** | ||
* @param {function} nodeFunction | ||
* @return {function} | ||
*/ | ||
static promisify(nodeFunction) { | ||
function promisified(...args) { | ||
return new Promise((resolve, reject) => { | ||
function callback(err, ...result) { | ||
if (err) | ||
return reject(err); | ||
if (result.length === 1) | ||
return resolve(result[0]); | ||
return resolve(result); | ||
} | ||
nodeFunction.call(null, ...args, callback); | ||
}); | ||
} | ||
return promisified; | ||
} | ||
async function readProtocolStream(client, handle, path) { | ||
let eof = false; | ||
let file; | ||
if (path) | ||
file = await openAsync(path, 'w'); | ||
const bufs = []; | ||
while (!eof) { | ||
const response = await client.send('IO.read', { handle }); | ||
eof = response.eof; | ||
const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined); | ||
bufs.push(buf); | ||
if (path) | ||
await writeAsync(file, buf); | ||
} | ||
/** | ||
* @param {!NodeJS.EventEmitter} emitter | ||
* @param {(string|symbol)} eventName | ||
* @param {function} predicate | ||
* @param {number} timeout | ||
* @param {!Promise<!Error>} abortPromise | ||
* @return {!Promise} | ||
*/ | ||
static async waitForEvent(emitter, eventName, predicate, timeout, abortPromise) { | ||
let eventTimeout, resolveCallback, rejectCallback; | ||
const promise = new Promise((resolve, reject) => { | ||
resolveCallback = resolve; | ||
rejectCallback = reject; | ||
}); | ||
const listener = Helper.addEventListener(emitter, eventName, event => { | ||
if (!predicate(event)) | ||
return; | ||
resolveCallback(event); | ||
}); | ||
if (timeout) { | ||
eventTimeout = setTimeout(() => { | ||
rejectCallback(new TimeoutError('Timeout exceeded while waiting for event')); | ||
}, timeout); | ||
} | ||
function cleanup() { | ||
Helper.removeEventListeners([listener]); | ||
clearTimeout(eventTimeout); | ||
} | ||
const result = await Promise.race([promise, abortPromise]).then(r => { | ||
cleanup(); | ||
return r; | ||
}, e => { | ||
cleanup(); | ||
throw e; | ||
}); | ||
if (result instanceof Error) | ||
throw result; | ||
return result; | ||
if (path) | ||
await closeAsync(file); | ||
await client.send('IO.close', { handle }); | ||
let resultBuffer = null; | ||
try { | ||
resultBuffer = Buffer.concat(bufs); | ||
} | ||
/** | ||
* @template T | ||
* @param {!Promise<T>} promise | ||
* @param {string} taskName | ||
* @param {number} timeout | ||
* @return {!Promise<T>} | ||
*/ | ||
static async waitWithTimeout(promise, taskName, timeout) { | ||
let reject; | ||
const timeoutError = new TimeoutError(`waiting for ${taskName} failed: timeout ${timeout}ms exceeded`); | ||
const timeoutPromise = new Promise((resolve, x) => reject = x); | ||
let timeoutTimer = null; | ||
if (timeout) | ||
timeoutTimer = setTimeout(() => reject(timeoutError), timeout); | ||
try { | ||
return await Promise.race([promise, timeoutPromise]); | ||
} | ||
finally { | ||
if (timeoutTimer) | ||
clearTimeout(timeoutTimer); | ||
} | ||
finally { | ||
return resultBuffer; | ||
} | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {string} handle | ||
* @param {?string} path | ||
* @return {!Promise<!Buffer>} | ||
*/ | ||
static async readProtocolStream(client, handle, path) { | ||
let eof = false; | ||
let file; | ||
if (path) | ||
file = await openAsync(path, 'w'); | ||
const bufs = []; | ||
while (!eof) { | ||
const response = await client.send('IO.read', { handle }); | ||
eof = response.eof; | ||
const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined); | ||
bufs.push(buf); | ||
if (path) | ||
await writeAsync(file, buf); | ||
} | ||
if (path) | ||
await closeAsync(file); | ||
await client.send('IO.close', { handle }); | ||
let resultBuffer = null; | ||
try { | ||
resultBuffer = Buffer.concat(bufs); | ||
} | ||
finally { | ||
return resultBuffer; | ||
} | ||
} | ||
} | ||
const openAsync = Helper.promisify(fs.open); | ||
const writeAsync = Helper.promisify(fs.write); | ||
const closeAsync = Helper.promisify(fs.close); | ||
/** | ||
* @param {*} value | ||
* @param {string=} message | ||
*/ | ||
function assert(value, message) { | ||
if (!value) | ||
throw new Error(message); | ||
} | ||
module.exports = { | ||
helper: Helper, | ||
assert, | ||
debugError | ||
exports.helper = { | ||
promisify, | ||
evaluationString, | ||
readProtocolStream, | ||
waitWithTimeout, | ||
waitForEvent, | ||
isString, | ||
isNumber, | ||
addEventListener, | ||
removeEventListeners, | ||
valueFromRemoteObject, | ||
installAsyncStackHooks, | ||
getExceptionMessage, | ||
releaseObject, | ||
}; |
@@ -17,3 +17,6 @@ /** | ||
const { assert } = require('./helper'); | ||
const keyDefinitions = require('./USKeyboardLayout'); | ||
// CDPSession is used only as a typedef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
const { keyDefinitions } = require('./USKeyboardLayout'); | ||
/** | ||
@@ -29,3 +32,3 @@ * @typedef {Object} KeyDescription | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
*/ | ||
@@ -166,3 +169,3 @@ constructor(client) { | ||
/** | ||
* @param {Puppeteer.CDPSession} client | ||
* @param {CDPSession} client | ||
* @param {!Keyboard} keyboard | ||
@@ -254,3 +257,3 @@ */ | ||
/** | ||
* @param {Puppeteer.CDPSession} client | ||
* @param {CDPSession} client | ||
* @param {Keyboard} keyboard | ||
@@ -257,0 +260,0 @@ */ |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -16,3 +17,4 @@ * Copyright 2019 Google Inc. All rights reserved. | ||
*/ | ||
const { helper, assert, debugError } = require('./helper'); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const helper_1 = require("./helper"); | ||
function createJSHandle(context, remoteObject) { | ||
@@ -26,40 +28,19 @@ const frame = context.frame(); | ||
} | ||
exports.createJSHandle = createJSHandle; | ||
class JSHandle { | ||
/** | ||
* @param {!Puppeteer.ExecutionContext} context | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!Protocol.Runtime.RemoteObject} remoteObject | ||
*/ | ||
constructor(context, client, remoteObject) { | ||
this._disposed = false; | ||
this._context = context; | ||
this._client = client; | ||
this._remoteObject = remoteObject; | ||
this._disposed = false; | ||
} | ||
/** | ||
* @return {!Puppeteer.ExecutionContext} | ||
*/ | ||
executionContext() { | ||
return this._context; | ||
} | ||
/** | ||
* @param {Function|String} pageFunction | ||
* @param {!Array<*>} args | ||
* @return {!Promise<(!Object|undefined)>} | ||
*/ | ||
async evaluate(pageFunction, ...args) { | ||
return await this.executionContext().evaluate(pageFunction, this, ...args); | ||
} | ||
/** | ||
* @param {Function|string} pageFunction | ||
* @param {!Array<*>} args | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
*/ | ||
async evaluateHandle(pageFunction, ...args) { | ||
return await this.executionContext().evaluateHandle(pageFunction, this, ...args); | ||
} | ||
/** | ||
* @param {string} propertyName | ||
* @return {!Promise<?JSHandle>} | ||
*/ | ||
async getProperty(propertyName) { | ||
@@ -76,5 +57,2 @@ const objectHandle = await this.evaluateHandle((object, propertyName) => { | ||
} | ||
/** | ||
* @return {!Promise<!Map<string, !JSHandle>>} | ||
*/ | ||
async getProperties() { | ||
@@ -93,5 +71,2 @@ const response = await this._client.send('Runtime.getProperties', { | ||
} | ||
/** | ||
* @return {!Promise<?Object>} | ||
*/ | ||
async jsonValue() { | ||
@@ -105,9 +80,7 @@ if (this._remoteObject.objectId) { | ||
}); | ||
return helper.valueFromRemoteObject(response.result); | ||
return helper_1.helper.valueFromRemoteObject(response.result); | ||
} | ||
return helper.valueFromRemoteObject(this._remoteObject); | ||
return helper_1.helper.valueFromRemoteObject(this._remoteObject); | ||
} | ||
/** | ||
* @return {?Puppeteer.ElementHandle} | ||
*/ | ||
/* This always returns null but children can define this and return an ElementHandle */ | ||
asElement() { | ||
@@ -120,8 +93,4 @@ return null; | ||
this._disposed = true; | ||
await helper.releaseObject(this._client, this._remoteObject); | ||
await helper_1.helper.releaseObject(this._client, this._remoteObject); | ||
} | ||
/** | ||
* @override | ||
* @return {string} | ||
*/ | ||
toString() { | ||
@@ -132,9 +101,10 @@ if (this._remoteObject.objectId) { | ||
} | ||
return 'JSHandle:' + helper.valueFromRemoteObject(this._remoteObject); | ||
return 'JSHandle:' + helper_1.helper.valueFromRemoteObject(this._remoteObject); | ||
} | ||
} | ||
exports.JSHandle = JSHandle; | ||
class ElementHandle extends JSHandle { | ||
/** | ||
* @param {!Puppeteer.ExecutionContext} context | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!ExecutionContext} context | ||
* @param {!CDPSession} client | ||
* @param {!Protocol.Runtime.RemoteObject} remoteObject | ||
@@ -150,14 +120,6 @@ * @param {!Puppeteer.Page} page | ||
this._frameManager = frameManager; | ||
this._disposed = false; | ||
} | ||
/** | ||
* @override | ||
* @return {?ElementHandle} | ||
*/ | ||
asElement() { | ||
return this; | ||
} | ||
/** | ||
* @return {!Promise<?Puppeteer.Frame>} | ||
*/ | ||
async contentFrame() { | ||
@@ -179,2 +141,5 @@ const nodeInfo = await this._client.send('DOM.describeNode', { | ||
if (!pageJavascriptEnabled) { | ||
// Chrome still supports behavior: instant but it's not in the spec so TS shouts | ||
// We don't want to make this breaking change in Puppeteer yet so we'll ignore the line. | ||
// @ts-ignore | ||
element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' }); | ||
@@ -190,4 +155,8 @@ return false; | ||
}); | ||
if (visibleRatio !== 1.0) | ||
if (visibleRatio !== 1.0) { | ||
// Chrome still supports behavior: instant but it's not in the spec so TS shouts | ||
// We don't want to make this breaking change in Puppeteer yet so we'll ignore the line. | ||
// @ts-ignore | ||
element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' }); | ||
} | ||
return false; | ||
@@ -198,5 +167,2 @@ }, this._page._javascriptEnabled); | ||
} | ||
/** | ||
* @return {!Promise<!{x: number, y: number}>} | ||
*/ | ||
async _clickablePoint() { | ||
@@ -206,3 +172,3 @@ const [result, layoutMetrics] = await Promise.all([ | ||
objectId: this._remoteObject.objectId | ||
}).catch(debugError), | ||
}).catch(helper_1.debugError), | ||
this._client.send('Page.getLayoutMetrics'), | ||
@@ -230,14 +196,7 @@ ]); | ||
} | ||
/** | ||
* @return {!Promise<void|Protocol.DOM.getBoxModelReturnValue>} | ||
*/ | ||
_getBoxModel() { | ||
return this._client.send('DOM.getBoxModel', { | ||
objectId: this._remoteObject.objectId | ||
}).catch(error => debugError(error)); | ||
}).catch(error => helper_1.debugError(error)); | ||
} | ||
/** | ||
* @param {!Array<number>} quad | ||
* @return {!Array<{x: number, y: number}>} | ||
*/ | ||
_fromProtocolQuad(quad) { | ||
@@ -268,5 +227,2 @@ return [ | ||
} | ||
/** | ||
* @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options | ||
*/ | ||
async click(options) { | ||
@@ -283,3 +239,7 @@ await this._scrollIntoViewIfNeeded(); | ||
for (const value of values) | ||
assert(helper.isString(value), 'Values must be strings. Found value "' + value + '" of type "' + (typeof value) + '"'); | ||
helper_1.assert(helper_1.helper.isString(value), 'Values must be strings. Found value "' + value + '" of type "' + (typeof value) + '"'); | ||
/* TODO(jacktfranklin@): once ExecutionContext is TypeScript, and | ||
* its evaluate function is properly typed with generics we can | ||
* return here and remove the typecasting | ||
*/ | ||
return this.evaluate((element, values) => { | ||
@@ -300,37 +260,27 @@ if (element.nodeName.toLowerCase() !== 'select') | ||
} | ||
/** | ||
* @param {!Array<string>} filePaths | ||
*/ | ||
async uploadFile(...filePaths) { | ||
const isMultiple = await this.evaluate(element => element.multiple); | ||
assert(filePaths.length <= 1 || isMultiple, 'Multiple file uploads only work with <input type=file multiple>'); | ||
// These imports are only needed for `uploadFile`, so keep them | ||
// scoped here to avoid paying the cost unnecessarily. | ||
const isMultiple = await this.evaluate((element) => element.multiple); | ||
helper_1.assert(filePaths.length <= 1 || isMultiple, 'Multiple file uploads only work with <input type=file multiple>'); | ||
// This import is only needed for `uploadFile`, so keep it scoped here to avoid paying | ||
// the cost unnecessarily. | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const path = require('path'); | ||
const mime = require('mime-types'); | ||
const fs = require('fs'); | ||
const readFileAsync = helper.promisify(fs.readFile); | ||
const promises = filePaths.map(filePath => readFileAsync(filePath)); | ||
const files = []; | ||
for (let i = 0; i < filePaths.length; i++) { | ||
const buffer = await promises[i]; | ||
const filePath = path.basename(filePaths[i]); | ||
const file = { | ||
name: filePath, | ||
content: buffer.toString('base64'), | ||
mimeType: mime.lookup(filePath), | ||
}; | ||
files.push(file); | ||
const files = filePaths.map(filePath => path.resolve(filePath)); | ||
const { objectId } = this._remoteObject; | ||
const { node } = await this._client.send('DOM.describeNode', { objectId }); | ||
const { backendNodeId } = node; | ||
// The zero-length array is a special case, it seems that DOM.setFileInputFiles does | ||
// not actually update the files in that case, so the solution is to eval the element | ||
// value to a new FileList directly. | ||
if (files.length === 0) { | ||
await this.evaluate((element) => { | ||
element.files = new DataTransfer().files; | ||
// Dispatch events for this case because it should behave akin to a user action. | ||
element.dispatchEvent(new Event('input', { bubbles: true })); | ||
element.dispatchEvent(new Event('change', { bubbles: true })); | ||
}); | ||
} | ||
await this.evaluateHandle(async (element, files) => { | ||
const dt = new DataTransfer(); | ||
for (const item of files) { | ||
const response = await fetch(`data:${item.mimeType};base64,${item.content}`); | ||
const file = new File([await response.blob()], item.name); | ||
dt.items.add(file); | ||
} | ||
element.files = dt.files; | ||
element.dispatchEvent(new Event('input', { bubbles: true })); | ||
element.dispatchEvent(new Event('change', { bubbles: true })); | ||
}, files); | ||
else { | ||
await this._client.send('DOM.setFileInputFiles', { objectId, files, backendNodeId }); | ||
} | ||
} | ||
@@ -345,6 +295,2 @@ async tap() { | ||
} | ||
/** | ||
* @param {string} text | ||
* @param {{delay: (number|undefined)}=} options | ||
*/ | ||
async type(text, options) { | ||
@@ -354,6 +300,2 @@ await this.focus(); | ||
} | ||
/** | ||
* @param {string} key | ||
* @param {!{delay?: number, text?: string}=} options | ||
*/ | ||
async press(key, options) { | ||
@@ -363,5 +305,2 @@ await this.focus(); | ||
} | ||
/** | ||
* @return {!Promise<?{x: number, y: number, width: number, height: number}>} | ||
*/ | ||
async boundingBox() { | ||
@@ -395,11 +334,6 @@ const result = await this._getBoxModel(); | ||
} | ||
/** | ||
* | ||
* @param {!Object=} options | ||
* @returns {!Promise<string|!Buffer>} | ||
*/ | ||
async screenshot(options = {}) { | ||
let needsViewportReset = false; | ||
let boundingBox = await this.boundingBox(); | ||
assert(boundingBox, 'Node is either not visible or not an HTMLElement'); | ||
helper_1.assert(boundingBox, 'Node is either not visible or not an HTMLElement'); | ||
const viewport = this._page.viewport(); | ||
@@ -416,5 +350,5 @@ if (viewport && (boundingBox.width > viewport.width || boundingBox.height > viewport.height)) { | ||
boundingBox = await this.boundingBox(); | ||
assert(boundingBox, 'Node is either not visible or not an HTMLElement'); | ||
assert(boundingBox.width !== 0, 'Node has 0 width.'); | ||
assert(boundingBox.height !== 0, 'Node has 0 height.'); | ||
helper_1.assert(boundingBox, 'Node is either not visible or not an HTMLElement'); | ||
helper_1.assert(boundingBox.width !== 0, 'Node has 0 width.'); | ||
helper_1.assert(boundingBox.height !== 0, 'Node has 0 height.'); | ||
const { layoutViewport: { pageX, pageY } } = await this._client.send('Page.getLayoutMetrics'); | ||
@@ -431,6 +365,2 @@ const clip = Object.assign({}, boundingBox); | ||
} | ||
/** | ||
* @param {string} selector | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
async $(selector) { | ||
@@ -460,8 +390,2 @@ const handle = await this.evaluateHandle((element, selector) => element.querySelector(selector), selector); | ||
} | ||
/** | ||
* @param {string} selector | ||
* @param {Function|String} pageFunction | ||
* @param {!Array<*>} args | ||
* @return {!Promise<(!Object|undefined)>} | ||
*/ | ||
async $eval(selector, pageFunction, ...args) { | ||
@@ -475,8 +399,2 @@ const elementHandle = await this.$(selector); | ||
} | ||
/** | ||
* @param {string} selector | ||
* @param {Function|String} pageFunction | ||
* @param {!Array<*>} args | ||
* @return {!Promise<(!Object|undefined)>} | ||
*/ | ||
async $$eval(selector, pageFunction, ...args) { | ||
@@ -488,6 +406,2 @@ const arrayHandle = await this.evaluateHandle((element, selector) => Array.from(element.querySelectorAll(selector)), selector); | ||
} | ||
/** | ||
* @param {string} expression | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
async $x(expression) { | ||
@@ -513,7 +427,4 @@ const arrayHandle = await this.evaluateHandle((element, expression) => { | ||
} | ||
/** | ||
* @returns {!Promise<boolean>} | ||
*/ | ||
isIntersectingViewport() { | ||
return this.evaluate(async (element) => { | ||
async isIntersectingViewport() { | ||
return await this.evaluate(async (element) => { | ||
const visibleRatio = await new Promise(resolve => { | ||
@@ -530,2 +441,3 @@ const observer = new IntersectionObserver(entries => { | ||
} | ||
exports.ElementHandle = ElementHandle; | ||
function computeQuadArea(quad) { | ||
@@ -542,11 +454,1 @@ // Compute sum of all directed areas of adjacent triangles | ||
} | ||
/** | ||
* @typedef {Object} BoxModel | ||
* @property {!Array<!{x: number, y: number}>} content | ||
* @property {!Array<!{x: number, y: number}>} padding | ||
* @property {!Array<!{x: number, y: number}>} border | ||
* @property {!Array<!{x: number, y: number}>} margin | ||
* @property {number} width | ||
* @property {number} height | ||
*/ | ||
module.exports = { createJSHandle, JSHandle, ElementHandle }; |
@@ -31,4 +31,4 @@ /** | ||
const { TimeoutError } = require('./Errors'); | ||
const WebSocketTransport = require('./WebSocketTransport'); | ||
const PipeTransport = require('./PipeTransport'); | ||
const { WebSocketTransport } = require('./WebSocketTransport'); | ||
const { PipeTransport } = require('./PipeTransport'); | ||
const mkdtempAsync = helper.promisify(fs.mkdtemp); | ||
@@ -35,0 +35,0 @@ const removeFolderAsync = helper.promisify(removeFolder); |
@@ -19,5 +19,8 @@ /** | ||
const { Events } = require('./Events'); | ||
// CDPSession is used only as a typedef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
class NetworkManager extends EventEmitter { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {!Puppeteer.FrameManager} frameManager | ||
@@ -293,3 +296,3 @@ */ | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {?Puppeteer.Frame} frame | ||
@@ -481,3 +484,3 @@ * @param {string} interceptionId | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {!Request} request | ||
@@ -484,0 +487,0 @@ * @param {!Protocol.Network.Response} responsePayload |
@@ -20,3 +20,5 @@ /** | ||
const { Events } = require('./Events'); | ||
const { Connection } = require('./Connection'); | ||
// CDPSession is used only as a typedef | ||
// eslint-disable-next-line no-unused-vars | ||
const { Connection, CDPSession } = require('./Connection'); | ||
const { Dialog } = require('./Dialog'); | ||
@@ -30,13 +32,19 @@ const { EmulationManager } = require('./EmulationManager'); | ||
const { Worker: PuppeteerWorker } = require('./Worker'); | ||
const { createJSHandle } = require('./JSHandle'); | ||
// Import used as typedef | ||
// eslint-disable-next-line no-unused-vars | ||
const { createJSHandle, JSHandle, ElementHandle } = require('./JSHandle'); | ||
const { Accessibility } = require('./Accessibility'); | ||
const { TimeoutSettings } = require('./TimeoutSettings'); | ||
// This import is used as a TypeDef, but ESLint's rule doesn't | ||
// understand that unfortunately. | ||
// eslint-disable-next-line no-unused-vars | ||
const { TaskQueue } = require('./TaskQueue'); | ||
const writeFileAsync = helper.promisify(fs.writeFile); | ||
class Page extends EventEmitter { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {!Puppeteer.Target} target | ||
* @param {boolean} ignoreHTTPSErrors | ||
* @param {?Puppeteer.Viewport} defaultViewport | ||
* @param {!Puppeteer.TaskQueue} screenshotTaskQueue | ||
* @param {!TaskQueue} screenshotTaskQueue | ||
* @return {!Promise<!Page>} | ||
@@ -52,6 +60,6 @@ */ | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
* @param {!Puppeteer.Target} target | ||
* @param {boolean} ignoreHTTPSErrors | ||
* @param {!Puppeteer.TaskQueue} screenshotTaskQueue | ||
* @param {!TaskQueue} screenshotTaskQueue | ||
*/ | ||
@@ -282,3 +290,3 @@ constructor(client, target, ignoreHTTPSErrors, screenshotTaskQueue) { | ||
* @param {string} selector | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -291,3 +299,3 @@ async $(selector) { | ||
* @param {!Array<*>} args | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -299,4 +307,4 @@ async evaluateHandle(pageFunction, ...args) { | ||
/** | ||
* @param {!Puppeteer.JSHandle} prototypeHandle | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @param {!JSHandle} prototypeHandle | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -327,3 +335,3 @@ async queryObjects(prototypeHandle) { | ||
* @param {string} selector | ||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>} | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
@@ -335,3 +343,3 @@ async $$(selector) { | ||
* @param {string} expression | ||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>} | ||
* @return {!Promise<!Array<!ElementHandle>>} | ||
*/ | ||
@@ -389,3 +397,3 @@ async $x(expression) { | ||
* @param {!{url?: string, path?: string, content?: string, type?: string}} options | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -397,3 +405,3 @@ async addScriptTag(options) { | ||
* @param {!{url?: string, path?: string, content?: string}} options | ||
* @return {!Promise<!Puppeteer.ElementHandle>} | ||
* @return {!Promise<!ElementHandle>} | ||
*/ | ||
@@ -562,3 +570,3 @@ async addStyleTag(options) { | ||
* @param {string} type | ||
* @param {!Array<!Puppeteer.JSHandle>} args | ||
* @param {!Array<!JSHandle>} args | ||
* @param {Protocol.Runtime.StackTrace=} stackTrace | ||
@@ -1007,3 +1015,3 @@ */ | ||
* @param {!Array<*>} args | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -1016,3 +1024,3 @@ waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -1025,3 +1033,3 @@ waitForSelector(selector, options = {}) { | ||
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options | ||
* @return {!Promise<?Puppeteer.ElementHandle>} | ||
* @return {!Promise<?ElementHandle>} | ||
*/ | ||
@@ -1035,3 +1043,3 @@ waitForXPath(xpath, options = {}) { | ||
* @param {!Array<*>} args | ||
* @return {!Promise<!Puppeteer.JSHandle>} | ||
* @return {!Promise<!JSHandle>} | ||
*/ | ||
@@ -1194,3 +1202,3 @@ waitForFunction(pageFunction, options = {}, ...args) { | ||
* @param {string} text | ||
* @param {!Array<!Puppeteer.JSHandle>} args | ||
* @param {!Array<!JSHandle>} args | ||
* @param {ConsoleMessage.Location} location | ||
@@ -1217,3 +1225,3 @@ */ | ||
/** | ||
* @return {!Array<!Puppeteer.JSHandle>} | ||
* @return {!Array<!JSHandle>} | ||
*/ | ||
@@ -1232,4 +1240,4 @@ args() { | ||
/** | ||
* @param {Puppeteer.CDPSession} client | ||
* @param {Puppeteer.ElementHandle} element | ||
* @param {CDPSession} client | ||
* @param {ElementHandle} element | ||
* @param {!Protocol.Page.fileChooserOpenedPayload} event | ||
@@ -1236,0 +1244,0 @@ */ |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -16,11 +17,4 @@ * Copyright 2018 Google Inc. All rights reserved. | ||
*/ | ||
const { helper, debugError } = require('./helper'); | ||
/** | ||
* @implements {!Puppeteer.ConnectionTransport} | ||
*/ | ||
const helper_1 = require("./helper"); | ||
class PipeTransport { | ||
/** | ||
* @param {!NodeJS.WritableStream} pipeWrite | ||
* @param {!NodeJS.ReadableStream} pipeRead | ||
*/ | ||
constructor(pipeWrite, pipeRead) { | ||
@@ -30,9 +24,9 @@ this._pipeWrite = pipeWrite; | ||
this._eventListeners = [ | ||
helper.addEventListener(pipeRead, 'data', buffer => this._dispatch(buffer)), | ||
helper.addEventListener(pipeRead, 'close', () => { | ||
helper_1.helper.addEventListener(pipeRead, 'data', buffer => this._dispatch(buffer)), | ||
helper_1.helper.addEventListener(pipeRead, 'close', () => { | ||
if (this.onclose) | ||
this.onclose.call(null); | ||
}), | ||
helper.addEventListener(pipeRead, 'error', debugError), | ||
helper.addEventListener(pipeWrite, 'error', debugError), | ||
helper_1.helper.addEventListener(pipeRead, 'error', helper_1.debugError), | ||
helper_1.helper.addEventListener(pipeWrite, 'error', helper_1.debugError), | ||
]; | ||
@@ -42,5 +36,2 @@ this.onmessage = null; | ||
} | ||
/** | ||
* @param {string} message | ||
*/ | ||
send(message) { | ||
@@ -50,5 +41,2 @@ this._pipeWrite.write(message); | ||
} | ||
/** | ||
* @param {!Buffer} buffer | ||
*/ | ||
_dispatch(buffer) { | ||
@@ -75,5 +63,5 @@ let end = buffer.indexOf('\0'); | ||
this._pipeWrite = null; | ||
helper.removeEventListeners(this._eventListeners); | ||
helper_1.helper.removeEventListeners(this._eventListeners); | ||
} | ||
} | ||
module.exports = PipeTransport; | ||
module.exports = { PipeTransport }; |
@@ -19,2 +19,9 @@ /** | ||
const { Worker: PuppeteerWorker } = require('./Worker'); | ||
// CDPSession is used only as a typedef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
// This import is used as a TypeDef, but ESLint's rule doesn't | ||
// understand that unfortunately. | ||
// eslint-disable-next-line no-unused-vars | ||
const { TaskQueue } = require('./TaskQueue'); | ||
class Target { | ||
@@ -24,6 +31,6 @@ /** | ||
* @param {!Puppeteer.BrowserContext} browserContext | ||
* @param {!function():!Promise<!Puppeteer.CDPSession>} sessionFactory | ||
* @param {!function():!Promise<!CDPSession>} sessionFactory | ||
* @param {boolean} ignoreHTTPSErrors | ||
* @param {?Puppeteer.Viewport} defaultViewport | ||
* @param {!Puppeteer.TaskQueue} screenshotTaskQueue | ||
* @param {!TaskQueue} screenshotTaskQueue | ||
*/ | ||
@@ -61,3 +68,3 @@ constructor(targetInfo, browserContext, sessionFactory, ignoreHTTPSErrors, defaultViewport, screenshotTaskQueue) { | ||
/** | ||
* @return {!Promise<!Puppeteer.CDPSession>} | ||
* @return {!Promise<!CDPSession>} | ||
*/ | ||
@@ -64,0 +71,0 @@ createCDPSession() { |
@@ -0,1 +1,22 @@ | ||
"use strict"; | ||
/** | ||
* Copyright 2020 Google Inc. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
/* TODO(jacktfranklin@): once we are calling this from TS files we can | ||
* avoid the horrible void | any type and instead make use of generics | ||
* to make this into TaskQueue<T> and let the caller tell us what types | ||
* the promise in the queue should return. | ||
*/ | ||
class TaskQueue { | ||
@@ -5,6 +26,2 @@ constructor() { | ||
} | ||
/** | ||
* @param {Function} task | ||
* @return {!Promise} | ||
*/ | ||
postTask(task) { | ||
@@ -11,0 +28,0 @@ const result = this._chain.then(task); |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -22,5 +23,2 @@ * Copyright 2019 Google Inc. All rights reserved. | ||
} | ||
/** | ||
* @param {number} timeout | ||
*/ | ||
setDefaultTimeout(timeout) { | ||
@@ -35,5 +33,2 @@ this._defaultTimeout = timeout; | ||
} | ||
/** | ||
* @return {number} | ||
*/ | ||
navigationTimeout() { | ||
@@ -40,0 +35,0 @@ if (this._defaultNavigationTimeout !== null) |
@@ -17,5 +17,8 @@ /** | ||
const { helper, assert } = require('./helper'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
class Tracing { | ||
/** | ||
* @param {!Puppeteer.CDPSession} client | ||
* @param {!CDPSession} client | ||
*/ | ||
@@ -22,0 +25,0 @@ constructor(client) { |
@@ -0,1 +1,2 @@ | ||
"use strict"; | ||
/** | ||
@@ -16,17 +17,4 @@ * Copyright 2017 Google Inc. All rights reserved. | ||
*/ | ||
/** | ||
* @typedef {Object} KeyDefinition | ||
* @property {number=} keyCode | ||
* @property {number=} shiftKeyCode | ||
* @property {string=} key | ||
* @property {string=} shiftKey | ||
* @property {string=} code | ||
* @property {string=} text | ||
* @property {string=} shiftText | ||
* @property {number=} location | ||
*/ | ||
/** | ||
* @type {Object<string, KeyDefinition>} | ||
*/ | ||
module.exports = { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.keyDefinitions = { | ||
'0': { 'keyCode': 48, 'key': '0', 'code': 'Digit0' }, | ||
@@ -33,0 +21,0 @@ '1': { 'keyCode': 49, 'key': '1', 'code': 'Digit1' }, |
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
@@ -16,24 +18,4 @@ * Copyright 2018 Google Inc. All rights reserved. | ||
*/ | ||
const NodeWebSocket = require('ws'); | ||
/** | ||
* @implements {!Puppeteer.ConnectionTransport} | ||
*/ | ||
const NodeWebSocket = require("ws"); | ||
class WebSocketTransport { | ||
/** | ||
* @param {string} url | ||
* @return {!Promise<!WebSocketTransport>} | ||
*/ | ||
static create(url) { | ||
return new Promise((resolve, reject) => { | ||
const ws = new NodeWebSocket(url, [], { | ||
perMessageDeflate: false, | ||
maxPayload: 256 * 1024 * 1024, | ||
}); | ||
ws.addEventListener('open', () => resolve(new WebSocketTransport(ws))); | ||
ws.addEventListener('error', reject); | ||
}); | ||
} | ||
/** | ||
* @param {!NodeWebSocket} ws | ||
*/ | ||
constructor(ws) { | ||
@@ -45,3 +27,3 @@ this._ws = ws; | ||
}); | ||
this._ws.addEventListener('close', event => { | ||
this._ws.addEventListener('close', () => { | ||
if (this.onclose) | ||
@@ -55,5 +37,12 @@ this.onclose.call(null); | ||
} | ||
/** | ||
* @param {string} message | ||
*/ | ||
static create(url) { | ||
return new Promise((resolve, reject) => { | ||
const ws = new NodeWebSocket(url, [], { | ||
perMessageDeflate: false, | ||
maxPayload: 256 * 1024 * 1024, | ||
}); | ||
ws.addEventListener('open', () => resolve(new WebSocketTransport(ws))); | ||
ws.addEventListener('error', reject); | ||
}); | ||
} | ||
send(message) { | ||
@@ -66,2 +55,2 @@ this._ws.send(message); | ||
} | ||
module.exports = WebSocketTransport; | ||
exports.WebSocketTransport = WebSocketTransport; |
@@ -20,5 +20,8 @@ /** | ||
const { JSHandle } = require('./JSHandle'); | ||
// Used as a TypeDef | ||
// eslint-disable-next-line no-unused-vars | ||
const { CDPSession } = require('./Connection'); | ||
class Worker extends EventEmitter { | ||
/** | ||
* @param {Puppeteer.CDPSession} client | ||
* @param {CDPSession} client | ||
* @param {string} url | ||
@@ -25,0 +28,0 @@ * @param {function(string, !Array<!JSHandle>, Protocol.Runtime.StackTrace=):void} consoleAPICalled |
{ | ||
"name": "puppeteer-core", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"description": "A high-level API to control headless Chrome over the DevTools Protocol", | ||
@@ -21,5 +21,7 @@ "main": "index.js", | ||
"test": "npm run tsc && npm run lint --silent && npm run coverage && npm run test-doclint && npm run test-types", | ||
"prepare": "node typescript-if-required.js", | ||
"prepublishOnly": "npm run tsc", | ||
"dev-install": "npm run tsc && node install.js", | ||
"lint": "([ \"$CI\" = true ] && eslint --ext js --ext ts --quiet -f codeframe . || eslint --ext js --ext ts .) && npm run tsc && npm run doc", | ||
"eslint": "([ \"$CI\" = true ] && eslint --ext js --ext ts --quiet -f codeframe . || eslint --ext js --ext ts .)", | ||
"lint": "npm run eslint && npm run tsc && npm run doc", | ||
"doc": "node utils/doclint/cli.js", | ||
@@ -31,4 +33,13 @@ "tsc": "tsc --version && tsc -p . && cp src/protocol.d.ts lib/ && cp src/externs.d.ts lib/", | ||
"unit-bundle": "mocha --config mocha-config/browser-bundle-tests.js", | ||
"update-protocol-d-ts": "node utils/protocol-types-generator" | ||
"update-protocol-d-ts": "node utils/protocol-types-generator", | ||
"test-install": "scripts/test-install.sh" | ||
}, | ||
"files": [ | ||
"lib/", | ||
"Errors.js", | ||
"DeviceDescriptors.js", | ||
"index.js", | ||
"install.js", | ||
"typescript-if-required.js" | ||
], | ||
"author": "The Chromium Authors", | ||
@@ -35,0 +46,0 @@ "license": "Apache-2.0", |
@@ -9,3 +9,3 @@ # Puppeteer | ||
###### [API](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/puppeteer/puppeteer/blob/master/CONTRIBUTING.md) | [Troubleshooting](https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md) | ||
###### [API](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/puppeteer/puppeteer/blob/master/CONTRIBUTING.md) | [Troubleshooting](https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md) | ||
@@ -41,3 +41,3 @@ > Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). Puppeteer runs [headless](https://developers.google.com/web/updates/2017/04/headless-chrome) by default, but can be configured to run full (non-headless) Chrome or Chromium. | ||
Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. To skip the download, or to download a different browser, see [Environment variables](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#environment-variables). | ||
Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. To skip the download, or to download a different browser, see [Environment variables](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#environment-variables). | ||
@@ -68,3 +68,3 @@ | ||
Puppeteer will be familiar to people using other browser testing frameworks. You create an instance | ||
of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#). | ||
of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#). | ||
@@ -94,3 +94,3 @@ **Example** - navigating to https://example.com and saving a screenshot as *example.png*: | ||
Puppeteer sets an initial page size to 800×600px, which defines the screenshot size. The page size can be customized with [`Page.setViewport()`](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#pagesetviewportviewport). | ||
Puppeteer sets an initial page size to 800×600px, which defines the screenshot size. The page size can be customized with [`Page.setViewport()`](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#pagesetviewportviewport). | ||
@@ -120,3 +120,3 @@ **Example** - create a PDF. | ||
See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#pagepdfoptions) for more information about creating pdfs. | ||
See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#pagepdfoptions) for more information about creating pdfs. | ||
@@ -156,3 +156,3 @@ **Example** - evaluate script in the context of the page | ||
See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`. | ||
See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`. | ||
@@ -166,3 +166,3 @@ <!-- [END getstarted] --> | ||
Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the [`headless` option](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#puppeteerlaunchoptions) when launching a browser: | ||
Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the [`headless` option](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#puppeteerlaunchoptions) when launching a browser: | ||
@@ -183,3 +183,3 @@ ```js | ||
You can also use Puppeteer with Firefox Nightly (experimental support). See [`Puppeteer.launch()`](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#puppeteerlaunchoptions) for more information. | ||
You can also use Puppeteer with Firefox Nightly (experimental support). See [`Puppeteer.launch()`](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#puppeteerlaunchoptions) for more information. | ||
@@ -196,3 +196,3 @@ See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/master/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. | ||
- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md) | ||
- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md) | ||
- [Examples](https://github.com/puppeteer/puppeteer/tree/master/examples/) | ||
@@ -312,3 +312,3 @@ - [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer) | ||
From Puppeteer v2.1.0 onwards you can specify [`puppeteer.launch({product: 'firefox'})`](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#puppeteerlaunchoptions) to run your Puppeteer scripts in Firefox Nightly, without any additional custom patches. While [an older experiment](https://www.npmjs.com/package/puppeteer-firefox) required a patched version of Firefox, [the current approach](https://wiki.mozilla.org/Remote) works with “stock” Firefox. | ||
From Puppeteer v2.1.0 onwards you can specify [`puppeteer.launch({product: 'firefox'})`](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#puppeteerlaunchoptions) to run your Puppeteer scripts in Firefox Nightly, without any additional custom patches. While [an older experiment](https://www.npmjs.com/package/puppeteer-firefox) required a patched version of Firefox, [the current approach](https://wiki.mozilla.org/Remote) works with “stock” Firefox. | ||
@@ -409,3 +409,3 @@ We will continue to collaborate with other browser vendors to bring Puppeteer support to browsers such as Safari. | ||
* Puppeteer is bundled with Chromium — not Chrome — and so by default, it inherits all of [Chromium's media-related limitations](https://www.chromium.org/audio-video). This means that Puppeteer does not support licensed formats such as AAC or H.264. (However, it is possible to force Puppeteer to use a separately-installed version Chrome instead of Chromium via the [`executablePath` option to `puppeteer.launch`](https://github.com/puppeteer/puppeteer/blob/v3.0.0/docs/api.md#puppeteerlaunchoptions). You should only use this configuration if you need an official release of Chrome that supports these media formats.) | ||
* Puppeteer is bundled with Chromium — not Chrome — and so by default, it inherits all of [Chromium's media-related limitations](https://www.chromium.org/audio-video). This means that Puppeteer does not support licensed formats such as AAC or H.264. (However, it is possible to force Puppeteer to use a separately-installed version Chrome instead of Chromium via the [`executablePath` option to `puppeteer.launch`](https://github.com/puppeteer/puppeteer/blob/v3.0.1/docs/api.md#puppeteerlaunchoptions). You should only use this configuration if you need an official release of Chrome that supports these media formats.) | ||
* Since Puppeteer (in all configurations) controls a desktop version of Chromium/Chrome, features that are only supported by the mobile version of Chrome are not supported. This means that Puppeteer [does not support HTTP Live Streaming (HLS)](https://caniuse.com/#feat=http-live-streaming). | ||
@@ -412,0 +412,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
47
918318
41
24300