@@ -22,7 +22,7 @@ import path from 'path';
2222import * as playwright from '../../..' ;
2323import { registryDirectory } from '../../server/registry/index' ;
2424import { startTraceViewerServer } from '../../server' ;
25- import { logUnhandledError , testDebug } from '../log' ;
25+ import { testDebug } from '../log' ;
2626import { outputDir , outputFile } from './config' ;
2727import { firstRootPath } from '../sdk/server' ;
2828
@@ -31,8 +31,6 @@ import type { LaunchOptions, BrowserContextOptions } from '../../client/types';
3131import type { ClientInfo } from '../sdk/server' ;
3232
3333export function contextFactory ( config : FullConfig ) : BrowserContextFactory {
34- if ( config . sharedBrowserContext )
35- return SharedContextFactory . create ( config ) ;
3634 if ( config . browser . remoteEndpoint )
3735 return new RemoteContextFactory ( config ) ;
3836 if ( config . browser . cdpEndpoint )
@@ -42,26 +40,19 @@ export function contextFactory(config: FullConfig): BrowserContextFactory {
4240 return new PersistentContextFactory ( config ) ;
4341}
4442
45- export type BrowserContextFactoryResult = {
46- browserContext : playwright . BrowserContext ;
47- close : ( ) => Promise < void > ;
48- } ;
49-
50- type CreateContextOptions = {
51- toolName ?: string ;
52- } ;
53-
5443export interface BrowserContextFactory {
55- createContext ( clientInfo : ClientInfo , abortSignal : AbortSignal , options : CreateContextOptions ) : Promise < BrowserContextFactoryResult > ;
44+ contexts ( clientInfo : ClientInfo ) : Promise < playwright . BrowserContext [ ] > ;
45+ createContext ( clientInfo : ClientInfo ) : Promise < playwright . BrowserContext > ;
5646}
5747
5848export function identityBrowserContextFactory ( browserContext : playwright . BrowserContext ) : BrowserContextFactory {
5949 return {
60- createContext : async ( clientInfo : ClientInfo , abortSignal : AbortSignal , options : CreateContextOptions ) => {
61- return {
62- browserContext,
63- close : async ( ) => { }
64- } ;
50+ contexts : async ( clientInfo : ClientInfo ) => {
51+ return [ browserContext ] ;
52+ } ,
53+
54+ createContext : async ( clientInfo : ClientInfo ) => {
55+ return browserContext ;
6556 }
6657 } ;
6758}
@@ -76,11 +67,11 @@ class BaseContextFactory implements BrowserContextFactory {
7667 this . config = config ;
7768 }
7869
79- protected async _obtainBrowser ( clientInfo : ClientInfo , options : CreateContextOptions ) : Promise < playwright . Browser > {
70+ protected async _obtainBrowser ( clientInfo : ClientInfo ) : Promise < playwright . Browser > {
8071 if ( this . _browserPromise )
8172 return this . _browserPromise ;
8273 testDebug ( `obtain browser (${ this . _logName } )` ) ;
83- this . _browserPromise = this . _doObtainBrowser ( clientInfo , options ) ;
74+ this . _browserPromise = this . _doObtainBrowser ( clientInfo ) ;
8475 void this . _browserPromise . then ( browser => {
8576 browser . on ( 'disconnected' , ( ) => {
8677 this . _browserPromise = undefined ;
@@ -91,43 +82,32 @@ class BaseContextFactory implements BrowserContextFactory {
9182 return this . _browserPromise ;
9283 }
9384
94- protected async _doObtainBrowser ( clientInfo : ClientInfo , options : CreateContextOptions ) : Promise < playwright . Browser > {
85+ protected async _doObtainBrowser ( clientInfo : ClientInfo ) : Promise < playwright . Browser > {
9586 throw new Error ( 'Not implemented' ) ;
9687 }
9788
98- async createContext ( clientInfo : ClientInfo , _ : AbortSignal , options : CreateContextOptions ) : Promise < BrowserContextFactoryResult > {
89+ async contexts ( clientInfo : ClientInfo ) : Promise < playwright . BrowserContext [ ] > {
90+ const browser = await this . _obtainBrowser ( clientInfo ) ;
91+ return browser . contexts ( ) ;
92+ }
93+
94+ async createContext ( clientInfo : ClientInfo ) : Promise < playwright . BrowserContext > {
9995 testDebug ( `create browser context (${ this . _logName } )` ) ;
100- const browser = await this . _obtainBrowser ( clientInfo , options ) ;
101- const browserContext = await this . _doCreateContext ( browser , clientInfo ) ;
102- await addInitScript ( browserContext , this . config . browser . initScript ) ;
103- return {
104- browserContext,
105- close : ( ) => this . _closeBrowserContext ( browserContext , browser )
106- } ;
96+ const browser = await this . _obtainBrowser ( clientInfo ) ;
97+ return await this . _doCreateContext ( browser , clientInfo ) ;
10798 }
10899
109100 protected async _doCreateContext ( browser : playwright . Browser , clientInfo : ClientInfo ) : Promise < playwright . BrowserContext > {
110101 throw new Error ( 'Not implemented' ) ;
111102 }
112-
113- private async _closeBrowserContext ( browserContext : playwright . BrowserContext , browser : playwright . Browser ) {
114- testDebug ( `close browser context (${ this . _logName } )` ) ;
115- if ( browser . contexts ( ) . length === 1 )
116- this . _browserPromise = undefined ;
117- await browserContext . close ( ) . catch ( logUnhandledError ) ;
118- if ( browser . contexts ( ) . length === 0 ) {
119- testDebug ( `close browser (${ this . _logName } )` ) ;
120- await browser . close ( ) . catch ( logUnhandledError ) ;
121- }
122- }
123103}
124104
125105class IsolatedContextFactory extends BaseContextFactory {
126106 constructor ( config : FullConfig ) {
127107 super ( 'isolated' , config ) ;
128108 }
129109
130- protected override async _doObtainBrowser ( clientInfo : ClientInfo , options : CreateContextOptions ) : Promise < playwright . Browser > {
110+ protected override async _doObtainBrowser ( clientInfo : ClientInfo ) : Promise < playwright . Browser > {
131111 await injectCdpPort ( this . config . browser ) ;
132112 const browserType = playwright [ this . config . browser . browserName ] ;
133113 const tracesDir = await computeTracesDir ( this . config , clientInfo ) ;
@@ -185,16 +165,15 @@ class RemoteContextFactory extends BaseContextFactory {
185165 }
186166}
187167
188- class PersistentContextFactory implements BrowserContextFactory {
189- readonly config : FullConfig ;
168+ class PersistentContextFactory extends BaseContextFactory {
190169 readonly name = 'persistent' ;
191170 readonly description = 'Create a new persistent browser context' ;
192171
193172 constructor ( config : FullConfig ) {
194- this . config = config ;
173+ super ( 'persistent' , config ) ;
195174 }
196175
197- async createContext ( clientInfo : ClientInfo , abortSignal : AbortSignal , options : CreateContextOptions ) : Promise < BrowserContextFactoryResult > {
176+ protected override async _doObtainBrowser ( clientInfo : ClientInfo ) : Promise < playwright . Browser > {
198177 await injectCdpPort ( this . config . browser ) ;
199178 testDebug ( 'create browser context (persistent)' ) ;
200179 const userDataDir = this . config . browser . userDataDir ?? await this . _createUserDataDir ( clientInfo ) ;
@@ -205,8 +184,6 @@ class PersistentContextFactory implements BrowserContextFactory {
205184 if ( await isProfileLocked5Times ( userDataDir ) )
206185 throw new Error ( `Browser is already in use for ${ userDataDir } , use --isolated to run multiple instances of the same browser` ) ;
207186
208- testDebug ( 'lock user data dir' , userDataDir ) ;
209-
210187 const browserType = playwright [ this . config . browser . browserName ] ;
211188 const launchOptions : LaunchOptions & BrowserContextOptions = {
212189 tracesDir,
@@ -221,9 +198,7 @@ class PersistentContextFactory implements BrowserContextFactory {
221198 } ;
222199 try {
223200 const browserContext = await browserType . launchPersistentContext ( userDataDir , launchOptions ) ;
224- await addInitScript ( browserContext , this . config . browser . initScript ) ;
225- const close = ( ) => this . _closeBrowserContext ( browserContext , userDataDir ) ;
226- return { browserContext, close } ;
201+ return browserContext . browser ( ) ! ;
227202 } catch ( error : any ) {
228203 if ( error . message . includes ( 'Executable doesn\'t exist' ) )
229204 throwBrowserIsNotInstalledError ( this . config ) ;
@@ -237,15 +212,6 @@ class PersistentContextFactory implements BrowserContextFactory {
237212 }
238213 }
239214
240- private async _closeBrowserContext ( browserContext : playwright . BrowserContext , userDataDir : string ) {
241- testDebug ( 'close browser context (persistent)' ) ;
242- testDebug ( 'release user data dir' , userDataDir ) ;
243- await browserContext . close ( ) . catch ( ( ) => { } ) ;
244- if ( process . env . PWMCP_PROFILES_DIR_FOR_TEST && userDataDir . startsWith ( process . env . PWMCP_PROFILES_DIR_FOR_TEST ) )
245- await fs . promises . rm ( userDataDir , { recursive : true } ) . catch ( logUnhandledError ) ;
246- testDebug ( 'close browser context complete (persistent)' ) ;
247- }
248-
249215 private async _createUserDataDir ( clientInfo : ClientInfo ) {
250216 const dir = process . env . PWMCP_PROFILES_DIR_FOR_TEST ?? registryDirectory ;
251217 const browserToken = this . config . browser . launchOptions ?. channel ?? this . config . browser ?. browserName ;
@@ -289,59 +255,6 @@ function createHash(data: string): string {
289255 return crypto . createHash ( 'sha256' ) . update ( data ) . digest ( 'hex' ) . slice ( 0 , 7 ) ;
290256}
291257
292- async function addInitScript ( browserContext : playwright . BrowserContext , initScript : string [ ] | undefined ) {
293- for ( const scriptPath of initScript ?? [ ] )
294- await browserContext . addInitScript ( { path : path . resolve ( scriptPath ) } ) ;
295- }
296-
297- export class SharedContextFactory implements BrowserContextFactory {
298- private _contextPromise : Promise < BrowserContextFactoryResult > | undefined ;
299- private _baseFactory : BrowserContextFactory ;
300- private static _instance : SharedContextFactory | undefined ;
301-
302- static create ( config : FullConfig ) {
303- if ( SharedContextFactory . _instance )
304- throw new Error ( 'SharedContextFactory already exists' ) ;
305- const baseConfig = { ...config , sharedBrowserContext : false } ;
306- const baseFactory = contextFactory ( baseConfig ) ;
307- SharedContextFactory . _instance = new SharedContextFactory ( baseFactory ) ;
308- return SharedContextFactory . _instance ;
309- }
310-
311- private constructor ( baseFactory : BrowserContextFactory ) {
312- this . _baseFactory = baseFactory ;
313- }
314-
315- async createContext ( clientInfo : ClientInfo , abortSignal : AbortSignal , options : { toolName ?: string } ) : Promise < { browserContext : playwright . BrowserContext , close : ( ) => Promise < void > } > {
316- if ( ! this . _contextPromise ) {
317- testDebug ( 'create shared browser context' ) ;
318- this . _contextPromise = this . _baseFactory . createContext ( clientInfo , abortSignal , options ) ;
319- }
320-
321- const { browserContext } = await this . _contextPromise ;
322- testDebug ( `shared context client connected` ) ;
323- return {
324- browserContext,
325- close : async ( ) => {
326- testDebug ( `shared context client disconnected` ) ;
327- } ,
328- } ;
329- }
330-
331- static async dispose ( ) {
332- await SharedContextFactory . _instance ?. _dispose ( ) ;
333- }
334-
335- private async _dispose ( ) {
336- const contextPromise = this . _contextPromise ;
337- this . _contextPromise = undefined ;
338- if ( ! contextPromise )
339- return ;
340- const { close } = await contextPromise ;
341- await close ( ) ;
342- }
343- }
344-
345258async function computeTracesDir ( config : FullConfig , clientInfo : ClientInfo ) : Promise < string | undefined > {
346259 return path . resolve ( outputDir ( config , clientInfo ) , 'traces' ) ;
347260}
0 commit comments