chore: get rid of page state overrides (#15470)

This commit is contained in:
Pavel Feldman 2022-07-07 15:28:20 -08:00 committed by GitHub
parent 738d71ebc2
commit 0781d04a5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 66 deletions

View File

@ -98,7 +98,7 @@ export class CRPage implements PageDelegate {
const features = opener._nextWindowOpenPopupFeatures.shift() || []; const features = opener._nextWindowOpenPopupFeatures.shift() || [];
const viewportSize = helper.getViewportSizeFromWindowFeatures(features); const viewportSize = helper.getViewportSizeFromWindowFeatures(features);
if (viewportSize) if (viewportSize)
this._page._state.emulatedSize = { viewport: viewportSize, screen: viewportSize }; this._page._emulatedSize = { viewport: viewportSize, screen: viewportSize };
} }
// Note: it is important to call |reportAsNew| before resolving pageOrError promise, // Note: it is important to call |reportAsNew| before resolving pageOrError promise,
// so that anyone who awaits pageOrError got a ready and reported page. // so that anyone who awaits pageOrError got a ready and reported page.
@ -199,8 +199,7 @@ export class CRPage implements PageDelegate {
await this._forAllFrameSessions(frame => frame._updateHttpCredentials(false)); await this._forAllFrameSessions(frame => frame._updateHttpCredentials(false));
} }
async setEmulatedSize(emulatedSize: types.EmulatedSize): Promise<void> { async updateEmulatedViewportSize(): Promise<void> {
assert(this._page._state.emulatedSize === emulatedSize);
await this._mainFrameSession._updateViewport(); await this._mainFrameSession._updateViewport();
} }
@ -972,7 +971,7 @@ class FrameSession {
async _updateExtraHTTPHeaders(initial: boolean): Promise<void> { async _updateExtraHTTPHeaders(initial: boolean): Promise<void> {
const headers = network.mergeHeaders([ const headers = network.mergeHeaders([
this._crPage._browserContext._options.extraHTTPHeaders, this._crPage._browserContext._options.extraHTTPHeaders,
this._page._state.extraHTTPHeaders this._page.extraHTTPHeaders()
]); ]);
if (!initial || headers.length) if (!initial || headers.length)
await this._client.send('Network.setExtraHTTPHeaders', { headers: headersArrayToObject(headers, false /* lowerCase */) }); await this._client.send('Network.setExtraHTTPHeaders', { headers: headersArrayToObject(headers, false /* lowerCase */) });
@ -1001,7 +1000,7 @@ class FrameSession {
return; return;
assert(this._isMainFrame()); assert(this._isMainFrame());
const options = this._crPage._browserContext._options; const options = this._crPage._browserContext._options;
const emulatedSize = this._page._state.emulatedSize; const emulatedSize = this._page.emulatedSize();
if (emulatedSize === null) if (emulatedSize === null)
return; return;
const viewportSize = emulatedSize.viewport; const viewportSize = emulatedSize.viewport;
@ -1059,16 +1058,17 @@ class FrameSession {
} }
async _updateEmulateMedia(initial: boolean): Promise<void> { async _updateEmulateMedia(initial: boolean): Promise<void> {
const colorScheme = this._page._state.colorScheme === null ? '' : this._page._state.colorScheme; const emulatedMedia = this._page.emulatedMedia();
const reducedMotion = this._page._state.reducedMotion === null ? '' : this._page._state.reducedMotion; const colorScheme = emulatedMedia.colorScheme === null ? '' : emulatedMedia.colorScheme;
const forcedColors = this._page._state.forcedColors === null ? '' : this._page._state.forcedColors; const reducedMotion = emulatedMedia.reducedMotion === null ? '' : emulatedMedia.reducedMotion;
const forcedColors = emulatedMedia.forcedColors === null ? '' : emulatedMedia.forcedColors;
const features = [ const features = [
{ name: 'prefers-color-scheme', value: colorScheme }, { name: 'prefers-color-scheme', value: colorScheme },
{ name: 'prefers-reduced-motion', value: reducedMotion }, { name: 'prefers-reduced-motion', value: reducedMotion },
{ name: 'forced-colors', value: forcedColors }, { name: 'forced-colors', value: forcedColors },
]; ];
// Empty string disables the override. // Empty string disables the override.
await this._client.send('Emulation.setEmulatedMedia', { media: this._page._state.mediaType || '', features }); await this._client.send('Emulation.setEmulatedMedia', { media: emulatedMedia.media || '', features });
} }
private async _setDefaultFontFamilies(session: CRSession) { private async _setDefaultFontFamilies(session: CRSession) {
@ -1081,7 +1081,7 @@ class FrameSession {
} }
async _updateFileChooserInterception(initial: boolean) { async _updateFileChooserInterception(initial: boolean) {
const enabled = this._page._state.interceptFileChooser; const enabled = this._page.fileChooserIntercepted();
if (initial && !enabled) if (initial && !enabled)
return; return;
await this._client.send('Page.setInterceptFileChooserDialog', { enabled }).catch(e => {}); // target can be closed. await this._client.send('Page.setInterceptFileChooserDialog', { enabled }).catch(e => {}); // target can be closed.

View File

@ -20,7 +20,6 @@ import * as dom from '../dom';
import type * as frames from '../frames'; import type * as frames from '../frames';
import type { RegisteredListener } from '../../utils/eventsHelper'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils/eventsHelper'; import { eventsHelper } from '../../utils/eventsHelper';
import { assert } from '../../utils';
import type { PageBinding, PageDelegate } from '../page'; import type { PageBinding, PageDelegate } from '../page';
import { Page, Worker } from '../page'; import { Page, Worker } from '../page';
import type * as types from '../types'; import type * as types from '../types';
@ -352,17 +351,12 @@ export class FFPage implements PageDelegate {
} }
async updateExtraHTTPHeaders(): Promise<void> { async updateExtraHTTPHeaders(): Promise<void> {
await this._session.send('Network.setExtraHTTPHeaders', { headers: this._page._state.extraHTTPHeaders || [] }); await this._session.send('Network.setExtraHTTPHeaders', { headers: this._page.extraHTTPHeaders() || [] });
} }
async setEmulatedSize(emulatedSize: types.EmulatedSize): Promise<void> { async updateEmulatedViewportSize(): Promise<void> {
assert(this._page._state.emulatedSize === emulatedSize); const viewportSize = this._page.viewportSize();
await this._session.send('Page.setViewportSize', { await this._session.send('Page.setViewportSize', { viewportSize });
viewportSize: {
width: emulatedSize.viewport.width,
height: emulatedSize.viewport.height,
},
});
} }
async bringToFront(): Promise<void> { async bringToFront(): Promise<void> {
@ -370,12 +364,13 @@ export class FFPage implements PageDelegate {
} }
async updateEmulateMedia(): Promise<void> { async updateEmulateMedia(): Promise<void> {
const colorScheme = this._page._state.colorScheme === null ? undefined : this._page._state.colorScheme; const emulatedMedia = this._page.emulatedMedia();
const reducedMotion = this._page._state.reducedMotion === null ? undefined : this._page._state.reducedMotion; const colorScheme = emulatedMedia.colorScheme ?? undefined;
const forcedColors = this._page._state.forcedColors === null ? undefined : this._page._state.forcedColors; const reducedMotion = emulatedMedia.reducedMotion ?? undefined;
const forcedColors = emulatedMedia.forcedColors ?? undefined;
await this._session.send('Page.setEmulatedMedia', { await this._session.send('Page.setEmulatedMedia', {
// Empty string means reset. // Empty string means reset.
type: this._page._state.mediaType === null ? '' : this._page._state.mediaType, type: emulatedMedia.media === null ? '' : emulatedMedia.media,
colorScheme, colorScheme,
reducedMotion, reducedMotion,
forcedColors, forcedColors,

View File

@ -653,7 +653,7 @@ export class Frame extends SdkObject {
private async _gotoAction(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> { private async _gotoAction(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> {
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`); progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
const headers = this._page._state.extraHTTPHeaders || []; const headers = this._page.extraHTTPHeaders() || [];
const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer'); const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer');
let referer = refererHeader ? refererHeader.value : undefined; let referer = refererHeader ? refererHeader.value : undefined;
if (options.referer !== undefined) { if (options.referer !== undefined) {

View File

@ -65,7 +65,7 @@ export interface PageDelegate {
navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>; navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>;
updateExtraHTTPHeaders(): Promise<void>; updateExtraHTTPHeaders(): Promise<void>;
setEmulatedSize(emulatedSize: types.EmulatedSize): Promise<void>; updateEmulatedViewportSize(): Promise<void>;
updateEmulateMedia(): Promise<void>; updateEmulateMedia(): Promise<void>;
updateRequestInterception(): Promise<void>; updateRequestInterception(): Promise<void>;
updateFileChooserInterception(enabled: boolean): Promise<void>; updateFileChooserInterception(enabled: boolean): Promise<void>;
@ -98,14 +98,13 @@ export interface PageDelegate {
readonly cspErrorsAsynchronousForInlineScipts?: boolean; readonly cspErrorsAsynchronousForInlineScipts?: boolean;
} }
type PageState = { type EmulatedSize = { screen: types.Size, viewport: types.Size };
emulatedSize: { screen: types.Size, viewport: types.Size } | null;
mediaType: types.MediaType | null; type EmulatedMedia = {
media: types.MediaType | null;
colorScheme: types.ColorScheme | null; colorScheme: types.ColorScheme | null;
reducedMotion: types.ReducedMotion | null; reducedMotion: types.ReducedMotion | null;
forcedColors: types.ForcedColors | null; forcedColors: types.ForcedColors | null;
extraHTTPHeaders: types.HeadersArray | null;
interceptFileChooser: boolean;
}; };
type ExpectScreenshotOptions = { type ExpectScreenshotOptions = {
@ -152,7 +151,10 @@ export class Page extends SdkObject {
readonly touchscreen: input.Touchscreen; readonly touchscreen: input.Touchscreen;
readonly _timeoutSettings: TimeoutSettings; readonly _timeoutSettings: TimeoutSettings;
readonly _delegate: PageDelegate; readonly _delegate: PageDelegate;
readonly _state: PageState; _emulatedSize: EmulatedSize | undefined;
private _extraHTTPHeaders: types.HeadersArray | undefined;
private _emulatedMedia: Partial<EmulatedMedia> = {};
private _interceptFileChooser = false;
private readonly _pageBindings = new Map<string, PageBinding>(); private readonly _pageBindings = new Map<string, PageBinding>();
readonly initScripts: string[] = []; readonly initScripts: string[] = [];
readonly _screenshotter: Screenshotter; readonly _screenshotter: Screenshotter;
@ -176,15 +178,6 @@ export class Page extends SdkObject {
this.attribution.page = this; this.attribution.page = this;
this._delegate = delegate; this._delegate = delegate;
this._browserContext = browserContext; this._browserContext = browserContext;
this._state = {
emulatedSize: browserContext._options.viewport ? { viewport: browserContext._options.viewport, screen: browserContext._options.screen || browserContext._options.viewport } : null,
mediaType: null,
colorScheme: browserContext._options.colorScheme !== undefined ? browserContext._options.colorScheme : 'light',
reducedMotion: browserContext._options.reducedMotion !== undefined ? browserContext._options.reducedMotion : 'no-preference',
forcedColors: browserContext._options.forcedColors !== undefined ? browserContext._options.forcedColors : 'none',
extraHTTPHeaders: null,
interceptFileChooser: false,
};
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate)); this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
this.keyboard = new input.Keyboard(delegate.rawKeyboard, this); this.keyboard = new input.Keyboard(delegate.rawKeyboard, this);
this.mouse = new input.Mouse(delegate.rawMouse, this); this.mouse = new input.Mouse(delegate.rawMouse, this);
@ -327,10 +320,14 @@ export class Page extends SdkObject {
} }
setExtraHTTPHeaders(headers: types.HeadersArray) { setExtraHTTPHeaders(headers: types.HeadersArray) {
this._state.extraHTTPHeaders = headers; this._extraHTTPHeaders = headers;
return this._delegate.updateExtraHTTPHeaders(); return this._delegate.updateExtraHTTPHeaders();
} }
extraHTTPHeaders(): types.HeadersArray | undefined {
return this._extraHTTPHeaders;
}
async _onBindingCalled(payload: string, context: dom.FrameExecutionContext) { async _onBindingCalled(payload: string, context: dom.FrameExecutionContext) {
if (this._disconnected || this._closedState === 'closed') if (this._disconnected || this._closedState === 'closed')
return; return;
@ -402,27 +399,44 @@ export class Page extends SdkObject {
}), this._timeoutSettings.navigationTimeout(options)); }), this._timeoutSettings.navigationTimeout(options));
} }
async emulateMedia(options: { media?: types.MediaType | null, colorScheme?: types.ColorScheme | null, reducedMotion?: types.ReducedMotion | null, forcedColors?: types.ForcedColors | null }) { async emulateMedia(options: Partial<EmulatedMedia>) {
if (options.media !== undefined) if (options.media !== undefined)
this._state.mediaType = options.media; this._emulatedMedia.media = options.media;
if (options.colorScheme !== undefined) if (options.colorScheme !== undefined)
this._state.colorScheme = options.colorScheme; this._emulatedMedia.colorScheme = options.colorScheme;
if (options.reducedMotion !== undefined) if (options.reducedMotion !== undefined)
this._state.reducedMotion = options.reducedMotion; this._emulatedMedia.reducedMotion = options.reducedMotion;
if (options.forcedColors !== undefined) if (options.forcedColors !== undefined)
this._state.forcedColors = options.forcedColors; this._emulatedMedia.forcedColors = options.forcedColors;
await this._delegate.updateEmulateMedia(); await this._delegate.updateEmulateMedia();
await this._doSlowMo(); await this._doSlowMo();
} }
emulatedMedia(): EmulatedMedia {
const contextOptions = this._browserContext._options;
return {
media: this._emulatedMedia.media || null,
colorScheme: this._emulatedMedia.colorScheme !== undefined ? this._emulatedMedia.colorScheme : contextOptions.colorScheme ?? 'light',
reducedMotion: this._emulatedMedia.reducedMotion !== undefined ? this._emulatedMedia.reducedMotion : contextOptions.reducedMotion ?? 'no-preference',
forcedColors: this._emulatedMedia.forcedColors !== undefined ? this._emulatedMedia.forcedColors : contextOptions.forcedColors ?? 'none',
};
}
async setViewportSize(viewportSize: types.Size) { async setViewportSize(viewportSize: types.Size) {
this._state.emulatedSize = { viewport: { ...viewportSize }, screen: { ...viewportSize } }; this._emulatedSize = { viewport: { ...viewportSize }, screen: { ...viewportSize } };
await this._delegate.setEmulatedSize(this._state.emulatedSize); await this._delegate.updateEmulatedViewportSize();
await this._doSlowMo(); await this._doSlowMo();
} }
viewportSize(): types.Size | null { viewportSize(): types.Size | null {
return this._state.emulatedSize?.viewport || null; return this.emulatedSize()?.viewport || null;
}
emulatedSize(): EmulatedSize | null {
if (this._emulatedSize)
return this._emulatedSize;
const contextOptions = this._browserContext._options;
return contextOptions.viewport ? { viewport: contextOptions.viewport, screen: contextOptions.screen || contextOptions.viewport } : null;
} }
async bringToFront(): Promise<void> { async bringToFront(): Promise<void> {
@ -604,10 +618,14 @@ export class Page extends SdkObject {
} }
async setFileChooserIntercepted(enabled: boolean): Promise<void> { async setFileChooserIntercepted(enabled: boolean): Promise<void> {
this._state.interceptFileChooser = enabled; this._interceptFileChooser = enabled;
await this._delegate.updateFileChooserInterception(enabled); await this._delegate.updateFileChooserInterception(enabled);
} }
fileChooserIntercepted() {
return this._interceptFileChooser;
}
frameNavigatedToNewDocument(frame: frames.Frame) { frameNavigatedToNewDocument(frame: frames.Frame) {
this.emit(Page.Events.InternalFrameNavigatedToNewDocument, frame); this.emit(Page.Events.InternalFrameNavigatedToNewDocument, frame);
const url = frame.url(); const url = frame.url();

View File

@ -106,7 +106,7 @@ export class WKPage implements PageDelegate {
const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures); const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures);
opener._nextWindowOpenPopupFeatures = undefined; opener._nextWindowOpenPopupFeatures = undefined;
if (viewportSize) if (viewportSize)
this._page._state.emulatedSize = { viewport: viewportSize, screen: viewportSize }; this._page._emulatedSize = { viewport: viewportSize, screen: viewportSize };
} }
} }
@ -194,18 +194,20 @@ export class WKPage implements PageDelegate {
const contextOptions = this._browserContext._options; const contextOptions = this._browserContext._options;
if (contextOptions.userAgent) if (contextOptions.userAgent)
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent })); promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
if (this._page._state.mediaType || this._page._state.colorScheme || this._page._state.reducedMotion) const emulatedMedia = this._page.emulatedMedia();
promises.push(WKPage._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme, this._page._state.reducedMotion)); if (emulatedMedia.media || emulatedMedia.colorScheme || emulatedMedia.reducedMotion)
promises.push(WKPage._setEmulateMedia(session, emulatedMedia.media, emulatedMedia.colorScheme, emulatedMedia.reducedMotion));
const bootstrapScript = this._calculateBootstrapScript(); const bootstrapScript = this._calculateBootstrapScript();
if (bootstrapScript.length) if (bootstrapScript.length)
promises.push(session.send('Page.setBootstrapScript', { source: bootstrapScript })); promises.push(session.send('Page.setBootstrapScript', { source: bootstrapScript }));
this._page.frames().map(frame => frame.evaluateExpression(bootstrapScript, false, undefined).catch(e => {})); this._page.frames().map(frame => frame.evaluateExpression(bootstrapScript, false, undefined).catch(e => {}));
if (contextOptions.bypassCSP) if (contextOptions.bypassCSP)
promises.push(session.send('Page.setBypassCSP', { enabled: true })); promises.push(session.send('Page.setBypassCSP', { enabled: true }));
if (this._page._state.emulatedSize) { const emulatedSize = this._page.emulatedSize();
if (emulatedSize) {
promises.push(session.send('Page.setScreenSizeOverride', { promises.push(session.send('Page.setScreenSizeOverride', {
width: this._page._state.emulatedSize.screen.width, width: emulatedSize.screen.width,
height: this._page._state.emulatedSize.screen.height, height: emulatedSize.screen.height,
})); }));
} }
promises.push(this.updateEmulateMedia()); promises.push(this.updateEmulateMedia());
@ -217,7 +219,7 @@ export class WKPage implements PageDelegate {
promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }). promises.push(session.send('Page.setTimeZone', { timeZone: contextOptions.timezoneId }).
catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); })); catch(e => { throw new Error(`Invalid timezone ID: ${contextOptions.timezoneId}`); }));
} }
if (this._page._state.interceptFileChooser) if (this._page.fileChooserIntercepted())
promises.push(session.send('Page.setInterceptFileChooserDialog', { enabled: true })); promises.push(session.send('Page.setInterceptFileChooserDialog', { enabled: true }));
promises.push(session.send('Page.overrideSetting', { setting: 'DeviceOrientationEventEnabled' as any, value: contextOptions.isMobile })); promises.push(session.send('Page.overrideSetting', { setting: 'DeviceOrientationEventEnabled' as any, value: contextOptions.isMobile }));
promises.push(session.send('Page.overrideSetting', { setting: 'FullScreenEnabled' as any, value: !contextOptions.isMobile })); promises.push(session.send('Page.overrideSetting', { setting: 'FullScreenEnabled' as any, value: !contextOptions.isMobile }));
@ -658,20 +660,20 @@ export class WKPage implements PageDelegate {
const locale = this._browserContext._options.locale; const locale = this._browserContext._options.locale;
const headers = network.mergeHeaders([ const headers = network.mergeHeaders([
this._browserContext._options.extraHTTPHeaders, this._browserContext._options.extraHTTPHeaders,
this._page._state.extraHTTPHeaders, this._page.extraHTTPHeaders(),
locale ? network.singleHeader('Accept-Language', locale) : undefined, locale ? network.singleHeader('Accept-Language', locale) : undefined,
]); ]);
return headers; return headers;
} }
async updateEmulateMedia(): Promise<void> { async updateEmulateMedia(): Promise<void> {
const colorScheme = this._page._state.colorScheme; const emulatedMedia = this._page.emulatedMedia();
const reducedMotion = this._page._state.reducedMotion; const colorScheme = emulatedMedia.colorScheme;
await this._forAllSessions(session => WKPage._setEmulateMedia(session, this._page._state.mediaType, colorScheme, reducedMotion)); const reducedMotion = emulatedMedia.reducedMotion;
await this._forAllSessions(session => WKPage._setEmulateMedia(session, emulatedMedia.media, colorScheme, reducedMotion));
} }
async setEmulatedSize(emulatedSize: types.EmulatedSize): Promise<void> { async updateEmulatedViewportSize(): Promise<void> {
assert(this._page._state.emulatedSize === emulatedSize);
await this._updateViewport(); await this._updateViewport();
} }
@ -683,7 +685,7 @@ export class WKPage implements PageDelegate {
async _updateViewport(): Promise<void> { async _updateViewport(): Promise<void> {
const options = this._browserContext._options; const options = this._browserContext._options;
const deviceSize = this._page._state.emulatedSize; const deviceSize = this._page.emulatedSize();
if (deviceSize === null) if (deviceSize === null)
return; return;
const viewportSize = deviceSize.viewport; const viewportSize = deviceSize.viewport;
@ -725,7 +727,7 @@ export class WKPage implements PageDelegate {
} }
async updateFileChooserInterception() { async updateFileChooserInterception() {
const enabled = this._page._state.interceptFileChooser; const enabled = this._page.fileChooserIntercepted();
await this._session.send('Page.setInterceptFileChooserDialog', { enabled }).catch(e => {}); // target can be closed. await this._session.send('Page.setInterceptFileChooserDialog', { enabled }).catch(e => {}); // target can be closed.
} }