diff --git a/docs/api.md b/docs/api.md index 344feb38b71..77bc26953cb 100644 --- a/docs/api.md +++ b/docs/api.md @@ -359,6 +359,13 @@ puppeteer.launch().then(async browser => { - `options` <[Object]> - `browserWSEndpoint` <[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to. - `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`. + - `defaultViewport` Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport. + - `width` <[number]> page width in pixels. + - `height` <[number]> page height in pixels. + - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`. + - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`. + - `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. - returns: <[Promise]<[Browser]>> @@ -383,6 +390,13 @@ This methods attaches Puppeteer to an existing Chromium instance. - `headless` <[boolean]> Whether to run browser in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`. - `executablePath` <[string]> Path to a Chromium or Chrome executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). - `slowMo` <[number]> Slows down Puppeteer operations by the specified amount of milliseconds. Useful so that you can see what is going on. + - `defaultViewport` Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport. + - `width` <[number]> page width in pixels. + - `height` <[number]> page height in pixels. + - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`. + - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`. + - `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]> Do not use [`puppeteer.defaultArgs()`](#puppeteerdefaultargs). Dangerous option; use with care. Defaults to `false`. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`. @@ -1546,7 +1560,7 @@ Shortcut for [page.mainFrame().type(selector, text[, options])](#frametypeselect This is a shortcut for [page.mainFrame().url()](#frameurl) #### page.viewport() -- returns: <[Object]> +- returns: - `width` <[number]> page width in pixels. - `height` <[number]> page height in pixels. - `deviceScaleFactor` <[number]> Specify device scale factor (can be though of as dpr). Defaults to `1`. diff --git a/lib/Browser.js b/lib/Browser.js index e6c437ad4a9..d4834104e07 100644 --- a/lib/Browser.js +++ b/lib/Browser.js @@ -24,14 +24,14 @@ class Browser extends EventEmitter { * @param {!Puppeteer.Connection} connection * @param {!Array} contextIds * @param {boolean} ignoreHTTPSErrors - * @param {boolean} setDefaultViewport + * @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.ChildProcess} process * @param {(function():Promise)=} closeCallback */ - constructor(connection, contextIds, ignoreHTTPSErrors, setDefaultViewport, process, closeCallback) { + constructor(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) { super(); this._ignoreHTTPSErrors = ignoreHTTPSErrors; - this._setDefaultViewport = setDefaultViewport; + this._defaultViewport = defaultViewport; this._process = process; this._screenshotTaskQueue = new TaskQueue(); this._connection = connection; @@ -89,12 +89,12 @@ class Browser extends EventEmitter { * @param {!Puppeteer.Connection} connection * @param {!Array} contextIds * @param {boolean} ignoreHTTPSErrors - * @param {boolean} appMode + * @param {?Puppeteer.Viewport} defaultViewport * @param {?Puppeteer.ChildProcess} process * @param {function()=} closeCallback */ - static async create(connection, contextIds, ignoreHTTPSErrors, appMode, process, closeCallback) { - const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, appMode, process, closeCallback); + static async create(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) { + const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback); await connection.send('Target.setDiscoverTargets', {discover: true}); return browser; } @@ -107,7 +107,7 @@ class Browser extends EventEmitter { const {browserContextId} = targetInfo; const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext; - const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._setDefaultViewport, this._screenshotTaskQueue); + const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue); assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated'); this._targets.set(event.targetInfo.targetId, target); @@ -300,6 +300,6 @@ module.exports = {Browser, BrowserContext}; /** * @typedef {Object} BrowserOptions - * @property {boolean=} appMode + * @property {boolean=} setDefaultViewport * @property {boolean=} ignoreHTTPSErrors */ diff --git a/lib/EmulationManager.js b/lib/EmulationManager.js index 4e2e4385fa8..2de84554850 100644 --- a/lib/EmulationManager.js +++ b/lib/EmulationManager.js @@ -25,7 +25,7 @@ class EmulationManager { } /** - * @param {!EmulationManager.Viewport} viewport + * @param {!Puppeteer.Viewport} viewport * @return {Promise} */ async emulateViewport(viewport) { @@ -51,14 +51,4 @@ class EmulationManager { } } -/** - * @typedef {Object} EmulationManager.Viewport - * @property {number} width - * @property {number} height - * @property {number=} deviceScaleFactor - * @property {boolean=} isMobile - * @property {boolean=} isLandscape - * @property {boolean=} hasTouch - */ - module.exports = EmulationManager; diff --git a/lib/Launcher.js b/lib/Launcher.js index 290e2158aea..a9ecbc5b7df 100644 --- a/lib/Launcher.js +++ b/lib/Launcher.js @@ -58,22 +58,17 @@ const AUTOMATION_ARGS = [ class Launcher { /** - * @param {!LaunchOptions=} options + * @param {!(LaunchOptions & BrowserOptions)=} options * @return {!Promise} */ static async launch(options) { options = Object.assign({}, options || {}); - assert(!options.ignoreDefaultArgs || !options.appMode, '`appMode` flag cannot be used together with `ignoreDefaultArgs`'); let temporaryUserDataDir = null; const chromeArguments = []; if (!options.ignoreDefaultArgs) chromeArguments.push(...DEFAULT_ARGS); - if (options.appMode) { - options.headless = false; - options.pipe = true; - } else if (!options.ignoreDefaultArgs) { + if (!options.ignoreDefaultArgs) chromeArguments.push(...AUTOMATION_ARGS); - } if (!options.ignoreDefaultArgs || !chromeArguments.some(argument => argument.startsWith('--remote-debugging-'))) chromeArguments.push(options.pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0'); @@ -155,17 +150,19 @@ class Launcher { /** @type {?Connection} */ let connection = null; try { - const connectionDelay = options.slowMo || 0; + const { + ignoreHTTPSErrors = false, + defaultViewport = {width: 800, height: 600}, + slowMo = 0 + } = options; if (!usePipe) { const timeout = helper.isNumber(options.timeout) ? options.timeout : 30000; const browserWSEndpoint = await waitForWSEndpoint(chromeProcess, timeout); - connection = await Connection.createForWebSocket(browserWSEndpoint, connectionDelay); + connection = await Connection.createForWebSocket(browserWSEndpoint, slowMo); } else { - connection = Connection.createForPipe(/** @type {!NodeJS.WritableStream} */(chromeProcess.stdio[3]), /** @type {!NodeJS.ReadableStream} */ (chromeProcess.stdio[4]), connectionDelay); + connection = Connection.createForPipe(/** @type {!NodeJS.WritableStream} */(chromeProcess.stdio[3]), /** @type {!NodeJS.ReadableStream} */ (chromeProcess.stdio[4]), slowMo); } - const ignoreHTTPSErrors = !!options.ignoreHTTPSErrors; - const setDefaultViewport = !options.appMode; - const browser = await Browser.create(connection, [], ignoreHTTPSErrors, setDefaultViewport, chromeProcess, gracefullyCloseChrome); + const browser = await Browser.create(connection, [], ignoreHTTPSErrors, defaultViewport, chromeProcess, gracefullyCloseChrome); await ensureInitialPage(browser); return browser; } catch (e) { @@ -247,15 +244,19 @@ class Launcher { } /** - * @param {!Object=} options + * @param {!(BrowserOptions & {browserWSEndpoint: string})=} options * @return {!Promise} */ - static async connect(options = {}) { - const connectionDelay = options.slowMo || 0; - const connection = await Connection.createForWebSocket(options.browserWSEndpoint, connectionDelay); + static async connect(options) { + const { + browserWSEndpoint, + ignoreHTTPSErrors = false, + defaultViewport = {width: 800, height: 600}, + slowMo = 0, + } = options; + const connection = await Connection.createForWebSocket(browserWSEndpoint, slowMo); const {browserContextIds} = await connection.send('Target.getBrowserContexts'); - const ignoreHTTPSErrors = !!options.ignoreHTTPSErrors; - return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, true /* setDefaultViewport */, null, () => connection.send('Browser.close').catch(debugError)); + return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, defaultViewport, null, () => connection.send('Browser.close').catch(debugError)); } } @@ -317,10 +318,8 @@ function waitForWSEndpoint(chromeProcess, timeout) { /** * @typedef {Object} LaunchOptions - * @property {boolean=} ignoreHTTPSErrors * @property {boolean=} headless * @property {string=} executablePath - * @property {number=} slowMo * @property {!Array=} args * @property {boolean=} ignoreDefaultArgs * @property {boolean=} handleSIGINT @@ -332,7 +331,13 @@ function waitForWSEndpoint(chromeProcess, timeout) { * @property {!Object=} env * @property {boolean=} devtools * @property {boolean=} pipe - * @property {boolean=} appMode + */ + +/** + * @typedef {Object} BrowserOptions + * @property {boolean=} ignoreHTTPSErrors + * @property {(?Puppeteer.Viewport)=} defaultViewport + * @property {number=} slowMo */ diff --git a/lib/Page.js b/lib/Page.js index 2d01c6a6fa5..5bdc30cacbc 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -35,11 +35,11 @@ class Page extends EventEmitter { * @param {!Puppeteer.CDPSession} client * @param {!Puppeteer.Target} target * @param {boolean} ignoreHTTPSErrors - * @param {boolean} setDefaultViewport + * @param {?Puppeteer.Viewport} defaultViewport * @param {!Puppeteer.TaskQueue} screenshotTaskQueue * @return {!Promise} */ - static async create(client, target, ignoreHTTPSErrors, setDefaultViewport, screenshotTaskQueue) { + static async create(client, target, ignoreHTTPSErrors, defaultViewport, screenshotTaskQueue) { await client.send('Page.enable'); const {frameTree} = await client.send('Page.getFrameTree'); @@ -57,8 +57,8 @@ class Page extends EventEmitter { if (ignoreHTTPSErrors) await client.send('Security.setOverrideCertificateErrors', {override: true}); // Initialize default page size. - if (setDefaultViewport) - await page.setViewport({width: 800, height: 600}); + if (defaultViewport) + await page.setViewport(defaultViewport); return page; } @@ -89,6 +89,8 @@ class Page extends EventEmitter { this._coverage = new Coverage(client); this._defaultNavigationTimeout = 30000; this._javascriptEnabled = true; + /** @type {?Puppeteer.Viewport} */ + this._viewport = null; this._screenshotTaskQueue = screenshotTaskQueue; @@ -738,7 +740,7 @@ class Page extends EventEmitter { } /** - * @param {!Page.Viewport} viewport + * @param {!Puppeteer.Viewport} viewport */ async setViewport(viewport) { const needsReload = await this._emulationManager.emulateViewport(viewport); @@ -748,7 +750,7 @@ class Page extends EventEmitter { } /** - * @return {!Page.Viewport} + * @return {?Puppeteer.Viewport} */ viewport() { return this._viewport; @@ -1128,15 +1130,6 @@ Page.Events = { WorkerDestroyed: 'workerdestroyed', }; -/** - * @typedef {Object} Page.Viewport - * @property {number} width - * @property {number} height - * @property {number=} deviceScaleFactor - * @property {boolean=} isMobile - * @property {boolean=} isLandscape - * @property {boolean=} hasTouch - */ /** * @typedef {Object} Network.Cookie diff --git a/lib/Target.js b/lib/Target.js index 8d15b241ee1..7f5bafa11f0 100644 --- a/lib/Target.js +++ b/lib/Target.js @@ -7,16 +7,16 @@ class Target { * @param {!Puppeteer.BrowserContext} browserContext * @param {!function():!Promise} sessionFactory * @param {boolean} ignoreHTTPSErrors - * @param {boolean} setDefaultViewport + * @param {?Puppeteer.Viewport} defaultViewport * @param {!Puppeteer.TaskQueue} screenshotTaskQueue */ - constructor(targetInfo, browserContext, sessionFactory, ignoreHTTPSErrors, setDefaultViewport, screenshotTaskQueue) { + constructor(targetInfo, browserContext, sessionFactory, ignoreHTTPSErrors, defaultViewport, screenshotTaskQueue) { this._targetInfo = targetInfo; this._browserContext = browserContext; this._targetId = targetInfo.targetId; this._sessionFactory = sessionFactory; this._ignoreHTTPSErrors = ignoreHTTPSErrors; - this._setDefaultViewport = setDefaultViewport; + this._defaultViewport = defaultViewport; this._screenshotTaskQueue = screenshotTaskQueue; /** @type {?Promise} */ this._pagePromise = null; @@ -40,7 +40,7 @@ class Target { async page() { if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) { this._pagePromise = this._sessionFactory() - .then(client => Page.create(client, this, this._ignoreHTTPSErrors, this._setDefaultViewport, this._screenshotTaskQueue)); + .then(client => Page.create(client, this, this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue)); } return this._pagePromise; } diff --git a/lib/externs.d.ts b/lib/externs.d.ts index 328c0d27fa7..6a4076c8f1a 100644 --- a/lib/externs.d.ts +++ b/lib/externs.d.ts @@ -38,7 +38,15 @@ declare global { close(); } - export interface ChildProcess extends child_process.ChildProcess {} + export interface ChildProcess extends child_process.ChildProcess { } + export type Viewport = { + width: number; + height: number; + deviceScaleFactor?: number; + isMobile?: boolean; + isLandscape?: boolean; + hasTouch?: boolean; + } } } diff --git a/test/puppeteer.spec.js b/test/puppeteer.spec.js index 1afa2715e63..dffd15149af 100644 --- a/test/puppeteer.spec.js +++ b/test/puppeteer.spec.js @@ -60,16 +60,6 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro await rmAsync(downloadsFolder); }); }); - describe('AppMode', function() { - it('should work', async() => { - const options = Object.assign({appMode: true}, defaultBrowserOptions); - const browser = await puppeteer.launch(options); - const page = await browser.newPage(); - expect(await page.evaluate('11 * 11')).toBe(121); - await page.close(); - await browser.close(); - }); - }); describe('Puppeteer.launch', function() { it('should reject all promises when browser is closed', async() => { @@ -229,6 +219,28 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro expect(pages[0].url()).toBe(customUrl); await browser.close(); }); + it('should set the default viewport', async() => { + const options = Object.assign({}, defaultBrowserOptions, { + defaultViewport: { + width: 456, + height: 789 + } + }); + const browser = await puppeteer.launch(options); + const page = await browser.newPage(); + expect(await page.evaluate('window.innerWidth')).toBe(456); + expect(await page.evaluate('window.innerHeight')).toBe(789); + await browser.close(); + }); + it('should disable the default viewport', async() => { + const options = Object.assign({}, defaultBrowserOptions, { + defaultViewport: null + }); + const browser = await puppeteer.launch(options); + const page = await browser.newPage(); + expect(page.viewport()).toBe(null); + await browser.close(); + }); }); describe('Puppeteer.connect', function() { it('should be able to connect multiple times to the same browser', async({server}) => {