@@ -27,9 +27,9 @@ import type {
2727 HTTPRequest ,
2828 Page ,
2929 SerializedAXNode ,
30- PredefinedNetworkConditions ,
3130 Viewport ,
3231} from './third_party/index.js' ;
32+ import { PredefinedNetworkConditions } from './third_party/index.js' ;
3333import { listPages } from './tools/pages.js' ;
3434import { takeSnapshot } from './tools/snapshot.js' ;
3535import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js' ;
@@ -282,83 +282,127 @@ export class McpContext implements Context {
282282 return this . #networkCollector. getById ( this . getSelectedPage ( ) , reqid ) ;
283283 }
284284
285- setNetworkConditions ( conditions : string | null ) : void {
285+ async emulate ( options : {
286+ networkConditions ?: string | null ;
287+ cpuThrottlingRate ?: number | null ;
288+ geolocation ?: GeolocationOptions | null ;
289+ userAgent ?: string | null ;
290+ colorScheme ?: 'dark' | 'light' | 'auto' | null ;
291+ viewport ?: Viewport | null ;
292+ } ) : Promise < void > {
286293 const page = this . getSelectedPage ( ) ;
287- if ( conditions === null ) {
288- this . #networkConditionsMap. delete ( page ) ;
289- } else {
290- this . #networkConditionsMap. set ( page , conditions ) ;
294+ let timeoutsNeedUpdate = false ;
295+
296+ if ( options . networkConditions !== undefined ) {
297+ if ( options . networkConditions === null || options . networkConditions === 'No emulation' ) {
298+ await page . emulateNetworkConditions ( null ) ;
299+ this . #networkConditionsMap. delete ( page ) ;
300+ } else if ( options . networkConditions === 'Offline' ) {
301+ await page . emulateNetworkConditions ( {
302+ offline : true ,
303+ download : 0 ,
304+ upload : 0 ,
305+ latency : 0 ,
306+ } ) ;
307+ this . #networkConditionsMap. set ( page , 'Offline' ) ;
308+ } else if ( options . networkConditions in PredefinedNetworkConditions ) {
309+ const networkCondition =
310+ PredefinedNetworkConditions [
311+ options . networkConditions as keyof typeof PredefinedNetworkConditions
312+ ] ;
313+ await page . emulateNetworkConditions ( networkCondition ) ;
314+ this . #networkConditionsMap. set ( page , options . networkConditions ) ;
315+ }
316+ timeoutsNeedUpdate = true ;
317+ }
318+
319+ if ( options . cpuThrottlingRate !== undefined ) {
320+ if ( options . cpuThrottlingRate === null ) {
321+ await page . emulateCPUThrottling ( 1 ) ;
322+ this . #cpuThrottlingRateMap. delete ( page ) ;
323+ } else {
324+ await page . emulateCPUThrottling ( options . cpuThrottlingRate ) ;
325+ this . #cpuThrottlingRateMap. set ( page , options . cpuThrottlingRate ) ;
326+ }
327+ timeoutsNeedUpdate = true ;
328+ }
329+
330+ if ( options . geolocation !== undefined ) {
331+ if ( options . geolocation === null ) {
332+ await page . setGeolocation ( { latitude : 0 , longitude : 0 } ) ;
333+ this . #geolocationMap. delete ( page ) ;
334+ } else {
335+ await page . setGeolocation ( options . geolocation ) ;
336+ this . #geolocationMap. set ( page , options . geolocation ) ;
337+ }
338+ }
339+
340+ if ( options . userAgent !== undefined ) {
341+ if ( options . userAgent === null ) {
342+ await page . setUserAgent ( { userAgent : undefined } ) ;
343+ this . #userAgentMap. delete ( page ) ;
344+ } else {
345+ await page . setUserAgent ( { userAgent : options . userAgent } ) ;
346+ this . #userAgentMap. set ( page , options . userAgent ) ;
347+ }
348+ }
349+
350+ if ( options . colorScheme !== undefined ) {
351+ if ( options . colorScheme === null || options . colorScheme === 'auto' ) {
352+ await page . emulateMediaFeatures ( [ { name : 'prefers-color-scheme' , value : '' } ] ) ;
353+ this . #colorSchemeMap. delete ( page ) ;
354+ } else {
355+ await page . emulateMediaFeatures ( [ { name : 'prefers-color-scheme' , value : options . colorScheme } ] ) ;
356+ this . #colorSchemeMap. set ( page , options . colorScheme ) ;
357+ }
358+ }
359+
360+ if ( options . viewport !== undefined ) {
361+ if ( options . viewport === null ) {
362+ await page . setViewport ( null ) ;
363+ this . #viewportMap. delete ( page ) ;
364+ } else {
365+ const defaults = {
366+ deviceScaleFactor : 1 ,
367+ isMobile : false ,
368+ hasTouch : false ,
369+ isLandscape : false ,
370+ } ;
371+ await page . setViewport ( { ...defaults , ...options . viewport } ) ;
372+ this . #viewportMap. set ( page , { ...defaults , ...options . viewport } ) ;
373+ }
374+ }
375+
376+ if ( timeoutsNeedUpdate ) {
377+ this . #updateSelectedPageTimeouts( ) ;
291378 }
292- this . #updateSelectedPageTimeouts( ) ;
293379 }
294380
295381 getNetworkConditions ( ) : string | null {
296382 const page = this . getSelectedPage ( ) ;
297383 return this . #networkConditionsMap. get ( page ) ?? null ;
298384 }
299385
300- setCpuThrottlingRate ( rate : number ) : void {
301- const page = this . getSelectedPage ( ) ;
302- this . #cpuThrottlingRateMap. set ( page , rate ) ;
303- this . #updateSelectedPageTimeouts( ) ;
304- }
305-
306386 getCpuThrottlingRate ( ) : number {
307387 const page = this . getSelectedPage ( ) ;
308388 return this . #cpuThrottlingRateMap. get ( page ) ?? 1 ;
309389 }
310390
311- setGeolocation ( geolocation : GeolocationOptions | null ) : void {
312- const page = this . getSelectedPage ( ) ;
313- if ( geolocation === null ) {
314- this . #geolocationMap. delete ( page ) ;
315- } else {
316- this . #geolocationMap. set ( page , geolocation ) ;
317- }
318- }
319-
320391 getGeolocation ( ) : GeolocationOptions | null {
321392 const page = this . getSelectedPage ( ) ;
322393 return this . #geolocationMap. get ( page ) ?? null ;
323394 }
324395
325- setViewport ( viewport : Viewport | null ) : void {
326- const page = this . getSelectedPage ( ) ;
327- if ( viewport === null ) {
328- this . #viewportMap. delete ( page ) ;
329- } else {
330- this . #viewportMap. set ( page , viewport ) ;
331- }
332- }
333-
334396 getViewport ( ) : Viewport | null {
335397 const page = this . getSelectedPage ( ) ;
336398 return this . #viewportMap. get ( page ) ?? null ;
337399 }
338400
339- setUserAgent ( userAgent : string | null ) : void {
340- const page = this . getSelectedPage ( ) ;
341- if ( userAgent === null ) {
342- this . #userAgentMap. delete ( page ) ;
343- } else {
344- this . #userAgentMap. set ( page , userAgent ) ;
345- }
346- }
347-
348401 getUserAgent ( ) : string | null {
349402 const page = this . getSelectedPage ( ) ;
350403 return this . #userAgentMap. get ( page ) ?? null ;
351404 }
352405
353- setColorScheme ( scheme : 'dark' | 'light' | null ) : void {
354- const page = this . getSelectedPage ( ) ;
355- if ( scheme === null ) {
356- this . #colorSchemeMap. delete ( page ) ;
357- } else {
358- this . #colorSchemeMap. set ( page , scheme ) ;
359- }
360- }
361-
362406 getColorScheme ( ) : 'dark' | 'light' | null {
363407 const page = this . getSelectedPage ( ) ;
364408 return this . #colorSchemeMap. get ( page ) ?? null ;
0 commit comments