Skip to content

Commit 072dcba

Browse files
authored
api(viewport): do not allow isMobile and deviceScaleFactor for null viewports (#2190)
Also make sure we do not alter viewport when passed null.
1 parent 6361e07 commit 072dcba

File tree

7 files changed

+56
-28
lines changed

7 files changed

+56
-28
lines changed

src/browserContext.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ export function assertBrowserContextIsNotOwned(context: BrowserContextBase) {
186186

187187
export function validateBrowserContextOptions(options: BrowserContextOptions): BrowserContextOptions {
188188
const result = { ...options };
189+
if (result.viewport === null && result.deviceScaleFactor !== undefined)
190+
throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
191+
if (result.viewport === null && result.isMobile !== undefined)
192+
throw new Error(`"isMobile" option is not supported with null "viewport"`);
189193
if (!result.viewport && result.viewport !== null)
190194
result.viewport = { width: 1280, height: 720 };
191195
if (result.viewport)

src/chromium/crPage.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ class FrameSession {
429429
promises.push(this._client.send('Page.setBypassCSP', { enabled: true }));
430430
if (options.ignoreHTTPSErrors)
431431
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
432-
if (this._isMainFrame() && options.viewport)
432+
if (this._isMainFrame())
433433
promises.push(this._updateViewport());
434434
if (options.hasTouch)
435435
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: true }));
@@ -706,23 +706,23 @@ class FrameSession {
706706
async _updateViewport(): Promise<void> {
707707
assert(this._isMainFrame());
708708
const options = this._crPage._browserContext._options;
709-
let viewport = options.viewport || { width: 0, height: 0 };
710709
const viewportSize = this._page._state.viewportSize;
711-
if (viewportSize)
712-
viewport = { ...viewport, ...viewportSize };
713-
const isLandscape = viewport.width > viewport.height;
710+
if (viewportSize === null)
711+
return;
712+
const isLandscape = viewportSize.width > viewportSize.height;
714713
const promises = [
715714
this._client.send('Emulation.setDeviceMetricsOverride', {
716715
mobile: !!options.isMobile,
717-
width: viewport.width,
718-
height: viewport.height,
719-
screenWidth: viewport.width,
720-
screenHeight: viewport.height,
716+
width: viewportSize.width,
717+
height: viewportSize.height,
718+
screenWidth: viewportSize.width,
719+
screenHeight: viewportSize.height,
721720
deviceScaleFactor: options.deviceScaleFactor || 1,
722721
screenOrientation: isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' },
723722
}),
724723
];
725724
if (this._windowId) {
725+
// TODO: popup windows have their own insets.
726726
let insets = { width: 24, height: 88 };
727727
if (process.platform === 'win32')
728728
insets = { width: 16, height: 88 };
@@ -733,7 +733,7 @@ class FrameSession {
733733

734734
promises.push(this._client.send('Browser.setWindowBounds', {
735735
windowId: this._windowId,
736-
bounds: { width: viewport.width + insets.width, height: viewport.height + insets.height }
736+
bounds: { width: viewportSize.width + insets.width, height: viewportSize.height + insets.height }
737737
}));
738738
}
739739
await Promise.all(promises);

src/page.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,8 @@ export class Page extends ExtendedEventEmitter implements InnerLogger {
120120
this._disconnectedCallback = () => {};
121121
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
122122
this._browserContext = browserContext;
123-
let viewportSize: types.Size | null = null;
124-
if (browserContext._options.viewport) {
125-
viewportSize = {
126-
width: browserContext._options.viewport.width,
127-
height: browserContext._options.viewport.height,
128-
};
129-
}
130123
this._state = {
131-
viewportSize,
124+
viewportSize: browserContext._options.viewport ? { ...browserContext._options.viewport } : null,
132125
mediaType: null,
133126
colorScheme: null,
134127
extraHTTPHeaders: null,

src/webkit/wkPage.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,7 @@ export class WKPage implements PageDelegate {
100100
const contextOptions = this._browserContext._options;
101101
if (contextOptions.javaScriptEnabled === false)
102102
promises.push(this._pageProxySession.send('Emulation.setJavaScriptEnabled', { enabled: false }));
103-
if (this._page._state.viewportSize || contextOptions.viewport)
104-
promises.push(this._updateViewport());
103+
promises.push(this._updateViewport());
105104
promises.push(this.updateHttpCredentials());
106105
if (this._browserContext._permissions.size) {
107106
for (const [key, value] of this._browserContext._permissions)
@@ -573,24 +572,23 @@ export class WKPage implements PageDelegate {
573572

574573
async _updateViewport(): Promise<void> {
575574
const options = this._browserContext._options;
576-
let viewport = options.viewport || { width: 0, height: 0 };
577575
const viewportSize = this._page._state.viewportSize;
578-
if (viewportSize)
579-
viewport = { ...viewport, ...viewportSize };
576+
if (viewportSize === null)
577+
return;
580578
const promises: Promise<any>[] = [
581579
this._pageProxySession.send('Emulation.setDeviceMetricsOverride', {
582-
width: viewport.width,
583-
height: viewport.height,
580+
width: viewportSize.width,
581+
height: viewportSize.height,
584582
fixedLayout: !!options.isMobile,
585583
deviceScaleFactor: options.deviceScaleFactor || 1
586584
}),
587585
this._session.send('Page.setScreenSizeOverride', {
588-
width: viewport.width,
589-
height: viewport.height,
586+
width: viewportSize.width,
587+
height: viewportSize.height,
590588
}),
591589
];
592590
if (options.isMobile) {
593-
const angle = viewport.width > viewport.height ? 90 : 0;
591+
const angle = viewportSize.width > viewportSize.height ? 90 : 0;
594592
promises.push(this._session.send('Page.setOrientationOverride', { angle }));
595593
}
596594
await Promise.all(promises);

test/browsercontext.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ describe('BrowserContext', function() {
102102
expect(await page.evaluate('window.innerHeight')).toBe(789);
103103
await context.close();
104104
});
105+
it('should not allow deviceScaleFactor with null viewport', async({ browser }) => {
106+
const error = await browser.newContext({ viewport: null, deviceScaleFactor: 1 }).catch(e => e);
107+
expect(error.message).toBe('"deviceScaleFactor" option is not supported with null "viewport"');
108+
});
109+
it('should not allow isMobile with null viewport', async({ browser }) => {
110+
const error = await browser.newContext({ viewport: null, isMobile: true }).catch(e => e);
111+
expect(error.message).toBe('"isMobile" option is not supported with null "viewport"');
112+
});
105113
it('close() should work for empty context', async({ browser }) => {
106114
const context = await browser.newContext();
107115
await context.close();

test/emulation.spec.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ describe('BrowserContext({viewport})', function() {
7777
await page.goto(server.PREFIX + '/detect-touch.html');
7878
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
7979
});
80+
it('should support touch with null viewport', async({browser, server}) => {
81+
const context = await browser.newContext({ viewport: null, hasTouch: true });
82+
const page = await context.newPage();
83+
await page.goto(server.PREFIX + '/mobile.html');
84+
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
85+
await context.close();
86+
});
8087
});
8188

8289
describe.skip(FFOX)('viewport.isMobile', () => {

test/headful.spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,22 @@ describe('Headful', function() {
114114
}
115115
await browser.close();
116116
});
117+
it.fail(WEBKIT)('should not override viewport size when passed null', async function({browserType, defaultBrowserOptions, server}) {
118+
// Our WebKit embedder does not respect window features.
119+
const browser = await browserType.launch({...defaultBrowserOptions, headless: false });
120+
const context = await browser.newContext({ viewport: null });
121+
const page = await context.newPage();
122+
await page.goto(server.EMPTY_PAGE);
123+
const [popup] = await Promise.all([
124+
page.waitForEvent('popup'),
125+
page.evaluate(() => {
126+
const win = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top=0,left=0');
127+
win.resizeTo(500, 450);
128+
}),
129+
]);
130+
await popup.waitForLoadState();
131+
const size = await popup.evaluate(() => ({ width: window.outerWidth, height: window.outerHeight }));
132+
await context.close();
133+
expect(size).toEqual({width: 500, height: 450});
134+
});
117135
});

0 commit comments

Comments
 (0)