diff --git a/docs/api.md b/docs/api.md index 7b3b3a7919180..d88c51187ebe2 100644 --- a/docs/api.md +++ b/docs/api.md @@ -451,6 +451,7 @@ puppeteer.launch().then(async browser => { - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false` - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`. - `slowMo` <[number]> Slows down Puppeteer operations by the specified amount of milliseconds. Useful so that you can see what is going on. + - `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Puppeteer to use. - returns: <[Promise]<[Browser]>> This methods attaches Puppeteer to an existing Chromium instance. @@ -489,7 +490,7 @@ The default flags that Chromium will be launched with. - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false` - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`. - `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/). - - `ignoreDefaultArgs` <([boolean]|<[Array]<[string]>>)> If `true`, then do not use [`puppeteer.defaultArgs()`](#puppeteerdefaultargs-options). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`. + - `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`puppeteer.defaultArgs()`](#puppeteerdefaultargs-options). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`. - `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`. - `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`. @@ -1145,10 +1146,11 @@ Gets the full HTML contents of the page, including the doctype. - `domain` <[string]> - `path` <[string]> - `expires` <[number]> Unix time in seconds. + - `size` <[number]> - `httpOnly` <[boolean]> - `secure` <[boolean]> - `session` <[boolean]> - - `sameSite` <[string]> `"Strict"` or `"Lax"`. + - `sameSite` <"Strict"|"Lax"> If no URLs are specified, this method returns cookies for the current page URL. If URLs are specified, only cookies for those URLs are returned. @@ -1163,7 +1165,6 @@ If URLs are specified, only cookies for those URLs are returned. - `url` <[string]> - `domain` <[string]> - `path` <[string]> - - `secure` <[boolean]> - returns: <[Promise]> #### page.emulate(options) @@ -1424,7 +1425,7 @@ Shortcut for [page.mainFrame().hover(selector)](#framehoverselector). #### page.isClosed() -- returns: boolean +- returns: <[boolean]> Indicates that the page has been closed. @@ -1475,13 +1476,13 @@ Page is guaranteed to have a main frame which persists during navigations. - `landscape` <[boolean]> Paper orientation. Defaults to `false`. - `pageRanges` <[string]> Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages. - `format` <[string]> Paper format. If set, takes priority over `width` or `height` options. Defaults to 'Letter'. - - `width` <[string]> Paper width, accepts values labeled with units. - - `height` <[string]> Paper height, accepts values labeled with units. + - `width` <[string]|[number]> Paper width, accepts values labeled with units. + - `height` <[string]|[number]> Paper height, accepts values labeled with units. - `margin` <[Object]> Paper margins, defaults to none. - - `top` <[string]> Top margin, accepts values labeled with units. - - `right` <[string]> Right margin, accepts values labeled with units. - - `bottom` <[string]> Bottom margin, accepts values labeled with units. - - `left` <[string]> Left margin, accepts values labeled with units. + - `top` <[string]|[number]> Top margin, accepts values labeled with units. + - `right` <[string]|[number]> Right margin, accepts values labeled with units. + - `bottom` <[string]|[number]> Bottom margin, accepts values labeled with units. + - `left` <[string]|[number]> Left margin, accepts values labeled with units. - `preferCSSPageSize` <[boolean]> Give any CSS `@page` size declared in the page priority over what is declared in `width` and `height` or `format` options. Defaults to `false`, which will scale the content to fit the paper size. - returns: <[Promise]<[Buffer]>> Promise which resolves with PDF buffer. @@ -1619,7 +1620,7 @@ Toggles ignoring cache for each request based on the enabled state. By default, - `expires` <[number]> Unix time in seconds. - `httpOnly` <[boolean]> - `secure` <[boolean]> - - `sameSite` <[string]> `"Strict"` or `"Lax"`. + - `sameSite` <"Strict"|"Lax"> - returns: <[Promise]> ```js @@ -2004,7 +2005,7 @@ Most of the accessibility tree gets filtered out when converting from Blink AX T #### accessibility.snapshot([options]) - `options` <[Object]> - `interestingOnly` <[boolean]> Prune uninteresting nodes from the tree. Defaults to `true`. -- returns: <[Promise]<[AXNode]>> Returns an AXNode object with the following properties: +- returns: <[Promise]<[Object]>> Returns an [AXNode] object with the following properties: - `role` <[string]> The [role](https://www.w3.org/TR/wai-aria/#usage_intro). - `name` <[string]> A human readable name for the node. - `value` <[string]|[number]> The current value of the node. @@ -2021,8 +2022,8 @@ Most of the accessibility tree gets filtered out when converting from Blink AX T - `readonly` <[boolean]> Whether the node is read only. - `required` <[boolean]> Whether the node is required. - `selected` <[boolean]> Whether the node is selected in its parent node. - - `checked` <[boolean]|[string]> Whether the checkbox is checked, or "mixed". - - `pressed` <[boolean]|[string]> Whether the toggle button is checked, or "mixed". + - `checked` <[boolean]|"mixed"> Whether the checkbox is checked, or "mixed". + - `pressed` <[boolean]|"mixed"> Whether the toggle button is checked, or "mixed". - `level` <[number]> The level of a heading. - `valuemin` <[number]> The minimum value in a node. - `valuemax` <[number]> The maximum value in a node. @@ -2030,7 +2031,7 @@ Most of the accessibility tree gets filtered out when converting from Blink AX T - `haspopup` <[string]> What kind of popup is currently being shown for a node. - `invalid` <[string]> Whether and in what way this node's value is invalid. - `orientation` <[string]> Whether the node is oriented horizontally or vertically. - - `children` <[Array]<[AXNode]>> Child nodes of this node, if any. + - `children` <[Array]<[Object]>> Child [AXNode]s of this node, if any. Captures the current state of the accessibility tree. The returned object represents the root accessible node of the page. @@ -2841,7 +2842,7 @@ Returns either `null` or the object handle itself, if the object handle is an in The `jsHandle.dispose` method stops referencing the element handle. #### jsHandle.executionContext() -- returns: [ExecutionContext] +- returns: <[ExecutionContext]> Returns execution context the handle belongs to. @@ -2966,10 +2967,18 @@ This method returns the bounding box of the element (relative to the main frame) #### elementHandle.boxModel() - returns: <[Promise]> - - content <[Array]<[Object]>> Content box, represented as an array of {x, y} points. - - padding <[Array]<[Object]>> Padding box, represented as an array of {x, y} points. - - border <[Array]<[Object]>> Border box, represented as an array of {x, y} points. - - margin <[Array]<[Object]>> Margin box, represented as an array of {x, y} points. + - content <[Array]<[Object]>> Content box. + - x <[number]> + - y <[number]> + - padding <[Array]<[Object]>> Padding box. + - x <[number]> + - y <[number]> + - border <[Array]<[Object]>> Border box. + - x <[number]> + - y <[number]> + - margin <[Array]<[Object]>> Margin box. + - x <[number]> + - y <[number]> - width <[number]> Element's width. - height <[number]> Element's height. @@ -2994,7 +3003,7 @@ If the element is detached from DOM, the method throws an error. The `elementHandle.dispose` method stops referencing the element handle. #### elementHandle.executionContext() -- returns: [ExecutionContext] +- returns: <[ExecutionContext]> #### elementHandle.focus() - returns: <[Promise]> @@ -3224,7 +3233,7 @@ ResourceType will be one of the following: `document`, `stylesheet`, `image`, `m - `status` <[number]> Response status code, defaults to `200`. - `headers` <[Object]> Optional response headers - `contentType` <[string]> If set, equals to setting `Content-Type` response header - - `body` <[Buffer]|[string]> Optional response body + - `body` <[string]|[Buffer]> Optional response body - returns: <[Promise]> Fulfills request with given response. To use this, request interception should @@ -3524,3 +3533,4 @@ TimeoutError is emitted whenever certain operations are terminated due to timeou [Worker]: #class-worker "Worker" [Accessibility]: #class-accessibility "Accessibility" [AXNode]: #accessibilitysnapshotoptions "AXNode" +[ConnectionTransport]: ../lib/WebSocketTransport.js "ConnectionTransport" \ No newline at end of file diff --git a/lib/Coverage.js b/lib/Coverage.js index fe38a83238ce6..f4e3d31f37534 100644 --- a/lib/Coverage.js +++ b/lib/Coverage.js @@ -35,7 +35,7 @@ class Coverage { } /** - * @param {!Object} options + * @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options */ async startJSCoverage(options) { return await this._jsCoverage.start(options); @@ -49,7 +49,7 @@ class Coverage { } /** - * @param {!Object} options + * @param {{resetOnNavigation?: boolean}=} options */ async startCSSCoverage(options) { return await this._cssCoverage.start(options); @@ -80,12 +80,16 @@ class JSCoverage { } /** - * @param {!Object} options + * @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options */ async start(options = {}) { assert(!this._enabled, 'JSCoverage is already enabled'); - this._resetOnNavigation = options.resetOnNavigation === undefined ? true : !!options.resetOnNavigation; - this._reportAnonymousScripts = !!options.reportAnonymousScripts; + const { + resetOnNavigation = true, + reportAnonymousScripts = false + } = options; + this._resetOnNavigation = resetOnNavigation; + this._reportAnonymousScripts = reportAnonymousScripts; this._enabled = true; this._scriptURLs.clear(); this._scriptSources.clear(); @@ -174,11 +178,12 @@ class CSSCoverage { } /** - * @param {!Object} options + * @param {{resetOnNavigation?: boolean}=} options */ async start(options = {}) { assert(!this._enabled, 'CSSCoverage is already enabled'); - this._resetOnNavigation = options.resetOnNavigation === undefined ? true : !!options.resetOnNavigation; + const {resetOnNavigation = true} = options; + this._resetOnNavigation = resetOnNavigation; this._enabled = true; this._stylesheetURLs.clear(); this._stylesheetSources.clear(); diff --git a/lib/ExecutionContext.js b/lib/ExecutionContext.js index ab210dd5a0c69..4709b2e50e3ee 100644 --- a/lib/ExecutionContext.js +++ b/lib/ExecutionContext.js @@ -376,7 +376,7 @@ class ElementHandle extends JSHandle { /** * @param {!Array} quad - * @return {!Array} + * @return {!Array<{x: number, y: number}>} */ _fromProtocolQuad(quad) { return [ @@ -394,9 +394,9 @@ class ElementHandle extends JSHandle { } /** - * @param {!Object=} options + * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options */ - async click(options = {}) { + async click(options) { await this._scrollIntoViewIfNeeded(); const {x, y} = await this._clickablePoint(); await this._page.mouse.click(x, y, options); @@ -432,7 +432,7 @@ class ElementHandle extends JSHandle { /** * @param {string} key - * @param {!Object=} options + * @param {!{delay?: number, text?: string}=} options */ async press(key, options) { await this.focus(); @@ -458,7 +458,7 @@ class ElementHandle extends JSHandle { } /** - * @return {!Promise} + * @return {!Promise} */ async boxModel() { const result = await this._getBoxModel(); @@ -646,6 +646,16 @@ function computeQuadArea(quad) { return Math.abs(area); } +/** + * @typedef {Object} BoxModel + * @property {!Array} content + * @property {!Array} padding + * @property {!Array} border + * @property {!Array} margin + * @property {number} width + * @property {number} height + */ + helper.tracePublicAPI(ElementHandle); helper.tracePublicAPI(JSHandle); helper.tracePublicAPI(ExecutionContext); diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 3660ab9db835e..b2fd414907c90 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -65,17 +65,19 @@ class FrameManager extends EventEmitter { /** * @param {!Puppeteer.Frame} frame * @param {string} url - * @param {!Object=} options + * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ async navigateFrame(frame, url, options = {}) { - const referrer = typeof options.referer === 'string' ? options.referer : this._networkManager.extraHTTPHeaders()['referer']; + const { + referer = this._networkManager.extraHTTPHeaders()['referer'], + timeout = this._defaultNavigationTimeout, + } = options; - const timeout = typeof options.timeout === 'number' ? options.timeout : this._defaultNavigationTimeout; const watcher = new NavigatorWatcher(this._client, this, this._networkManager, frame, timeout, options); let ensureNewDocumentNavigation = false; let error = await Promise.race([ - navigate(this._client, url, referrer, frame._id), + navigate(this._client, url, referer, frame._id), watcher.timeoutOrTerminationPromise(), ]); if (!error) { @@ -109,11 +111,13 @@ class FrameManager extends EventEmitter { /** * @param {!Puppeteer.Frame} frame - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ - async waitForFrameNavigation(frame, options) { - const timeout = typeof options.timeout === 'number' ? options.timeout : this._defaultNavigationTimeout; + async waitForFrameNavigation(frame, options = {}) { + const { + timeout = this._defaultNavigationTimeout + } = options; const watcher = new NavigatorWatcher(this._client, this, this._networkManager, frame, timeout, options); const error = await Promise.race([ watcher.timeoutOrTerminationPromise(), @@ -397,18 +401,18 @@ class Frame { /** * @param {string} url - * @param {!Object=} options + * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ - async goto(url, options = {}) { + async goto(url, options) { return await this._frameManager.navigateFrame(this, url, options); } /** - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ - async waitForNavigation(options = {}) { + async waitForNavigation(options) { return await this._frameManager.waitForFrameNavigation(this, options); } @@ -566,30 +570,35 @@ class Frame { } /** - * @param {Object} options + * @param {!{url?: string, path?: string, content?: string, type?: string}} options * @return {!Promise} */ async addScriptTag(options) { - if (typeof options.url === 'string') { - const url = options.url; + const { + url = null, + path = null, + content = null, + type = '' + } = options; + if (url !== null) { try { const context = await this._contextPromise; - return (await context.evaluateHandle(addScriptUrl, url, options.type)).asElement(); + return (await context.evaluateHandle(addScriptUrl, url, type)).asElement(); } catch (error) { throw new Error(`Loading script from ${url} failed`); } } - if (typeof options.path === 'string') { - let contents = await readFileAsync(options.path, 'utf8'); - contents += '//# sourceURL=' + options.path.replace(/\n/g, ''); + if (path !== null) { + let contents = await readFileAsync(path, 'utf8'); + contents += '//# sourceURL=' + path.replace(/\n/g, ''); const context = await this._contextPromise; - return (await context.evaluateHandle(addScriptContent, contents, options.type)).asElement(); + return (await context.evaluateHandle(addScriptContent, contents, type)).asElement(); } - if (typeof options.content === 'string') { + if (content !== null) { const context = await this._contextPromise; - return (await context.evaluateHandle(addScriptContent, options.content, options.type)).asElement(); + return (await context.evaluateHandle(addScriptContent, content, type)).asElement(); } throw new Error('Provide an object with a `url`, `path` or `content` property'); @@ -632,12 +641,16 @@ class Frame { } /** - * @param {Object} options + * @param {!{url?: string, path?: string, content?: string}} options * @return {!Promise} */ async addStyleTag(options) { - if (typeof options.url === 'string') { - const url = options.url; + const { + url = null, + path = null, + content = null + } = options; + if (url !== null) { try { const context = await this._contextPromise; return (await context.evaluateHandle(addStyleUrl, url)).asElement(); @@ -646,16 +659,16 @@ class Frame { } } - if (typeof options.path === 'string') { - let contents = await readFileAsync(options.path, 'utf8'); - contents += '/*# sourceURL=' + options.path.replace(/\n/g, '') + '*/'; + if (path !== null) { + let contents = await readFileAsync(path, 'utf8'); + contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/'; const context = await this._contextPromise; return (await context.evaluateHandle(addStyleContent, contents)).asElement(); } - if (typeof options.content === 'string') { + if (content !== null) { const context = await this._contextPromise; - return (await context.evaluateHandle(addStyleContent, options.content)).asElement(); + return (await context.evaluateHandle(addStyleContent, content)).asElement(); } throw new Error('Provide an object with a `url`, `path` or `content` property'); @@ -697,9 +710,9 @@ class Frame { /** * @param {string} selector - * @param {!Object=} options + * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options */ - async click(selector, options = {}) { + async click(selector, options) { const handle = await this.$(selector); assert(handle, 'No node found for selector: ' + selector); await handle.click(options); @@ -797,30 +810,32 @@ class Frame { /** * @param {string} selector - * @param {!Object=} options + * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options * @return {!Promise} */ - waitForSelector(selector, options = {}) { + waitForSelector(selector, options) { return this._waitForSelectorOrXPath(selector, false, options); } /** * @param {string} xpath - * @param {!Object=} options + * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options * @return {!Promise} */ - waitForXPath(xpath, options = {}) { + waitForXPath(xpath, options) { return this._waitForSelectorOrXPath(xpath, true, options); } /** * @param {Function|string} pageFunction - * @param {!Object=} options + * @param {!{polling?: string|number, timeout?: number}=} options * @return {!Promise} */ waitForFunction(pageFunction, options = {}, ...args) { - const timeout = helper.isNumber(options.timeout) ? options.timeout : 30000; - const polling = options.polling || 'raf'; + const { + polling = 'raf', + timeout = 30000 + } = options; return new WaitTask(this, pageFunction, 'function', polling, timeout, ...args).promise; } @@ -834,14 +849,16 @@ class Frame { /** * @param {string} selectorOrXPath * @param {boolean} isXPath - * @param {!Object=} options + * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options * @return {!Promise} */ _waitForSelectorOrXPath(selectorOrXPath, isXPath, options = {}) { - const waitForVisible = !!options.visible; - const waitForHidden = !!options.hidden; + const { + visible: waitForVisible = false, + hidden: waitForHidden = false, + timeout = 30000, + } = options; const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation'; - const timeout = helper.isNumber(options.timeout) ? options.timeout : 30000; const title = `${isXPath ? 'XPath' : 'selector'} "${selectorOrXPath}"${waitForHidden ? ' to be hidden' : ''}`; return new WaitTask(this, predicate, title, polling, timeout, selectorOrXPath, isXPath, waitForVisible, waitForHidden).promise; @@ -1117,17 +1134,19 @@ class NavigatorWatcher { * @param {!NetworkManager} networkManager * @param {!Puppeteer.Frame} frame * @param {number} timeout - * @param {!Object=} options + * @param {!{waitUntil?: string|!Array}} options */ constructor(client, frameManager, networkManager, frame, timeout, options = {}) { - assert(options.networkIdleTimeout === undefined, 'ERROR: networkIdleTimeout option is no longer supported.'); - assert(options.networkIdleInflight === undefined, 'ERROR: networkIdleInflight option is no longer supported.'); + assert(options['networkIdleTimeout'] === undefined, 'ERROR: networkIdleTimeout option is no longer supported.'); + assert(options['networkIdleInflight'] === undefined, 'ERROR: networkIdleInflight option is no longer supported.'); assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead'); - let waitUntil = ['load']; - if (Array.isArray(options.waitUntil)) - waitUntil = options.waitUntil.slice(); - else if (typeof options.waitUntil === 'string') - waitUntil = [options.waitUntil]; + let { + waitUntil = ['load'] + } = options; + if (Array.isArray(waitUntil)) + waitUntil = waitUntil.slice(); + else if (typeof waitUntil === 'string') + waitUntil = [waitUntil]; this._expectedLifecycle = waitUntil.map(value => { const protocolEvent = puppeteerToProtocolLifecycle[value]; assert(protocolEvent, 'Unknown value for options.waitUntil: ' + value); diff --git a/lib/Input.js b/lib/Input.js index b188697e25ad5..b4c02f95f8bcc 100644 --- a/lib/Input.js +++ b/lib/Input.js @@ -171,11 +171,12 @@ class Keyboard { /** * @param {string} key - * @param {!Object=} options + * @param {!{delay?: number, text?: string}=} options */ - async press(key, options) { + async press(key, options = {}) { + const {delay = null} = options; await this.down(key, options); - if (options && options.delay) + if (delay !== null) await new Promise(f => setTimeout(f, options.delay)); await this.up(key); } @@ -198,13 +199,13 @@ class Mouse { /** * @param {number} x * @param {number} y - * @param {Object=} options + * @param {!{steps?: number}=} options */ async move(x, y, options = {}) { + const {steps = 1} = options; const fromX = this._x, fromY = this._y; this._x = x; this._y = y; - const steps = options.steps || 1; for (let i = 1; i <= steps; i++) { await this._client.send('Input.dispatchMouseEvent', { type: 'mouseMoved', @@ -219,43 +220,46 @@ class Mouse { /** * @param {number} x * @param {number} y - * @param {!Object=} options + * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options */ async click(x, y, options = {}) { + const {delay = null} = options; this.move(x, y); this.down(options); - if (typeof options.delay === 'number') - await new Promise(f => setTimeout(f, options.delay)); + if (delay !== null) + await new Promise(f => setTimeout(f, delay)); await this.up(options); } /** - * @param {!Object=} options + * @param {!{button?: "left"|"right"|"middle", clickCount?: number}=} options */ async down(options = {}) { - this._button = (options.button || 'left'); + const {button = 'left', clickCount = 1} = options; + this._button = button; await this._client.send('Input.dispatchMouseEvent', { type: 'mousePressed', - button: this._button, + button, x: this._x, y: this._y, modifiers: this._keyboard._modifiers, - clickCount: (options.clickCount || 1) + clickCount }); } /** - * @param {!Object=} options + * @param {!{button?: "left"|"right"|"middle", clickCount?: number}=} options */ async up(options = {}) { + const {button = 'left', clickCount = 1} = options; this._button = 'none'; await this._client.send('Input.dispatchMouseEvent', { type: 'mouseReleased', - button: (options.button || 'left'), + button, x: this._x, y: this._y, modifiers: this._keyboard._modifiers, - clickCount: (options.clickCount || 1) + clickCount }); } } diff --git a/lib/Launcher.js b/lib/Launcher.js index 7f494ced15477..b4c729fa07948 100644 --- a/lib/Launcher.js +++ b/lib/Launcher.js @@ -71,7 +71,7 @@ class Launcher { } /** - * @param {!(LaunchOptions & ChromeArgOptions & BrowserOptions)=} options + * @param {!(Launcher.LaunchOptions & Launcher.ChromeArgOptions & Launcher.BrowserOptions)=} options * @return {!Promise} */ async launch(options = {}) { @@ -236,7 +236,7 @@ class Launcher { } /** - * @param {!ChromeArgOptions=} options + * @param {!Launcher.ChromeArgOptions=} options * @return {!Array} */ defaultArgs(options = {}) { @@ -274,7 +274,7 @@ class Launcher { } /** - * @param {!(BrowserOptions & {browserWSEndpoint: string, transport?: !Puppeteer.ConnectionTransport})} options + * @param {!(Launcher.BrowserOptions & {browserWSEndpoint: string, transport?: !Puppeteer.ConnectionTransport})} options * @return {!Promise} */ async connect(options) { @@ -374,7 +374,7 @@ function waitForWSEndpoint(chromeProcess, timeout, preferredRevision) { } /** - * @typedef {Object} ChromeArgOptions + * @typedef {Object} Launcher.ChromeArgOptions * @property {boolean=} headless * @property {Array=} args * @property {string=} userDataDir @@ -382,9 +382,9 @@ function waitForWSEndpoint(chromeProcess, timeout, preferredRevision) { */ /** - * @typedef {Object} LaunchOptions + * @typedef {Object} Launcher.LaunchOptions * @property {string=} executablePath - * @property {boolean=} ignoreDefaultArgs + * @property {boolean|Array=} ignoreDefaultArgs * @property {boolean=} handleSIGINT * @property {boolean=} handleSIGTERM * @property {boolean=} handleSIGHUP @@ -395,7 +395,7 @@ function waitForWSEndpoint(chromeProcess, timeout, preferredRevision) { */ /** - * @typedef {Object} BrowserOptions + * @typedef {Object} Launcher.BrowserOptions * @property {boolean=} ignoreHTTPSErrors * @property {(?Puppeteer.Viewport)=} defaultViewport * @property {number=} slowMo diff --git a/lib/NetworkManager.js b/lib/NetworkManager.js index 38b0926e06f50..a5d5a78ac8cfa 100644 --- a/lib/NetworkManager.js +++ b/lib/NetworkManager.js @@ -391,18 +391,24 @@ class Request { } /** - * @param {!Object=} overrides + * @param {!{url?: string, method?:string, postData?: string, headers?: !Object}} overrides */ async continue(overrides = {}) { assert(this._allowInterception, 'Request Interception is not enabled!'); assert(!this._interceptionHandled, 'Request is already handled!'); + const { + url, + method, + postData, + headers + } = overrides; this._interceptionHandled = true; await this._client.send('Network.continueInterceptedRequest', { interceptionId: this._interceptionId, - url: overrides.url, - method: overrides.method, - postData: overrides.postData, - headers: overrides.headers, + url, + method, + postData, + headers, }).catch(error => { // In certain cases, protocol will return error if the request was already canceled // or the page was closed. We should tolerate these errors. diff --git a/lib/Page.js b/lib/Page.js index be5f984f3e0f6..2a8f85e19c6ff 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -350,7 +350,7 @@ class Page extends EventEmitter { } /** - * @param {Array} cookies + * @param {Array} cookies */ async deleteCookie(...cookies) { const pageURL = this.url(); @@ -388,7 +388,7 @@ class Page extends EventEmitter { } /** - * @param {Object} options + * @param {!{url?: string, path?: string, content?: string, type?: string}} options * @return {!Promise} */ async addScriptTag(options) { @@ -396,7 +396,7 @@ class Page extends EventEmitter { } /** - * @param {Object} options + * @param {!{url?: string, path?: string, content?: string}} options * @return {!Promise} */ async addStyleTag(options) { @@ -457,7 +457,7 @@ class Page extends EventEmitter { } /** - * @return {!Promise} + * @return {!Promise} */ async metrics() { const response = await this._client.send('Performance.getMetrics'); @@ -465,7 +465,7 @@ class Page extends EventEmitter { } /** - * @param {*} event + * @param {!Protocol.Performance.metricsPayload} event */ _emitMetrics(event) { this.emit(Page.Events.Metrics, { @@ -476,7 +476,7 @@ class Page extends EventEmitter { /** * @param {?Array} metrics - * @return {!Object} + * @return {!Metrics} */ _buildMetricsObject(metrics) { const result = {}; @@ -580,15 +580,15 @@ class Page extends EventEmitter { /** * @param {string} url - * @param {!Object=} options + * @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ - async goto(url, options = {}) { + async goto(url, options) { return await this._frameManager.mainFrame().goto(url, options); } /** - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ async reload(options) { @@ -600,7 +600,7 @@ class Page extends EventEmitter { } /** - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ async waitForNavigation(options = {}) { @@ -609,11 +609,13 @@ class Page extends EventEmitter { /** * @param {(string|Function)} urlOrPredicate - * @param {!Object=} options + * @param {!{timeout?: number}=} options * @return {!Promise} */ async waitForRequest(urlOrPredicate, options = {}) { - const timeout = typeof options.timeout === 'number' ? options.timeout : 30000; + const { + timeout = 30000 + } = options; return helper.waitForEvent(this._networkManager, NetworkManager.Events.Request, request => { if (helper.isString(urlOrPredicate)) return (urlOrPredicate === request.url()); @@ -625,11 +627,13 @@ class Page extends EventEmitter { /** * @param {(string|Function)} urlOrPredicate - * @param {!Object=} options + * @param {!{timeout?: number}=} options * @return {!Promise} */ async waitForResponse(urlOrPredicate, options = {}) { - const timeout = typeof options.timeout === 'number' ? options.timeout : 30000; + const { + timeout = 30000 + } = options; return helper.waitForEvent(this._networkManager, NetworkManager.Events.Response, response => { if (helper.isString(urlOrPredicate)) return (urlOrPredicate === response.url()); @@ -640,7 +644,7 @@ class Page extends EventEmitter { } /** - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ async goBack(options) { @@ -648,7 +652,7 @@ class Page extends EventEmitter { } /** - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ async goForward(options) { @@ -656,7 +660,7 @@ class Page extends EventEmitter { } /** - * @param {!Object=} options + * @param {!{timeout?: number, waitUntil?: string|!Array}=} options * @return {!Promise} */ async _go(delta, options) { @@ -676,7 +680,7 @@ class Page extends EventEmitter { } /** - * @param {!Object} options + * @param {!{viewport: !Puppeteer.Viewport, userAgent: string}} options */ async emulate(options) { await Promise.all([ @@ -753,7 +757,7 @@ class Page extends EventEmitter { } /** - * @param {!Object=} options + * @param {!ScreenshotOptions=} options * @return {!Promise} */ async screenshot(options = {}) { @@ -793,14 +797,12 @@ class Page extends EventEmitter { /** * @param {"png"|"jpeg"} format - * @param {!Object=} options + * @param {!ScreenshotOptions=} options * @return {!Promise} */ async _screenshotTask(format, options) { await this._client.send('Target.activateTarget', {targetId: this._target._targetId}); - let clip = options.clip ? Object.assign({}, options['clip']) : undefined; - if (clip) - clip.scale = 1; + let clip = options.clip ? Object.assign({}, options['clip'], {scale: 1}) : undefined; if (options.fullPage) { const metrics = await this._client.send('Page.getLayoutMetrics'); @@ -835,17 +837,22 @@ class Page extends EventEmitter { } /** - * @param {!Object=} options + * @param {!PDFOptions=} options * @return {!Promise} */ async pdf(options = {}) { - const scale = options.scale || 1; - const displayHeaderFooter = !!options.displayHeaderFooter; - const headerTemplate = options.headerTemplate || ''; - const footerTemplate = options.footerTemplate || ''; - const printBackground = !!options.printBackground; - const landscape = !!options.landscape; - const pageRanges = options.pageRanges || ''; + const { + scale = 1, + displayHeaderFooter = false, + headerTemplate = '', + footerTemplate = '', + printBackground = false, + landscape = false, + pageRanges = '', + preferCSSPageSize = false, + margin = {}, + path = null + } = options; let paperWidth = 8.5; let paperHeight = 11; @@ -859,32 +866,30 @@ class Page extends EventEmitter { paperHeight = convertPrintParameterToInches(options.height) || paperHeight; } - const marginOptions = options.margin || {}; - const marginTop = convertPrintParameterToInches(marginOptions.top) || 0; - const marginLeft = convertPrintParameterToInches(marginOptions.left) || 0; - const marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0; - const marginRight = convertPrintParameterToInches(marginOptions.right) || 0; - const preferCSSPageSize = options.preferCSSPageSize || false; + const marginTop = convertPrintParameterToInches(margin.top) || 0; + const marginLeft = convertPrintParameterToInches(margin.left) || 0; + const marginBottom = convertPrintParameterToInches(margin.bottom) || 0; + const marginRight = convertPrintParameterToInches(margin.right) || 0; const result = await this._client.send('Page.printToPDF', { - landscape: landscape, - displayHeaderFooter: displayHeaderFooter, - headerTemplate: headerTemplate, - footerTemplate: footerTemplate, - printBackground: printBackground, - scale: scale, - paperWidth: paperWidth, - paperHeight: paperHeight, - marginTop: marginTop, - marginBottom: marginBottom, - marginLeft: marginLeft, - marginRight: marginRight, - pageRanges: pageRanges, - preferCSSPageSize: preferCSSPageSize + landscape, + displayHeaderFooter, + headerTemplate, + footerTemplate, + printBackground, + scale, + paperWidth, + paperHeight, + marginTop, + marginBottom, + marginLeft, + marginRight, + pageRanges, + preferCSSPageSize }); const buffer = Buffer.from(result.data, 'base64'); - if (options.path) - await writeFileAsync(options.path, buffer); + if (path !== null) + await writeFileAsync(path, buffer); return buffer; } @@ -925,7 +930,7 @@ class Page extends EventEmitter { /** * @param {string} selector - * @param {!Object=} options + * @param {!{delay?: number, button?: "left"|"right"|"middle", clickCount?: number}=} options */ click(selector, options = {}) { return this.mainFrame().click(selector, options); @@ -982,7 +987,7 @@ class Page extends EventEmitter { /** * @param {string} selector - * @param {!Object=} options + * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options * @return {!Promise} */ waitForSelector(selector, options = {}) { @@ -991,7 +996,7 @@ class Page extends EventEmitter { /** * @param {string} xpath - * @param {!Object=} options + * @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options * @return {!Promise} */ waitForXPath(xpath, options = {}) { @@ -1000,7 +1005,7 @@ class Page extends EventEmitter { /** * @param {function()} pageFunction - * @param {!Object=} options + * @param {!{polling?: string|number, timeout?: number}=} options * @param {!Array<*>} args * @return {!Promise} */ @@ -1009,6 +1014,51 @@ class Page extends EventEmitter { } } +/** + * @typedef {Object} PDFOptions + * @property {number=} scale + * @property {boolean=} displayHeaderFooter + * @property {string=} headerTemplate + * @property {string=} footerTemplate + * @property {boolean=} printBackground + * @property {boolean=} landscape + * @property {string=} pageRanges + * @property {string=} format + * @property {string|number=} width + * @property {string|number=} height + * @property {boolean=} preferCSSPageSize + * @property {!{top?: string|number, bottom?: string|number, left?: string|number, right?: string|number}=} margin + * @property {string=} path + */ + +/** + * @typedef {Object} Metrics + * @property {number=} Timestamp + * @property {number=} Documents + * @property {number=} Frames + * @property {number=} JSEventListeners + * @property {number=} Nodes + * @property {number=} LayoutCount + * @property {number=} RecalcStyleCount + * @property {number=} LayoutDuration + * @property {number=} RecalcStyleDuration + * @property {number=} ScriptDuration + * @property {number=} TaskDuration + * @property {number=} JSHeapUsedSize + * @property {number=} JSHeapTotalSize + */ + +/** + * @typedef {Object} ScreenshotOptions + * @property {string=} type + * @property {string=} path + * @property {boolean=} fullPage + * @property {{x: number, y: number, width: number, height: number}=} clip + * @property {number=} quality + * @property {boolean=} omitBackground + * @property {string=} encoding + */ + /** @type {!Set} */ const supportedMetrics = new Set([ 'Timestamp', diff --git a/lib/Puppeteer.js b/lib/Puppeteer.js index b966a39462cef..76660dc37baed 100644 --- a/lib/Puppeteer.js +++ b/lib/Puppeteer.js @@ -29,7 +29,7 @@ module.exports = class { } /** - * @param {!Object=} options + * @param {!(Launcher.LaunchOptions & Launcher.ChromeArgOptions & Launcher.BrowserOptions)=} options * @return {!Promise} */ launch(options) { @@ -37,7 +37,7 @@ module.exports = class { } /** - * @param {{browserWSEndpoint: string, ignoreHTTPSErrors: boolean, transport?: !Puppeteer.ConnectionTransport}} options + * @param {!(Launcher.BrowserOptions & {browserWSEndpoint: string, transport?: !Puppeteer.ConnectionTransport})} options * @return {!Promise} */ connect(options) { @@ -52,6 +52,7 @@ module.exports = class { } /** + * @param {!Launcher.ChromeArgOptions=} options * @return {!Array} */ defaultArgs(options) { @@ -59,7 +60,7 @@ module.exports = class { } /** - * @param {!Object=} options + * @param {!BrowserFetcher.Options=} options * @return {!BrowserFetcher} */ createBrowserFetcher(options) { diff --git a/lib/Tracing.js b/lib/Tracing.js index 4af720870f8dc..d8c8ac7f2bd05 100644 --- a/lib/Tracing.js +++ b/lib/Tracing.js @@ -31,7 +31,7 @@ class Tracing { } /** - * @param {!Object} options + * @param {!{path: string, screenshots?: boolean, categories?: !Array}} options */ async start(options) { assert(!this._recording, 'Cannot start recording trace while already recording trace.'); @@ -42,16 +42,20 @@ class Tracing { 'blink.console', 'blink.user_timing', 'latencyInfo', 'disabled-by-default-devtools.timeline.stack', 'disabled-by-default-v8.cpu_profiler', 'disabled-by-default-v8.cpu_profiler.hires' ]; - const categoriesArray = options.categories || defaultCategories; + const { + path = null, + screenshots = false, + categories = defaultCategories, + } = options; - if (options.screenshots) - categoriesArray.push('disabled-by-default-devtools.screenshot'); + if (screenshots) + categories.push('disabled-by-default-devtools.screenshot'); - this._path = options.path; + this._path = path; this._recording = true; await this._client.send('Tracing.start', { transferMode: 'ReturnAsStream', - categories: categoriesArray.join(',') + categories: categories.join(',') }); }