chore: move devices from Playwright to LocalUtils (#27437)

Also, do not create `LocalUtils` for remote connections. This avoids
sending device on every remote connect.
This commit is contained in:
Dmitry Gozman 2023-10-04 16:48:54 -07:00 committed by GitHub
parent b584a86a8b
commit 045e8aa368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 93 deletions

View file

@ -16,9 +16,25 @@
import type * as channels from '@protocol/channels'; import type * as channels from '@protocol/channels';
import { ChannelOwner } from './channelOwner'; import { ChannelOwner } from './channelOwner';
import type { Size } from './types';
type DeviceDescriptor = {
userAgent: string,
viewport: Size,
deviceScaleFactor: number,
isMobile: boolean,
hasTouch: boolean,
defaultBrowserType: 'chromium' | 'firefox' | 'webkit'
};
type Devices = { [name: string]: DeviceDescriptor };
export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> { export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> {
readonly devices: Devices;
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.LocalUtilsInitializer) { constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.LocalUtilsInitializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this.devices = {};
for (const { name, descriptor } of initializer.deviceDescriptors)
this.devices[name] = descriptor;
} }
} }

View file

@ -22,17 +22,6 @@ import { ChannelOwner } from './channelOwner';
import { Electron } from './electron'; import { Electron } from './electron';
import { APIRequest } from './fetch'; import { APIRequest } from './fetch';
import { Selectors, SelectorsOwner } from './selectors'; import { Selectors, SelectorsOwner } from './selectors';
import type { Size } from './types';
type DeviceDescriptor = {
userAgent: string,
viewport: Size,
deviceScaleFactor: number,
isMobile: boolean,
hasTouch: boolean,
defaultBrowserType: 'chromium' | 'firefox' | 'webkit'
};
type Devices = { [name: string]: DeviceDescriptor };
export class Playwright extends ChannelOwner<channels.PlaywrightChannel> { export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
readonly _android: Android; readonly _android: Android;
@ -40,7 +29,7 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
readonly chromium: BrowserType; readonly chromium: BrowserType;
readonly firefox: BrowserType; readonly firefox: BrowserType;
readonly webkit: BrowserType; readonly webkit: BrowserType;
readonly devices: Devices; readonly devices: any;
selectors: Selectors; selectors: Selectors;
readonly request: APIRequest; readonly request: APIRequest;
readonly errors: { TimeoutError: typeof TimeoutError }; readonly errors: { TimeoutError: typeof TimeoutError };
@ -56,9 +45,7 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel> {
this.webkit._playwright = this; this.webkit._playwright = this;
this._android = Android.from(initializer.android); this._android = Android.from(initializer.android);
this._electron = Electron.from(initializer.electron); this._electron = Electron.from(initializer.electron);
this.devices = {}; this.devices = this._connection.localUtils()?.devices ?? {};
for (const { name, descriptor } of initializer.deviceDescriptors)
this.devices[name] = descriptor;
this.selectors = new Selectors(); this.selectors = new Selectors();
this.errors = { TimeoutError }; this.errors = { TimeoutError };

View file

@ -217,7 +217,26 @@ scheme.APIResponse = tObject({
headers: tArray(tType('NameValue')), headers: tArray(tType('NameValue')),
}); });
scheme.LifecycleEvent = tEnum(['load', 'domcontentloaded', 'networkidle', 'commit']); scheme.LifecycleEvent = tEnum(['load', 'domcontentloaded', 'networkidle', 'commit']);
scheme.LocalUtilsInitializer = tOptional(tObject({})); scheme.LocalUtilsInitializer = tObject({
deviceDescriptors: tArray(tObject({
name: tString,
descriptor: tObject({
userAgent: tString,
viewport: tObject({
width: tNumber,
height: tNumber,
}),
screen: tOptional(tObject({
width: tNumber,
height: tNumber,
})),
deviceScaleFactor: tNumber,
isMobile: tBoolean,
hasTouch: tBoolean,
defaultBrowserType: tEnum(['chromium', 'firefox', 'webkit']),
}),
})),
});
scheme.LocalUtilsZipParams = tObject({ scheme.LocalUtilsZipParams = tObject({
zipFile: tString, zipFile: tString,
entries: tArray(tType('NameValue')), entries: tArray(tType('NameValue')),
@ -298,25 +317,7 @@ scheme.PlaywrightInitializer = tObject({
webkit: tChannel(['BrowserType']), webkit: tChannel(['BrowserType']),
android: tChannel(['Android']), android: tChannel(['Android']),
electron: tChannel(['Electron']), electron: tChannel(['Electron']),
utils: tChannel(['LocalUtils']), utils: tOptional(tChannel(['LocalUtils'])),
deviceDescriptors: tArray(tObject({
name: tString,
descriptor: tObject({
userAgent: tString,
viewport: tObject({
width: tNumber,
height: tNumber,
}),
screen: tOptional(tObject({
width: tNumber,
height: tNumber,
})),
deviceScaleFactor: tNumber,
isMobile: tBoolean,
hasTouch: tBoolean,
defaultBrowserType: tEnum(['chromium', 'firefox', 'webkit']),
}),
})),
selectors: tChannel(['Selectors']), selectors: tChannel(['Selectors']),
preLaunchedBrowser: tOptional(tChannel(['Browser'])), preLaunchedBrowser: tOptional(tChannel(['Browser'])),
preConnectedAndroidDevice: tOptional(tChannel(['AndroidDevice'])), preConnectedAndroidDevice: tOptional(tChannel(['AndroidDevice'])),

View file

@ -26,7 +26,7 @@ import { Dispatcher } from './dispatcher';
import { yazl, yauzl } from '../../zipBundle'; import { yazl, yauzl } from '../../zipBundle';
import { ZipFile } from '../../utils/zipFile'; import { ZipFile } from '../../utils/zipFile';
import type * as har from '@trace/har'; import type * as har from '@trace/har';
import type { HeadersArray } from '../types'; import type { HeadersArray, Devices } from '../types';
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher'; import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
import { WebSocketTransport } from '../transport'; import { WebSocketTransport } from '../transport';
import { SocksInterceptor } from '../socksInterceptor'; import { SocksInterceptor } from '../socksInterceptor';
@ -53,7 +53,12 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
constructor(scope: RootDispatcher, playwright: Playwright) { constructor(scope: RootDispatcher, playwright: Playwright) {
const localUtils = new SdkObject(playwright, 'localUtils', 'localUtils'); const localUtils = new SdkObject(playwright, 'localUtils', 'localUtils');
super(scope, localUtils, 'LocalUtils', {}); const descriptors = require('../deviceDescriptors') as Devices;
const deviceDescriptors = Object.entries(descriptors)
.map(([name, descriptor]) => ({ name, descriptor }));
super(scope, localUtils, 'LocalUtils', {
deviceDescriptors,
});
this._type_LocalUtils = true; this._type_LocalUtils = true;
} }

View file

@ -20,7 +20,6 @@ import { GlobalAPIRequestContext } from '../fetch';
import type { Playwright } from '../playwright'; import type { Playwright } from '../playwright';
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../common/socksProxy'; import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../common/socksProxy';
import { SocksProxy } from '../../common/socksProxy'; import { SocksProxy } from '../../common/socksProxy';
import type * as types from '../types';
import { AndroidDispatcher } from './androidDispatcher'; import { AndroidDispatcher } from './androidDispatcher';
import { BrowserTypeDispatcher } from './browserTypeDispatcher'; import { BrowserTypeDispatcher } from './browserTypeDispatcher';
import type { RootDispatcher } from './dispatcher'; import type { RootDispatcher } from './dispatcher';
@ -40,9 +39,6 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
private _browserDispatcher: ConnectedBrowserDispatcher | undefined; private _browserDispatcher: ConnectedBrowserDispatcher | undefined;
constructor(scope: RootDispatcher, playwright: Playwright, socksProxy?: SocksProxy, preLaunchedBrowser?: Browser, prelaunchedAndroidDevice?: AndroidDevice) { constructor(scope: RootDispatcher, playwright: Playwright, socksProxy?: SocksProxy, preLaunchedBrowser?: Browser, prelaunchedAndroidDevice?: AndroidDevice) {
const descriptors = require('../deviceDescriptors') as types.Devices;
const deviceDescriptors = Object.entries(descriptors)
.map(([name, descriptor]) => ({ name, descriptor }));
const browserDispatcher = preLaunchedBrowser ? new ConnectedBrowserDispatcher(scope, preLaunchedBrowser) : undefined; const browserDispatcher = preLaunchedBrowser ? new ConnectedBrowserDispatcher(scope, preLaunchedBrowser) : undefined;
const android = new AndroidDispatcher(scope, playwright.android); const android = new AndroidDispatcher(scope, playwright.android);
const prelaunchedAndroidDeviceDispatcher = prelaunchedAndroidDevice ? new AndroidDeviceDispatcher(android, prelaunchedAndroidDevice) : undefined; const prelaunchedAndroidDeviceDispatcher = prelaunchedAndroidDevice ? new AndroidDeviceDispatcher(android, prelaunchedAndroidDevice) : undefined;
@ -52,8 +48,7 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
webkit: new BrowserTypeDispatcher(scope, playwright.webkit), webkit: new BrowserTypeDispatcher(scope, playwright.webkit),
android, android,
electron: new ElectronDispatcher(scope, playwright.electron), electron: new ElectronDispatcher(scope, playwright.electron),
utils: new LocalUtilsDispatcher(scope, playwright), utils: playwright.options.isServer ? undefined : new LocalUtilsDispatcher(scope, playwright),
deviceDescriptors,
selectors: new SelectorsDispatcher(scope, browserDispatcher?.selectors || playwright.selectors), selectors: new SelectorsDispatcher(scope, browserDispatcher?.selectors || playwright.selectors),
preLaunchedBrowser: browserDispatcher, preLaunchedBrowser: browserDispatcher,
preConnectedAndroidDevice: prelaunchedAndroidDeviceDispatcher, preConnectedAndroidDevice: prelaunchedAndroidDeviceDispatcher,

View file

@ -389,7 +389,26 @@ export type APIResponse = {
export type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle' | 'commit'; export type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle' | 'commit';
// ----------- LocalUtils ----------- // ----------- LocalUtils -----------
export type LocalUtilsInitializer = {}; export type LocalUtilsInitializer = {
deviceDescriptors: {
name: string,
descriptor: {
userAgent: string,
viewport: {
width: number,
height: number,
},
screen?: {
width: number,
height: number,
},
deviceScaleFactor: number,
isMobile: boolean,
hasTouch: boolean,
defaultBrowserType: 'chromium' | 'firefox' | 'webkit',
},
}[],
};
export interface LocalUtilsEventTarget { export interface LocalUtilsEventTarget {
} }
export interface LocalUtilsChannel extends LocalUtilsEventTarget, Channel { export interface LocalUtilsChannel extends LocalUtilsEventTarget, Channel {
@ -534,25 +553,7 @@ export type PlaywrightInitializer = {
webkit: BrowserTypeChannel, webkit: BrowserTypeChannel,
android: AndroidChannel, android: AndroidChannel,
electron: ElectronChannel, electron: ElectronChannel,
utils: LocalUtilsChannel, utils?: LocalUtilsChannel,
deviceDescriptors: {
name: string,
descriptor: {
userAgent: string,
viewport: {
width: number,
height: number,
},
screen?: {
width: number,
height: number,
},
deviceScaleFactor: number,
isMobile: boolean,
hasTouch: boolean,
defaultBrowserType: 'chromium' | 'firefox' | 'webkit',
},
}[],
selectors: SelectorsChannel, selectors: SelectorsChannel,
preLaunchedBrowser?: BrowserChannel, preLaunchedBrowser?: BrowserChannel,
preConnectedAndroidDevice?: AndroidDeviceChannel, preConnectedAndroidDevice?: AndroidDeviceChannel,

View file

@ -501,6 +501,37 @@ ContextOptions:
LocalUtils: LocalUtils:
type: interface type: interface
initializer:
deviceDescriptors:
type: array
items:
type: object
properties:
name: string
descriptor:
type: object
properties:
userAgent: string
viewport:
type: object
properties:
width: number
height: number
screen:
type: object?
properties:
width: number
height: number
deviceScaleFactor: number
isMobile: boolean
hasTouch: boolean
defaultBrowserType:
type: enum
literals:
- chromium
- firefox
- webkit
commands: commands:
zip: zip:
@ -614,36 +645,7 @@ Playwright:
webkit: BrowserType webkit: BrowserType
android: Android android: Android
electron: Electron electron: Electron
utils: LocalUtils utils: LocalUtils?
deviceDescriptors:
type: array
items:
type: object
properties:
name: string
descriptor:
type: object
properties:
userAgent: string
viewport:
type: object
properties:
width: number
height: number
screen:
type: object?
properties:
width: number
height: number
deviceScaleFactor: number
isMobile: boolean
hasTouch: boolean
defaultBrowserType:
type: enum
literals:
- chromium
- firefox
- webkit
selectors: Selectors selectors: Selectors
# Only present when connecting remotely via BrowserType.connect() method. # Only present when connecting remotely via BrowserType.connect() method.
preLaunchedBrowser: Browser? preLaunchedBrowser: Browser?