diff --git a/src/server/browser.ts b/src/server/browser.ts index ae56acdff2..30df25e6dd 100644 --- a/src/server/browser.ts +++ b/src/server/browser.ts @@ -21,6 +21,7 @@ import { EventEmitter } from 'events'; import { Download } from './download'; import { ProxySettings } from './types'; import { ChildProcess } from 'child_process'; +import { RecentLogsCollector } from '../utils/debugLogger'; export interface BrowserProcess { onclose: ((exitCode: number | null, signal: string | null) => void) | undefined; @@ -37,6 +38,7 @@ export type BrowserOptions = types.UIOptions & { browserProcess: BrowserProcess, proxy?: ProxySettings, protocolLogger: types.ProtocolLogger, + browserLogsCollector: RecentLogsCollector, }; export abstract class Browser extends EventEmitter { diff --git a/src/server/browserType.ts b/src/server/browserType.ts index a5610b4967..140f5b2734 100644 --- a/src/server/browserType.ts +++ b/src/server/browserType.ts @@ -30,6 +30,7 @@ import { TimeoutSettings } from '../utils/timeoutSettings'; import { validateHostRequirements } from './validateDependencies'; import { isDebugMode } from '../utils/utils'; import { helper } from './helper'; +import { RecentLogsCollector } from '../utils/debugLogger'; const mkdirAsync = util.promisify(fs.mkdir); const mkdtempAsync = util.promisify(fs.mkdtemp); @@ -81,7 +82,8 @@ export abstract class BrowserType { async _innerLaunch(progress: Progress, options: types.LaunchOptions, persistent: types.BrowserContextOptions | undefined, protocolLogger: types.ProtocolLogger, userDataDir?: string): Promise { options.proxy = options.proxy ? normalizeProxySettings(options.proxy) : undefined; - const { browserProcess, downloadsPath, transport } = await this._launchProcess(progress, options, !!persistent, userDataDir); + const browserLogsCollector = new RecentLogsCollector(); + const { browserProcess, downloadsPath, transport } = await this._launchProcess(progress, options, !!persistent, browserLogsCollector, userDataDir); if ((options as any).__testHookBeforeCreateBrowser) await (options as any).__testHookBeforeCreateBrowser(); const browserOptions: BrowserOptions = { @@ -93,6 +95,7 @@ export abstract class BrowserType { browserProcess, proxy: options.proxy, protocolLogger, + browserLogsCollector, }; if (persistent) validateBrowserContextOptions(persistent, browserOptions); @@ -104,7 +107,7 @@ export abstract class BrowserType { return browser; } - private async _launchProcess(progress: Progress, options: types.LaunchOptions, isPersistent: boolean, userDataDir?: string): Promise<{ browserProcess: BrowserProcess, downloadsPath: string, transport: ConnectionTransport }> { + private async _launchProcess(progress: Progress, options: types.LaunchOptions, isPersistent: boolean, browserLogsCollector: RecentLogsCollector, userDataDir?: string): Promise<{ browserProcess: BrowserProcess, downloadsPath: string, transport: ConnectionTransport }> { const { ignoreDefaultArgs, ignoreAllDefaultArgs, @@ -172,7 +175,10 @@ export abstract class BrowserType { handleSIGINT, handleSIGTERM, handleSIGHUP, - progress, + log: (message: string) => { + progress.log(message); + browserLogsCollector.log(message); + }, stdio: 'pipe', tempDirectories, attemptToGracefullyClose: async () => { diff --git a/src/server/chromium/crBrowser.ts b/src/server/chromium/crBrowser.ts index 59ae17dbb0..63ee58b57b 100644 --- a/src/server/chromium/crBrowser.ts +++ b/src/server/chromium/crBrowser.ts @@ -46,7 +46,7 @@ export class CRBrowser extends Browser { private _tracingClient: CRSession | undefined; static async connect(transport: ConnectionTransport, options: BrowserOptions, devtools?: CRDevTools): Promise { - const connection = new CRConnection(transport, options.protocolLogger); + const connection = new CRConnection(transport, options.protocolLogger, options.browserLogsCollector); const browser = new CRBrowser(connection, options); browser._devtools = devtools; const session = connection.rootSession; diff --git a/src/server/chromium/crConnection.ts b/src/server/chromium/crConnection.ts index 920dd373f3..117852abab 100644 --- a/src/server/chromium/crConnection.ts +++ b/src/server/chromium/crConnection.ts @@ -20,8 +20,9 @@ import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../trans import { Protocol } from './protocol'; import { EventEmitter } from 'events'; import { rewriteErrorMessage } from '../../utils/stackTrace'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger, RecentLogsCollector } from '../../utils/debugLogger'; import { ProtocolLogger } from '../types'; +import { helper } from '../helper'; export const ConnectionEvents = { Disconnected: Symbol('ConnectionEvents.Disconnected') @@ -36,13 +37,15 @@ export class CRConnection extends EventEmitter { private readonly _transport: ConnectionTransport; private readonly _sessions = new Map(); private readonly _protocolLogger: ProtocolLogger; + private readonly _browserLogsCollector: RecentLogsCollector; readonly rootSession: CRSession; _closed = false; - constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger) { + constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) { super(); this._transport = transport; this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; this._transport.onmessage = this._onMessage.bind(this); this._transport.onclose = this._onClose.bind(this); this.rootSession = new CRSession(this, '', 'browser', ''); @@ -79,7 +82,7 @@ export class CRConnection extends EventEmitter { } else if (message.method === 'Target.detachedFromTarget') { const session = this._sessions.get(message.params.sessionId); if (session) { - session._onClosed(); + session._onClosed(undefined); this._sessions.delete(message.params.sessionId); } } @@ -92,8 +95,9 @@ export class CRConnection extends EventEmitter { this._closed = true; this._transport.onmessage = undefined; this._transport.onclose = undefined; + const browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs()); for (const session of this._sessions.values()) - session._onClosed(); + session._onClosed(browserDisconnectedLogs); this._sessions.clear(); Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected)); } @@ -126,6 +130,7 @@ export class CRSession extends EventEmitter { private readonly _sessionId: string; private readonly _rootSessionId: string; private _crashed: boolean = false; + private _browserDisconnectedLogs: string | undefined; on: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; addListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; off: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; @@ -156,6 +161,8 @@ export class CRSession extends EventEmitter { ): Promise { if (this._crashed) throw new Error('Target crashed'); + if (this._browserDisconnectedLogs !== undefined) + throw new Error(`Protocol error (${method}): Browser closed.` + this._browserDisconnectedLogs); if (!this._connection) throw new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`); const id = this._connection._rawSend(this._sessionId, method, params); @@ -195,9 +202,11 @@ export class CRSession extends EventEmitter { await rootSession.send('Target.detachFromTarget', { sessionId: this._sessionId }); } - _onClosed() { + _onClosed(browserDisconnectedLogs: string | undefined) { + this._browserDisconnectedLogs = browserDisconnectedLogs; + const errorMessage = browserDisconnectedLogs !== undefined ? 'Browser closed.' + browserDisconnectedLogs : 'Target closed.'; for (const callback of this._callbacks.values()) - callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): Target closed.`)); + callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): ` + errorMessage)); this._callbacks.clear(); this._connection = null; Promise.resolve().then(() => this.emit(CRSessionEvents.Disconnected)); diff --git a/src/server/chromium/videoRecorder.ts b/src/server/chromium/videoRecorder.ts index acb991800e..9e45586148 100644 --- a/src/server/chromium/videoRecorder.ts +++ b/src/server/chromium/videoRecorder.ts @@ -65,7 +65,7 @@ export class VideoRecorder { executablePath, args, stdio: 'stdin', - progress, + log: (message: string) => progress.log(message), tempDirectories: [], attemptToGracefullyClose: async () => { progress.log('Closing stdin...'); diff --git a/src/server/clank/clank.ts b/src/server/clank/clank.ts index bbded2b2c4..5d81c75490 100644 --- a/src/server/clank/clank.ts +++ b/src/server/clank/clank.ts @@ -25,6 +25,7 @@ import { Env } from '../processLauncher'; import { CRBrowser } from '../chromium/crBrowser'; import { AndroidBrowser, AndroidClient, AndroidDevice } from './android'; import { AdbBackend } from './backendAdb'; +import { RecentLogsCollector } from '../../utils/debugLogger'; export class Clank extends BrowserType { async _innerLaunch(progress: Progress, options: types.LaunchOptions, persistent: types.BrowserContextOptions | undefined, protocolLogger: types.ProtocolLogger, userDataDir?: string): Promise { @@ -48,6 +49,7 @@ export class Clank extends BrowserType { browserProcess: new ClankBrowserProcess(device, adbBrowser), proxy: options.proxy, protocolLogger, + browserLogsCollector: new RecentLogsCollector(), }; if (persistent) validateBrowserContextOptions(persistent, browserOptions); diff --git a/src/server/electron/electron.ts b/src/server/electron/electron.ts index 0d8fe7de09..eb688f4f29 100644 --- a/src/server/electron/electron.ts +++ b/src/server/electron/electron.ts @@ -32,6 +32,7 @@ import { helper } from '../helper'; import { BrowserOptions, BrowserProcess } from '../browser'; import * as childProcess from 'child_process'; import * as readline from 'readline'; +import { RecentLogsCollector } from '../../utils/debugLogger'; export type ElectronLaunchOptionsBase = { args?: string[], @@ -157,6 +158,7 @@ export class Electron { electronArguments.push('--no-sandbox'); } + const browserLogsCollector = new RecentLogsCollector(); const { launchedProcess, gracefullyClose, kill } = await launchProcess({ executablePath, args: electronArguments, @@ -164,7 +166,10 @@ export class Electron { handleSIGINT, handleSIGTERM, handleSIGHUP, - progress, + log: (message: string) => { + progress.log(message); + browserLogsCollector.log(message); + }, stdio: 'pipe', cwd: options.cwd, tempDirectories: [], @@ -174,7 +179,7 @@ export class Electron { const nodeMatch = await waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/); const nodeTransport = await WebSocketTransport.connect(progress, nodeMatch[1]); - const nodeConnection = new CRConnection(nodeTransport, helper.debugProtocolLogger()); + const nodeConnection = new CRConnection(nodeTransport, helper.debugProtocolLogger(), browserLogsCollector); const chromeMatch = await waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/); const chromeTransport = await WebSocketTransport.connect(progress, chromeMatch[1]); @@ -190,6 +195,7 @@ export class Electron { persistent: { noDefaultViewport: true }, browserProcess, protocolLogger: helper.debugProtocolLogger(), + browserLogsCollector, }; const browser = await CRBrowser.connect(chromeTransport, browserOptions); app = new ElectronApplication(browser, nodeConnection); diff --git a/src/server/firefox/ffBrowser.ts b/src/server/firefox/ffBrowser.ts index bc93a5f270..e46b87835b 100644 --- a/src/server/firefox/ffBrowser.ts +++ b/src/server/firefox/ffBrowser.ts @@ -33,7 +33,7 @@ export class FFBrowser extends Browser { private _version = ''; static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise { - const connection = new FFConnection(transport, options.protocolLogger); + const connection = new FFConnection(transport, options.protocolLogger, options.browserLogsCollector); const browser = new FFBrowser(connection, options); const promises: Promise[] = [ connection.send('Browser.enable', { attachToDefaultContext: !!options.persistent }), diff --git a/src/server/firefox/ffConnection.ts b/src/server/firefox/ffConnection.ts index 5345d9261b..c65777e35c 100644 --- a/src/server/firefox/ffConnection.ts +++ b/src/server/firefox/ffConnection.ts @@ -20,8 +20,9 @@ import { assert } from '../../utils/utils'; import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import { Protocol } from './protocol'; import { rewriteErrorMessage } from '../../utils/stackTrace'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger, RecentLogsCollector } from '../../utils/debugLogger'; import { ProtocolLogger } from '../types'; +import { helper } from '../helper'; export const ConnectionEvents = { Disconnected: Symbol('Disconnected'), @@ -36,6 +37,7 @@ export class FFConnection extends EventEmitter { private _callbacks: Map; private _transport: ConnectionTransport; private readonly _protocolLogger: ProtocolLogger; + private readonly _browserLogsCollector: RecentLogsCollector; readonly _sessions: Map; _closed: boolean; @@ -45,10 +47,11 @@ export class FFConnection extends EventEmitter { removeListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; once: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; - constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger) { + constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) { super(); this._transport = transport; this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; this._lastId = 0; this._callbacks = new Map(); @@ -68,6 +71,7 @@ export class FFConnection extends EventEmitter { method: T, params?: Protocol.CommandParameters[T] ): Promise { + this._checkClosed(method); const id = this.nextMessageId(); this._rawSend({id, method, params}); return new Promise((resolve, reject) => { @@ -79,6 +83,11 @@ export class FFConnection extends EventEmitter { return ++this._lastId; } + _checkClosed(method: string) { + if (this._closed) + throw new Error(`Protocol error (${method}): Browser closed.` + helper.formatBrowserLogs(this._browserLogsCollector.recentLogs())); + } + _rawSend(message: ProtocolRequest) { this._protocolLogger('send', message); this._transport.send(message); @@ -111,12 +120,13 @@ export class FFConnection extends EventEmitter { this._closed = true; this._transport.onmessage = undefined; this._transport.onclose = undefined; - for (const callback of this._callbacks.values()) - callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): Target closed.`)); - this._callbacks.clear(); + const formattedBrowserLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs()); for (const session of this._sessions.values()) - session.dispose(); + session.dispose(formattedBrowserLogs); this._sessions.clear(); + for (const callback of this._callbacks.values()) + callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): Browser closed.` + formattedBrowserLogs)); + this._callbacks.clear(); Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected)); } @@ -175,6 +185,7 @@ export class FFSession extends EventEmitter { ): Promise { if (this._crashed) throw new Error('Page crashed'); + this._connection._checkClosed(method); if (this._disposed) throw new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`); const id = this._connection.nextMessageId(); @@ -202,9 +213,9 @@ export class FFSession extends EventEmitter { } } - dispose() { + dispose(formattedBrowserLogs?: string) { for (const callback of this._callbacks.values()) - callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): Target closed.`)); + callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): Target closed.` + formattedBrowserLogs)); this._callbacks.clear(); this._disposed = true; this._connection._sessions.delete(this._sessionId); diff --git a/src/server/helper.ts b/src/server/helper.ts index b595b130a3..c34e147724 100644 --- a/src/server/helper.ts +++ b/src/server/helper.ts @@ -120,6 +120,12 @@ class Helper { debugLogger.log('protocol', (direction === 'send' ? 'SEND ► ' : '◀ RECV ') + JSON.stringify(message)); }; } + + static formatBrowserLogs(logs: string[]) { + if (!logs.length) + return ''; + return '\n' + '='.repeat(20) + ' Browser output: ' + '='.repeat(20) + '\n' + logs.join('\n'); + } } export const helper = Helper; diff --git a/src/server/processLauncher.ts b/src/server/processLauncher.ts index 1b8b3a0ed9..b95845ef45 100644 --- a/src/server/processLauncher.ts +++ b/src/server/processLauncher.ts @@ -19,7 +19,6 @@ import * as childProcess from 'child_process'; import * as readline from 'readline'; import * as removeFolder from 'rimraf'; import { helper } from './helper'; -import { Progress } from './progress'; import * as types from './types'; import { isUnderTest } from '../utils/utils'; @@ -41,7 +40,7 @@ export type LaunchProcessOptions = { // Note: attemptToGracefullyClose should reject if it does not close the browser. attemptToGracefullyClose: () => Promise, onExit: (exitCode: number | null, signal: string | null) => void, - progress: Progress, + log: (message: string) => void, }; type LaunchResult = { @@ -65,9 +64,8 @@ if (maxListeners !== 0) export async function launchProcess(options: LaunchProcessOptions): Promise { const cleanup = () => helper.removeFolders(options.tempDirectories); - const progress = options.progress; const stdio: ('ignore' | 'pipe')[] = options.stdio === 'pipe' ? ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe']; - progress.log(` ${options.executablePath} ${options.args.join(' ')}`); + options.log(` ${options.executablePath} ${options.args.join(' ')}`); const spawnedProcess = childProcess.spawn( options.executablePath, options.args, @@ -93,16 +91,16 @@ export async function launchProcess(options: LaunchProcessOptions): Promise failedPromise).then(e => Promise.reject(e)); } - progress.log(` pid=${spawnedProcess.pid}`); + options.log(` pid=${spawnedProcess.pid}`); const stdout = readline.createInterface({ input: spawnedProcess.stdout }); stdout.on('line', (data: string) => { - progress.log('[out] ' + data); + options.log('[out] ' + data); }); const stderr = readline.createInterface({ input: spawnedProcess.stderr }); stderr.on('line', (data: string) => { - progress.log('[err] ' + data); + options.log('[err] ' + data); }); let processClosed = false; @@ -111,7 +109,7 @@ export async function launchProcess(options: LaunchProcessOptions): Promise {}; const waitForCleanup = new Promise(f => fulfillCleanup = f); spawnedProcess.once('exit', (exitCode, signal) => { - progress.log(``); + options.log(``); processClosed = true; helper.removeEventListeners(listeners); gracefullyCloseSet.delete(gracefullyClose); @@ -147,21 +145,21 @@ export async function launchProcess(options: LaunchProcessOptions): Promise`); + options.log(``); killProcess(); await waitForClose; // Ensure the process is dead and we called options.onkill. return; } gracefullyClosing = true; - progress.log(``); + options.log(``); await options.attemptToGracefullyClose().catch(() => killProcess()); await waitForCleanup; // Ensure the process is dead and we have cleaned up. - progress.log(``); + options.log(``); } // This method has to be sync to be used as 'exit' event handler. function killProcess() { - progress.log(``); + options.log(``); helper.removeEventListeners(listeners); if (spawnedProcess.pid && !spawnedProcess.killed && !processClosed) { // Force kill the browser. diff --git a/src/server/webkit/wkBrowser.ts b/src/server/webkit/wkBrowser.ts index c2912dc215..c120047c8d 100644 --- a/src/server/webkit/wkBrowser.ts +++ b/src/server/webkit/wkBrowser.ts @@ -52,7 +52,7 @@ export class WKBrowser extends Browser { constructor(transport: ConnectionTransport, options: BrowserOptions) { super(options); - this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options.protocolLogger); + this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector); this._browserSession = this._connection.browserSession; this._eventListeners = [ helper.addEventListener(this._browserSession, 'Playwright.pageProxyCreated', this._onPageProxyCreated.bind(this)), @@ -69,7 +69,7 @@ export class WKBrowser extends Browser { _onDisconnect() { for (const wkPage of this._wkPages.values()) - wkPage.dispose(); + wkPage.dispose(true); this._didClose(); } @@ -162,7 +162,7 @@ export class WKBrowser extends Browser { if (!wkPage) return; wkPage.didClose(); - wkPage.dispose(); + wkPage.dispose(false); this._wkPages.delete(pageProxyId); } diff --git a/src/server/webkit/wkConnection.ts b/src/server/webkit/wkConnection.ts index a63eef97de..ba90a288cd 100644 --- a/src/server/webkit/wkConnection.ts +++ b/src/server/webkit/wkConnection.ts @@ -20,8 +20,9 @@ import { assert } from '../../utils/utils'; import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; import { Protocol } from './protocol'; import { rewriteErrorMessage } from '../../utils/stackTrace'; -import { debugLogger } from '../../utils/debugLogger'; +import { debugLogger, RecentLogsCollector } from '../../utils/debugLogger'; import { ProtocolLogger } from '../types'; +import { helper } from '../helper'; // WKPlaywright uses this special id to issue Browser.close command which we // should ignore. @@ -36,16 +37,18 @@ export class WKConnection { private readonly _transport: ConnectionTransport; private readonly _onDisconnect: () => void; private readonly _protocolLogger: ProtocolLogger; + readonly _browserLogsCollector: RecentLogsCollector; private _lastId = 0; private _closed = false; readonly browserSession: WKSession; - constructor(transport: ConnectionTransport, onDisconnect: () => void, protocolLogger: ProtocolLogger) { + constructor(transport: ConnectionTransport, onDisconnect: () => void, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) { this._transport = transport; this._transport.onmessage = this._dispatchMessage.bind(this); this._transport.onclose = this._onClose.bind(this); this._onDisconnect = onDisconnect; this._protocolLogger = protocolLogger; + this._browserLogsCollector = browserLogsCollector; this.browserSession = new WKSession(this, '', 'Browser has been closed.', (message: any) => { this.rawSend(message); }); @@ -76,7 +79,7 @@ export class WKConnection { this._closed = true; this._transport.onmessage = undefined; this._transport.onclose = undefined; - this.browserSession.dispose(); + this.browserSession.dispose(true); this._onDisconnect(); } @@ -148,7 +151,9 @@ export class WKSession extends EventEmitter { return this._disposed; } - dispose() { + dispose(disconnected: boolean) { + if (disconnected) + this.errorText = 'Browser closed.' + helper.formatBrowserLogs(this.connection._browserLogsCollector.recentLogs()); for (const callback of this._callbacks.values()) callback.reject(rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): ${this.errorText}`)); this._callbacks.clear(); diff --git a/src/server/webkit/wkPage.ts b/src/server/webkit/wkPage.ts index 7306497093..a119c84d5f 100644 --- a/src/server/webkit/wkPage.ts +++ b/src/server/webkit/wkPage.ts @@ -215,11 +215,11 @@ export class WKPage implements PageDelegate { private _onTargetDestroyed(event: Protocol.Target.targetDestroyedPayload) { const { targetId, crashed } = event; if (this._provisionalPage && this._provisionalPage._session.sessionId === targetId) { - this._provisionalPage._session.dispose(); + this._provisionalPage._session.dispose(false); this._provisionalPage.dispose(); this._provisionalPage = null; } else if (this._session.sessionId === targetId) { - this._session.dispose(); + this._session.dispose(false); helper.removeEventListeners(this._sessionListeners); if (crashed) { this._session.markAsCrashed(); @@ -232,14 +232,14 @@ export class WKPage implements PageDelegate { this._page._didClose(); } - dispose() { - this._pageProxySession.dispose(); + dispose(disconnected: boolean) { + this._pageProxySession.dispose(disconnected); helper.removeEventListeners(this._sessionListeners); helper.removeEventListeners(this._eventListeners); if (this._session) - this._session.dispose(); + this._session.dispose(disconnected); if (this._provisionalPage) { - this._provisionalPage._session.dispose(); + this._provisionalPage._session.dispose(disconnected); this._provisionalPage.dispose(); this._provisionalPage = null; } diff --git a/src/server/webkit/wkWorkers.ts b/src/server/webkit/wkWorkers.ts index f1041abcee..4208b8872c 100644 --- a/src/server/webkit/wkWorkers.ts +++ b/src/server/webkit/wkWorkers.ts @@ -67,7 +67,7 @@ export class WKWorkers { const workerSession = this._workerSessions.get(event.workerId)!; if (!workerSession) return; - workerSession.dispose(); + workerSession.dispose(false); this._workerSessions.delete(event.workerId); this._page._removeWorker(event.workerId); }) diff --git a/src/utils/debugLogger.ts b/src/utils/debugLogger.ts index cbdf95dd0d..2579df63d1 100644 --- a/src/utils/debugLogger.ts +++ b/src/utils/debugLogger.ts @@ -61,3 +61,20 @@ class DebugLogger { } export const debugLogger = new DebugLogger(); + +const kLogCount = 50; +export class RecentLogsCollector { + private _logs: string[] = []; + + log(message: string) { + this._logs.push(message); + if (this._logs.length === kLogCount * 2) + this._logs.splice(0, kLogCount); + } + + recentLogs(): string[] { + if (this._logs.length > kLogCount) + return this._logs.slice(-kLogCount); + return this._logs; + } +}