豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit bc3c40e

Browse files
authored
refactor: move emulation settings to context (#1000)
- reduces boilerplate - allows easily to restore emulation settings This is in preparation for the integration with Lighthouse.
1 parent e9a1dea commit bc3c40e

File tree

7 files changed

+161
-173
lines changed

7 files changed

+161
-173
lines changed

src/McpContext.ts

Lines changed: 124 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
} from './DevtoolsUtils.js';
1717
import type {ListenerMap, UncaughtError} from './PageCollector.js';
1818
import {NetworkCollector, ConsoleCollector} from './PageCollector.js';
19-
import {Locator} from './third_party/index.js';
2019
import type {DevTools} from './third_party/index.js';
2120
import type {
2221
Browser,
@@ -27,9 +26,10 @@ import type {
2726
HTTPRequest,
2827
Page,
2928
SerializedAXNode,
30-
PredefinedNetworkConditions,
3129
Viewport,
3230
} from './third_party/index.js';
31+
import {Locator} from './third_party/index.js';
32+
import {PredefinedNetworkConditions} from './third_party/index.js';
3333
import {listPages} from './tools/pages.js';
3434
import {takeSnapshot} from './tools/snapshot.js';
3535
import {CLOSE_PAGE_ERROR} from './tools/ToolDefinition.js';
@@ -64,6 +64,15 @@ export interface TextSnapshot {
6464
verbose: boolean;
6565
}
6666

67+
interface EmulationSettings {
68+
networkConditions?: string | null;
69+
cpuThrottlingRate?: number | null;
70+
geolocation?: GeolocationOptions | null;
71+
userAgent?: string | null;
72+
colorScheme?: 'dark' | 'light' | null;
73+
viewport?: Viewport | null;
74+
}
75+
6776
interface McpContextOptions {
6877
// Whether the DevTools windows are exposed as pages for debugging of DevTools.
6978
experimentalDevToolsDebugging: boolean;
@@ -121,12 +130,7 @@ export class McpContext implements Context {
121130
#extensionRegistry = new ExtensionRegistry();
122131

123132
#isRunningTrace = false;
124-
#networkConditionsMap = new WeakMap<Page, string>();
125-
#cpuThrottlingRateMap = new WeakMap<Page, number>();
126-
#geolocationMap = new WeakMap<Page, GeolocationOptions>();
127-
#viewportMap = new WeakMap<Page, Viewport>();
128-
#userAgentMap = new WeakMap<Page, string>();
129-
#colorSchemeMap = new WeakMap<Page, 'dark' | 'light'>();
133+
#emulationSettingsMap = new WeakMap<Page, EmulationSettings>();
130134
#dialog?: Dialog;
131135

132136
#pageIdMap = new WeakMap<Page, number>();
@@ -282,86 +286,146 @@ export class McpContext implements Context {
282286
return this.#networkCollector.getById(this.getSelectedPage(), reqid);
283287
}
284288

285-
setNetworkConditions(conditions: string | null): void {
289+
async emulate(options: {
290+
networkConditions?: string | null;
291+
cpuThrottlingRate?: number | null;
292+
geolocation?: GeolocationOptions | null;
293+
userAgent?: string | null;
294+
colorScheme?: 'dark' | 'light' | 'auto' | null;
295+
viewport?: Viewport | null;
296+
}): Promise<void> {
286297
const page = this.getSelectedPage();
287-
if (conditions === null) {
288-
this.#networkConditionsMap.delete(page);
289-
} else {
290-
this.#networkConditionsMap.set(page, conditions);
298+
const currentSettings = this.#emulationSettingsMap.get(page) ?? {};
299+
const newSettings: EmulationSettings = {...currentSettings};
300+
let timeoutsNeedUpdate = false;
301+
302+
if (options.networkConditions !== undefined) {
303+
timeoutsNeedUpdate = true;
304+
if (
305+
options.networkConditions === null ||
306+
options.networkConditions === 'No emulation'
307+
) {
308+
await page.emulateNetworkConditions(null);
309+
delete newSettings.networkConditions;
310+
} else if (options.networkConditions === 'Offline') {
311+
await page.emulateNetworkConditions({
312+
offline: true,
313+
download: 0,
314+
upload: 0,
315+
latency: 0,
316+
});
317+
newSettings.networkConditions = 'Offline';
318+
} else if (options.networkConditions in PredefinedNetworkConditions) {
319+
const networkCondition =
320+
PredefinedNetworkConditions[
321+
options.networkConditions as keyof typeof PredefinedNetworkConditions
322+
];
323+
await page.emulateNetworkConditions(networkCondition);
324+
newSettings.networkConditions = options.networkConditions;
325+
}
291326
}
292-
this.#updateSelectedPageTimeouts();
293-
}
294327

295-
getNetworkConditions(): string | null {
296-
const page = this.getSelectedPage();
297-
return this.#networkConditionsMap.get(page) ?? null;
298-
}
328+
if (options.cpuThrottlingRate !== undefined) {
329+
timeoutsNeedUpdate = true;
330+
if (options.cpuThrottlingRate === null) {
331+
await page.emulateCPUThrottling(1);
332+
delete newSettings.cpuThrottlingRate;
333+
} else {
334+
await page.emulateCPUThrottling(options.cpuThrottlingRate);
335+
newSettings.cpuThrottlingRate = options.cpuThrottlingRate;
336+
}
337+
}
299338

300-
setCpuThrottlingRate(rate: number): void {
301-
const page = this.getSelectedPage();
302-
this.#cpuThrottlingRateMap.set(page, rate);
303-
this.#updateSelectedPageTimeouts();
304-
}
339+
if (options.geolocation !== undefined) {
340+
if (options.geolocation === null) {
341+
await page.setGeolocation({latitude: 0, longitude: 0});
342+
delete newSettings.geolocation;
343+
} else {
344+
await page.setGeolocation(options.geolocation);
345+
newSettings.geolocation = options.geolocation;
346+
}
347+
}
305348

306-
getCpuThrottlingRate(): number {
307-
const page = this.getSelectedPage();
308-
return this.#cpuThrottlingRateMap.get(page) ?? 1;
309-
}
349+
if (options.userAgent !== undefined) {
350+
if (options.userAgent === null) {
351+
await page.setUserAgent({userAgent: undefined});
352+
delete newSettings.userAgent;
353+
} else {
354+
await page.setUserAgent({userAgent: options.userAgent});
355+
newSettings.userAgent = options.userAgent;
356+
}
357+
}
310358

311-
setGeolocation(geolocation: GeolocationOptions | null): void {
312-
const page = this.getSelectedPage();
313-
if (geolocation === null) {
314-
this.#geolocationMap.delete(page);
359+
if (options.colorScheme !== undefined) {
360+
if (options.colorScheme === null || options.colorScheme === 'auto') {
361+
await page.emulateMediaFeatures([
362+
{name: 'prefers-color-scheme', value: ''},
363+
]);
364+
delete newSettings.colorScheme;
365+
} else {
366+
await page.emulateMediaFeatures([
367+
{name: 'prefers-color-scheme', value: options.colorScheme},
368+
]);
369+
newSettings.colorScheme = options.colorScheme;
370+
}
371+
}
372+
373+
if (options.viewport !== undefined) {
374+
if (options.viewport === null) {
375+
await page.setViewport(null);
376+
delete newSettings.viewport;
377+
} else {
378+
const defaults = {
379+
deviceScaleFactor: 1,
380+
isMobile: false,
381+
hasTouch: false,
382+
isLandscape: false,
383+
};
384+
const viewport = {...defaults, ...options.viewport};
385+
await page.setViewport(viewport);
386+
newSettings.viewport = viewport;
387+
}
388+
}
389+
390+
if (Object.keys(newSettings).length) {
391+
this.#emulationSettingsMap.set(page, newSettings);
315392
} else {
316-
this.#geolocationMap.set(page, geolocation);
393+
this.#emulationSettingsMap.delete(page);
317394
}
318-
}
319395

320-
getGeolocation(): GeolocationOptions | null {
321-
const page = this.getSelectedPage();
322-
return this.#geolocationMap.get(page) ?? null;
396+
if (timeoutsNeedUpdate) {
397+
this.#updateSelectedPageTimeouts();
398+
}
323399
}
324400

325-
setViewport(viewport: Viewport | null): void {
401+
getNetworkConditions(): string | null {
326402
const page = this.getSelectedPage();
327-
if (viewport === null) {
328-
this.#viewportMap.delete(page);
329-
} else {
330-
this.#viewportMap.set(page, viewport);
331-
}
403+
return this.#emulationSettingsMap.get(page)?.networkConditions ?? null;
332404
}
333405

334-
getViewport(): Viewport | null {
406+
getCpuThrottlingRate(): number {
335407
const page = this.getSelectedPage();
336-
return this.#viewportMap.get(page) ?? null;
408+
return this.#emulationSettingsMap.get(page)?.cpuThrottlingRate ?? 1;
337409
}
338410

339-
setUserAgent(userAgent: string | null): void {
411+
getGeolocation(): GeolocationOptions | null {
340412
const page = this.getSelectedPage();
341-
if (userAgent === null) {
342-
this.#userAgentMap.delete(page);
343-
} else {
344-
this.#userAgentMap.set(page, userAgent);
345-
}
413+
return this.#emulationSettingsMap.get(page)?.geolocation ?? null;
346414
}
347415

348-
getUserAgent(): string | null {
416+
getViewport(): Viewport | null {
349417
const page = this.getSelectedPage();
350-
return this.#userAgentMap.get(page) ?? null;
418+
return this.#emulationSettingsMap.get(page)?.viewport ?? null;
351419
}
352420

353-
setColorScheme(scheme: 'dark' | 'light' | null): void {
421+
getUserAgent(): string | null {
354422
const page = this.getSelectedPage();
355-
if (scheme === null) {
356-
this.#colorSchemeMap.delete(page);
357-
} else {
358-
this.#colorSchemeMap.set(page, scheme);
359-
}
423+
return this.#emulationSettingsMap.get(page)?.userAgent ?? null;
360424
}
361425

362426
getColorScheme(): 'dark' | 'light' | null {
363427
const page = this.getSelectedPage();
364-
return this.#colorSchemeMap.get(page) ?? null;
428+
return this.#emulationSettingsMap.get(page)?.colorScheme ?? null;
365429
}
366430

367431
setIsRunningPerformanceTrace(x: boolean): void {

src/tools/ToolDefinition.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,20 @@ export type Context = Readonly<{
116116
selectPage(page: Page): void;
117117
getElementByUid(uid: string): Promise<ElementHandle<Element>>;
118118
getAXNodeByUid(uid: string): TextSnapshotNode | undefined;
119-
setNetworkConditions(conditions: string | null): void;
120-
setCpuThrottlingRate(rate: number): void;
121-
setGeolocation(geolocation: GeolocationOptions | null): void;
122-
setViewport(viewport: Viewport | null): void;
119+
emulate(options: {
120+
networkConditions?: string | null;
121+
cpuThrottlingRate?: number | null;
122+
geolocation?: GeolocationOptions | null;
123+
userAgent?: string | null;
124+
colorScheme?: 'dark' | 'light' | 'auto' | null;
125+
viewport?: Viewport | null;
126+
}): Promise<void>;
127+
getNetworkConditions(): string | null;
128+
getCpuThrottlingRate(): number;
129+
getGeolocation(): GeolocationOptions | null;
123130
getViewport(): Viewport | null;
124-
setUserAgent(userAgent: string | null): void;
125131
getUserAgent(): string | null;
126-
setColorScheme(scheme: 'dark' | 'light' | null): void;
132+
getColorScheme(): 'dark' | 'light' | null;
127133
saveTemporaryFile(
128134
data: Uint8Array<ArrayBufferLike>,
129135
mimeType: 'image/png' | 'image/jpeg' | 'image/webp',

src/tools/emulation.ts

Lines changed: 1 addition & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -104,97 +104,6 @@ export const emulate = defineTool({
104104
),
105105
},
106106
handler: async (request, _response, context) => {
107-
const page = context.getSelectedPage();
108-
const {
109-
networkConditions,
110-
cpuThrottlingRate,
111-
geolocation,
112-
userAgent,
113-
viewport,
114-
} = request.params;
115-
116-
if (networkConditions) {
117-
if (networkConditions === 'No emulation') {
118-
await page.emulateNetworkConditions(null);
119-
context.setNetworkConditions(null);
120-
} else if (networkConditions === 'Offline') {
121-
await page.emulateNetworkConditions({
122-
offline: true,
123-
download: 0,
124-
upload: 0,
125-
latency: 0,
126-
});
127-
context.setNetworkConditions('Offline');
128-
} else if (networkConditions in PredefinedNetworkConditions) {
129-
const networkCondition =
130-
PredefinedNetworkConditions[
131-
networkConditions as keyof typeof PredefinedNetworkConditions
132-
];
133-
await page.emulateNetworkConditions(networkCondition);
134-
context.setNetworkConditions(networkConditions);
135-
}
136-
}
137-
138-
if (cpuThrottlingRate) {
139-
await page.emulateCPUThrottling(cpuThrottlingRate);
140-
context.setCpuThrottlingRate(cpuThrottlingRate);
141-
}
142-
143-
if (geolocation !== undefined) {
144-
if (geolocation === null) {
145-
await page.setGeolocation({latitude: 0, longitude: 0});
146-
context.setGeolocation(null);
147-
} else {
148-
await page.setGeolocation(geolocation);
149-
context.setGeolocation(geolocation);
150-
}
151-
}
152-
153-
if (userAgent !== undefined) {
154-
if (userAgent === null) {
155-
await page.setUserAgent({
156-
userAgent: undefined,
157-
});
158-
context.setUserAgent(null);
159-
} else {
160-
await page.setUserAgent({
161-
userAgent,
162-
});
163-
context.setUserAgent(userAgent);
164-
}
165-
}
166-
167-
if (request.params.colorScheme) {
168-
if (request.params.colorScheme === 'auto') {
169-
await page.emulateMediaFeatures([
170-
{name: 'prefers-color-scheme', value: ''},
171-
]);
172-
context.setColorScheme(null);
173-
} else {
174-
await page.emulateMediaFeatures([
175-
{
176-
name: 'prefers-color-scheme',
177-
value: request.params.colorScheme,
178-
},
179-
]);
180-
context.setColorScheme(request.params.colorScheme);
181-
}
182-
}
183-
184-
if (viewport !== undefined) {
185-
if (viewport === null) {
186-
await page.setViewport(null);
187-
context.setViewport(null);
188-
} else {
189-
const defaults = {
190-
deviceScaleFactor: 1,
191-
isMobile: false,
192-
hasTouch: false,
193-
isLandscape: false,
194-
};
195-
await page.setViewport({...defaults, ...viewport});
196-
context.setViewport({...defaults, ...viewport});
197-
}
198-
}
107+
await context.emulate(request.params);
199108
},
200109
});

0 commit comments

Comments
 (0)