From a9523d9d8f818914975a5da1edf74aeabdd0c251 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Sat, 8 May 2021 11:35:36 -0700 Subject: [PATCH] feat(ff): roll to 1256/1246 (#6466) --- browsers.json | 4 +-- src/server/firefox/ffBrowser.ts | 6 ++-- src/server/firefox/ffPage.ts | 27 +++++++++++++++-- src/server/firefox/protocol.ts | 42 +++++++++++++++++++++----- src/server/page.ts | 2 +- src/server/snapshot/snapshotTypes.ts | 1 - src/server/snapshot/snapshotter.ts | 9 ++---- src/server/trace/common/traceEvents.ts | 1 - src/server/trace/recorder/tracing.ts | 1 - src/server/webkit/wkPage.ts | 28 ++++++++++++++--- tests/screencast.spec.ts | 2 -- tests/tracing.spec.ts | 3 +- 12 files changed, 90 insertions(+), 36 deletions(-) diff --git a/browsers.json b/browsers.json index 8eecd10184..3d56552591 100644 --- a/browsers.json +++ b/browsers.json @@ -8,12 +8,12 @@ }, { "name": "firefox", - "revision": "1254", + "revision": "1256", "installByDefault": true }, { "name": "firefox-stable", - "revision": "1245", + "revision": "1246", "installByDefault": false }, { diff --git a/src/server/firefox/ffBrowser.ts b/src/server/firefox/ffBrowser.ts index 7c1baaaa74..372d3adaaf 100644 --- a/src/server/firefox/ffBrowser.ts +++ b/src/server/firefox/ffBrowser.ts @@ -61,7 +61,7 @@ export class FFBrowser extends Browser { this._connection.on('Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this)); this._connection.on('Browser.downloadCreated', this._onDownloadCreated.bind(this)); this._connection.on('Browser.downloadFinished', this._onDownloadFinished.bind(this)); - this._connection.on('Browser.screencastFinished', this._onScreencastFinished.bind(this)); + this._connection.on('Browser.videoRecordingFinished', this._onVideoRecordingFinished.bind(this)); } async _initVersion() { @@ -133,7 +133,7 @@ export class FFBrowser extends Browser { this._downloadFinished(payload.uuid, error); } - _onScreencastFinished(payload: Protocol.Browser.screencastFinishedPayload) { + _onVideoRecordingFinished(payload: Protocol.Browser.videoRecordingFinishedPayload) { this._takeVideo(payload.screencastId)?.reportFinished(); } } @@ -194,7 +194,7 @@ export class FFBrowserContext extends BrowserContext { promises.push(this._browser._connection.send('Browser.setColorScheme', { browserContextId, colorScheme: this._options.colorScheme })); if (this._options.recordVideo) { promises.push(this._ensureVideosPath().then(() => { - return this._browser._connection.send('Browser.setScreencastOptions', { + return this._browser._connection.send('Browser.setVideoRecordingOptions', { // validateBrowserContextOptions ensures correct video size. ...this._options.recordVideo!.size!, dir: this._options.recordVideo!.dir, diff --git a/src/server/firefox/ffPage.ts b/src/server/firefox/ffPage.ts index 1f69fce1c0..d917be0dfc 100644 --- a/src/server/firefox/ffPage.ts +++ b/src/server/firefox/ffPage.ts @@ -31,6 +31,7 @@ import { FFNetworkManager } from './ffNetworkManager'; import { Protocol } from './protocol'; import { Progress } from '../progress'; import { splitErrorMessage } from '../../utils/stackTrace'; +import { debugLogger } from '../../utils/debugLogger'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; @@ -51,6 +52,7 @@ export class FFPage implements PageDelegate { private readonly _contextIdToContext: Map; private _eventListeners: RegisteredListener[]; private _workers = new Map(); + private _screencastId: string | undefined; constructor(session: FFSession, browserContext: FFBrowserContext, opener: FFPage | null) { this._session = session; @@ -84,12 +86,14 @@ export class FFPage implements PageDelegate { helper.addEventListener(this._session, 'Page.workerDestroyed', this._onWorkerDestroyed.bind(this)), helper.addEventListener(this._session, 'Page.dispatchMessageFromWorker', this._onDispatchMessageFromWorker.bind(this)), helper.addEventListener(this._session, 'Page.crashed', this._onCrashed.bind(this)), - helper.addEventListener(this._session, 'Page.screencastStarted', this._onScreencastStarted.bind(this)), + helper.addEventListener(this._session, 'Page.videoRecordingStarted', this._onVideoRecordingStarted.bind(this)), helper.addEventListener(this._session, 'Page.webSocketCreated', this._onWebSocketCreated.bind(this)), helper.addEventListener(this._session, 'Page.webSocketClosed', this._onWebSocketClosed.bind(this)), helper.addEventListener(this._session, 'Page.webSocketFrameReceived', this._onWebSocketFrameReceived.bind(this)), helper.addEventListener(this._session, 'Page.webSocketFrameSent', this._onWebSocketFrameSent.bind(this)), + helper.addEventListener(this._session, 'Page.screencastFrame', this._onScreencastFrame.bind(this)), + ]; this._pagePromise = new Promise(f => this._pageCallback = f); session.once(FFSessionEvents.Disconnected, () => { @@ -308,7 +312,7 @@ export class FFPage implements PageDelegate { this._page._didCrash(); } - _onScreencastStarted(event: Protocol.Page.screencastStartedPayload) { + _onVideoRecordingStarted(event: Protocol.Page.videoRecordingStartedPayload) { this._browserContext._browser._videoStarted(this._browserContext, event.screencastId, event.file, this.pageOrError()); } @@ -476,7 +480,24 @@ export class FFPage implements PageDelegate { } async setScreencastEnabled(enabled: boolean): Promise { - throw new Error('Not implemented'); + if (enabled) { + const { screencastId } = await this._session.send('Page.startScreencast', { width: 800, height: 600, quality: 70 }); + this._screencastId = screencastId; + } else { + await this._session.send('Page.stopScreencast'); + } + } + + private _onScreencastFrame(event: Protocol.Page.screencastFramePayload) { + if (!this._screencastId) + return; + const buffer = Buffer.from(event.data, 'base64'); + this._page.emit(Page.Events.ScreencastFrame, { + buffer, + width: event.deviceWidth, + height: event.deviceHeight, + }); + this._session.send('Page.screencastFrameAck', { screencastId: this._screencastId }).catch(e => debugLogger.log('error', e)); } rafCountForStablePosition(): number { diff --git a/src/server/firefox/protocol.ts b/src/server/firefox/protocol.ts index f6c2c99c38..d8d5a2b9dd 100644 --- a/src/server/firefox/protocol.ts +++ b/src/server/firefox/protocol.ts @@ -65,7 +65,7 @@ export module Protocol { canceled?: boolean; error?: string; } - export type screencastFinishedPayload = { + export type videoRecordingFinishedPayload = { screencastId: string; } export type enableParameters = { @@ -265,13 +265,13 @@ export module Protocol { colorScheme: ("dark"|"light"|"no-preference")|null; }; export type setColorSchemeReturnValue = void; - export type setScreencastOptionsParameters = { + export type setVideoRecordingOptionsParameters = { browserContextId?: string; dir: string; width: number; height: number; }; - export type setScreencastOptionsReturnValue = void; + export type setVideoRecordingOptionsReturnValue = void; } export module Page { export type DOMPoint = { @@ -408,7 +408,7 @@ export module Protocol { workerId: string; message: string; } - export type screencastStartedPayload = { + export type videoRecordingStartedPayload = { screencastId: string; file: string; } @@ -440,6 +440,11 @@ export module Protocol { opcode: number; data: string; } + export type screencastFramePayload = { + data: string; + deviceWidth: number; + deviceHeight: number; + } export type closeParameters = { runBeforeUnload?: boolean; }; @@ -644,6 +649,20 @@ export module Protocol { message: string; }; export type sendMessageToWorkerReturnValue = void; + export type startScreencastParameters = { + width: number; + height: number; + quality: number; + }; + export type startScreencastReturnValue = { + screencastId: string; + }; + export type screencastFrameAckParameters = { + screencastId: string; + }; + export type screencastFrameAckReturnValue = void; + export type stopScreencastParameters = void; + export type stopScreencastReturnValue = void; } export module Runtime { export type RemoteObject = { @@ -965,7 +984,7 @@ export module Protocol { "Browser.detachedFromTarget": Browser.detachedFromTargetPayload; "Browser.downloadCreated": Browser.downloadCreatedPayload; "Browser.downloadFinished": Browser.downloadFinishedPayload; - "Browser.screencastFinished": Browser.screencastFinishedPayload; + "Browser.videoRecordingFinished": Browser.videoRecordingFinishedPayload; "Page.ready": Page.readyPayload; "Page.crashed": Page.crashedPayload; "Page.eventFired": Page.eventFiredPayload; @@ -985,12 +1004,13 @@ export module Protocol { "Page.workerCreated": Page.workerCreatedPayload; "Page.workerDestroyed": Page.workerDestroyedPayload; "Page.dispatchMessageFromWorker": Page.dispatchMessageFromWorkerPayload; - "Page.screencastStarted": Page.screencastStartedPayload; + "Page.videoRecordingStarted": Page.videoRecordingStartedPayload; "Page.webSocketCreated": Page.webSocketCreatedPayload; "Page.webSocketOpened": Page.webSocketOpenedPayload; "Page.webSocketClosed": Page.webSocketClosedPayload; "Page.webSocketFrameSent": Page.webSocketFrameSentPayload; "Page.webSocketFrameReceived": Page.webSocketFrameReceivedPayload; + "Page.screencastFrame": Page.screencastFramePayload; "Runtime.executionContextCreated": Runtime.executionContextCreatedPayload; "Runtime.executionContextDestroyed": Runtime.executionContextDestroyedPayload; "Runtime.console": Runtime.consolePayload; @@ -1030,7 +1050,7 @@ export module Protocol { "Browser.getCookies": Browser.getCookiesParameters; "Browser.setOnlineOverride": Browser.setOnlineOverrideParameters; "Browser.setColorScheme": Browser.setColorSchemeParameters; - "Browser.setScreencastOptions": Browser.setScreencastOptionsParameters; + "Browser.setVideoRecordingOptions": Browser.setVideoRecordingOptionsParameters; "Page.close": Page.closeParameters; "Page.setFileInputFiles": Page.setFileInputFilesParameters; "Page.addBinding": Page.addBindingParameters; @@ -1058,6 +1078,9 @@ export module Protocol { "Page.handleDialog": Page.handleDialogParameters; "Page.setInterceptFileChooserDialog": Page.setInterceptFileChooserDialogParameters; "Page.sendMessageToWorker": Page.sendMessageToWorkerParameters; + "Page.startScreencast": Page.startScreencastParameters; + "Page.screencastFrameAck": Page.screencastFrameAckParameters; + "Page.stopScreencast": Page.stopScreencastParameters; "Runtime.evaluate": Runtime.evaluateParameters; "Runtime.callFunction": Runtime.callFunctionParameters; "Runtime.disposeObject": Runtime.disposeObjectParameters; @@ -1101,7 +1124,7 @@ export module Protocol { "Browser.getCookies": Browser.getCookiesReturnValue; "Browser.setOnlineOverride": Browser.setOnlineOverrideReturnValue; "Browser.setColorScheme": Browser.setColorSchemeReturnValue; - "Browser.setScreencastOptions": Browser.setScreencastOptionsReturnValue; + "Browser.setVideoRecordingOptions": Browser.setVideoRecordingOptionsReturnValue; "Page.close": Page.closeReturnValue; "Page.setFileInputFiles": Page.setFileInputFilesReturnValue; "Page.addBinding": Page.addBindingReturnValue; @@ -1129,6 +1152,9 @@ export module Protocol { "Page.handleDialog": Page.handleDialogReturnValue; "Page.setInterceptFileChooserDialog": Page.setInterceptFileChooserDialogReturnValue; "Page.sendMessageToWorker": Page.sendMessageToWorkerReturnValue; + "Page.startScreencast": Page.startScreencastReturnValue; + "Page.screencastFrameAck": Page.screencastFrameAckReturnValue; + "Page.stopScreencast": Page.stopScreencastReturnValue; "Runtime.evaluate": Runtime.evaluateReturnValue; "Runtime.callFunction": Runtime.callFunctionReturnValue; "Runtime.disposeObject": Runtime.disposeObjectReturnValue; diff --git a/src/server/page.ts b/src/server/page.ts index fef96e2021..a658a7ca17 100644 --- a/src/server/page.ts +++ b/src/server/page.ts @@ -502,7 +502,7 @@ export class Page extends SdkObject { } setScreencastEnabled(enabled: boolean) { - this._delegate.setScreencastEnabled(enabled).catch(() => {}); + this._delegate.setScreencastEnabled(enabled).catch(e => debugLogger.log('error', e)); } } diff --git a/src/server/snapshot/snapshotTypes.ts b/src/server/snapshot/snapshotTypes.ts index 9abeac0469..e31cde8051 100644 --- a/src/server/snapshot/snapshotTypes.ts +++ b/src/server/snapshot/snapshotTypes.ts @@ -54,7 +54,6 @@ export type FrameSnapshot = { frameId: string, frameUrl: string, timestamp: number, - pageTimestamp: number, collectionTime: number, doctype?: string, html: NodeSnapshot, diff --git a/src/server/snapshot/snapshotter.ts b/src/server/snapshot/snapshotter.ts index 7785038082..642f8acf5b 100644 --- a/src/server/snapshot/snapshotter.ts +++ b/src/server/snapshot/snapshotter.ts @@ -92,7 +92,7 @@ export class Snapshotter { for (const page of this._context.pages()) frames.push(...page.frames()); await Promise.all(frames.map(frame => { - return frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(debugExceptionHandler); + return frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => debugLogger.log('error', e)); })); } @@ -111,7 +111,7 @@ export class Snapshotter { // In each frame, in a non-stalling manner, capture the snapshots. const snapshots = page.frames().map(async frame => { - const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(debugExceptionHandler) as SnapshotData; + const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => debugLogger.log('error', e)) as SnapshotData; // Something went wrong -> bail out, our snapshots are best-efforty. if (!data) return; @@ -125,7 +125,6 @@ export class Snapshotter { html: data.html, viewport: data.viewport, timestamp: monotonicTime(), - pageTimestamp: data.timestamp, collectionTime: data.collectionTime, resourceOverrides: [], isMainFrame: page.mainFrame() === frame @@ -230,7 +229,3 @@ export class Snapshotter { } } } - -function debugExceptionHandler(e: Error) { - // console.error(e); -} diff --git a/src/server/trace/common/traceEvents.ts b/src/server/trace/common/traceEvents.ts index 8796572dae..d1851467dd 100644 --- a/src/server/trace/common/traceEvents.ts +++ b/src/server/trace/common/traceEvents.ts @@ -43,7 +43,6 @@ export type ScreencastFrameTraceEvent = { timestamp: number, type: 'screencast-frame', pageId: string, - pageTimestamp: number, sha1: string, width: number, height: number, diff --git a/src/server/trace/recorder/tracing.ts b/src/server/trace/recorder/tracing.ts index 5b80e0faf1..a8658bc15c 100644 --- a/src/server/trace/recorder/tracing.ts +++ b/src/server/trace/recorder/tracing.ts @@ -239,7 +239,6 @@ export class Tracing implements InstrumentationListener { type: 'screencast-frame', pageId: page.guid, sha1, - pageTimestamp: params.timestamp, width: params.width, height: params.height, timestamp: monotonicTime() diff --git a/src/server/webkit/wkPage.ts b/src/server/webkit/wkPage.ts index 6a65cfd47f..a00ae4f3bf 100644 --- a/src/server/webkit/wkPage.ts +++ b/src/server/webkit/wkPage.ts @@ -40,6 +40,7 @@ import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './wkInput'; import { WKInterceptableRequest } from './wkInterceptableRequest'; import { WKProvisionalPage } from './wkProvisionalPage'; import { WKWorkers } from './wkWorkers'; +import { debugLogger } from '../../utils/debugLogger'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; const BINDING_CALL_MESSAGE = '__playwright_binding_call__'; @@ -72,6 +73,7 @@ export class WKPage implements PageDelegate { // until the popup page proxy arrives. private _nextWindowOpenPopupFeatures?: string[]; private _recordingVideoFile: string | null = null; + private _screencastGeneration: number = 0; constructor(browserContext: WKBrowserContext, pageProxySession: WKSession, opener: WKPage | null) { this._pageProxySession = pageProxySession; @@ -90,6 +92,7 @@ export class WKPage implements PageDelegate { helper.addEventListener(this._pageProxySession, 'Target.targetDestroyed', this._onTargetDestroyed.bind(this)), helper.addEventListener(this._pageProxySession, 'Target.dispatchMessageFromTarget', this._onDispatchMessageFromTarget.bind(this)), helper.addEventListener(this._pageProxySession, 'Target.didCommitProvisionalTarget', this._onDidCommitProvisionalTarget.bind(this)), + helper.addEventListener(this._pageProxySession, 'Screencast.screencastFrame', this._onScreencastFrame.bind(this)), ]; this._pagePromise = new Promise(f => this._pagePromiseCallback = f); this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => { @@ -121,7 +124,7 @@ export class WKPage implements PageDelegate { if (this._browserContext._options.recordVideo) { const outputFile = path.join(this._browserContext._options.recordVideo.dir, createGuid() + '.webm'); promises.push(this._browserContext._ensureVideosPath().then(() => { - return this._startScreencast({ + return this._startVideo({ // validateBrowserContextOptions ensures correct video size. ...this._browserContext._options.recordVideo!.size!, outputFile, @@ -721,7 +724,7 @@ export class WKPage implements PageDelegate { } async closePage(runBeforeUnload: boolean): Promise { - await this._stopScreencast(); + await this._stopVideo(); await this._pageProxySession.sendMayFail('Target.close', { targetId: this._session.sessionId, runBeforeUnload @@ -736,7 +739,7 @@ export class WKPage implements PageDelegate { await this._session.send('Page.setDefaultBackgroundColorOverride', { color }); } - async _startScreencast(options: types.PageScreencastOptions): Promise { + private async _startVideo(options: types.PageScreencastOptions): Promise { assert(!this._recordingVideoFile); const START_VIDEO_PROTOCOL_COMMAND = hostPlatform === 'mac10.14' ? 'Screencast.start' : 'Screencast.startVideo'; const { screencastId } = await this._pageProxySession.send(START_VIDEO_PROTOCOL_COMMAND as any, { @@ -748,7 +751,7 @@ export class WKPage implements PageDelegate { this._browserContext._browser._videoStarted(this._browserContext, screencastId, options.outputFile, this.pageOrError()); } - async _stopScreencast(): Promise { + private async _stopVideo(): Promise { if (!this._recordingVideoFile) return; const STOP_VIDEO_PROTOCOL_COMMAND = hostPlatform === 'mac10.14' ? 'Screencast.stop' : 'Screencast.stopVideo'; @@ -825,7 +828,22 @@ export class WKPage implements PageDelegate { } async setScreencastEnabled(enabled: boolean): Promise { - throw new Error('Not implemented'); + if (enabled) { + const { generation } = await this._pageProxySession.send('Screencast.startScreencast', { width: 800, height: 600, quality: 70 }); + this._screencastGeneration = generation; + } else { + await this._pageProxySession.send('Screencast.stopScreencast'); + } + } + + private _onScreencastFrame(event: Protocol.Screencast.screencastFramePayload) { + const buffer = Buffer.from(event.data, 'base64'); + this._page.emit(Page.Events.ScreencastFrame, { + buffer, + width: event.deviceWidth, + height: event.deviceHeight, + }); + this._pageProxySession.send('Screencast.screencastFrameAck', { generation: this._screencastGeneration }).catch(e => debugLogger.log('error', e)); } rafCountForStablePosition(): number { diff --git a/tests/screencast.spec.ts b/tests/screencast.spec.ts index 75d5f71d2e..06cfd5dcbb 100644 --- a/tests/screencast.spec.ts +++ b/tests/screencast.spec.ts @@ -160,7 +160,6 @@ it.describe('screencast', () => { }); it('should work with old options', async ({browser, isFirefox, isWindows}, testInfo) => { - it.fail(isFirefox && isWindows); const videosPath = testInfo.outputPath(''); const size = { width: 450, height: 240 }; const context = await browser.newContext({ @@ -184,7 +183,6 @@ it.describe('screencast', () => { }); it('should capture static page', async ({browser, isFirefox, isWindows}, testInfo) => { - it.fail(isFirefox && isWindows); const size = { width: 450, height: 240 }; const context = await browser.newContext({ recordVideo: { diff --git a/tests/tracing.spec.ts b/tests/tracing.spec.ts index b0101858e9..1e98bd9be3 100644 --- a/tests/tracing.spec.ts +++ b/tests/tracing.spec.ts @@ -45,8 +45,7 @@ test('should collect trace', async ({ context, page, server, browserName }, test expect(events.some(e => e.type === 'frame-snapshot')).toBeTruthy(); expect(events.some(e => e.type === 'resource-snapshot')).toBeTruthy(); - if (browserName === 'chromium') - expect(events.some(e => e.type === 'screencast-frame')).toBeTruthy(); + expect(events.some(e => e.type === 'screencast-frame')).toBeTruthy(); }); test('should collect trace', async ({ context, page, server }, testInfo) => {