playwright-core 1.58.0-alpha-2025-12-08 → 1.58.0-alpha-2025-12-10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browsers.json +8 -8
- package/lib/client/browserContext.js +7 -0
- package/lib/protocol/validator.js +10 -5
- package/lib/server/agent/actionRunner.js +16 -6
- package/lib/server/agent/agent.js +12 -10
- package/lib/server/agent/context.js +34 -10
- package/lib/server/agent/tools.js +51 -9
- package/lib/server/bidi/bidiBrowser.js +13 -4
- package/lib/server/bidi/bidiNetworkManager.js +1 -1
- package/lib/server/bidi/bidiPage.js +12 -2
- package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
- package/lib/server/browserContext.js +1 -1
- package/lib/server/chromium/chromiumSwitches.js +0 -2
- package/lib/server/chromium/crBrowser.js +1 -1
- package/lib/server/chromium/crNetworkManager.js +39 -2
- package/lib/server/chromium/crPage.js +14 -81
- package/lib/server/deviceDescriptorsSource.json +2 -2
- package/lib/server/firefox/ffNetworkManager.js +2 -2
- package/lib/server/firefox/ffPage.js +9 -8
- package/lib/server/frames.js +2 -2
- package/lib/server/network.js +3 -0
- package/lib/server/page.js +4 -64
- package/lib/server/progress.js +3 -2
- package/lib/server/registry/index.js +1 -1
- package/lib/server/screencast.js +191 -0
- package/lib/server/trace/recorder/tracing.js +5 -5
- package/lib/server/utils/network.js +18 -26
- package/lib/server/{chromium/videoRecorder.js → videoRecorder.js} +3 -3
- package/lib/server/webkit/wkBrowser.js +1 -1
- package/lib/server/webkit/wkInterceptableRequest.js +29 -1
- package/lib/server/webkit/wkPage.js +20 -40
- package/lib/vite/traceViewer/uiMode.DiEbKRwa.js +5 -0
- package/lib/vite/traceViewer/uiMode.html +1 -1
- package/package.json +1 -1
- package/types/types.d.ts +8 -3
- package/lib/vite/traceViewer/uiMode.CmFFBCQb.js +0 -5
|
@@ -31,9 +31,7 @@ __export(crPage_exports, {
|
|
|
31
31
|
CRPage: () => CRPage
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(crPage_exports);
|
|
34
|
-
var import_path = __toESM(require("path"));
|
|
35
34
|
var import_assert = require("../../utils/isomorphic/assert");
|
|
36
|
-
var import_crypto = require("../utils/crypto");
|
|
37
35
|
var import_eventsHelper = require("../utils/eventsHelper");
|
|
38
36
|
var import_stackTrace = require("../../utils/isomorphic/stackTrace");
|
|
39
37
|
var dialog = __toESM(require("../dialog"));
|
|
@@ -42,7 +40,6 @@ var frames = __toESM(require("../frames"));
|
|
|
42
40
|
var import_helper = require("../helper");
|
|
43
41
|
var network = __toESM(require("../network"));
|
|
44
42
|
var import_page = require("../page");
|
|
45
|
-
var import_registry = require("../registry");
|
|
46
43
|
var import_crCoverage = require("./crCoverage");
|
|
47
44
|
var import_crDragDrop = require("./crDragDrop");
|
|
48
45
|
var import_crExecutionContext = require("./crExecutionContext");
|
|
@@ -51,7 +48,6 @@ var import_crNetworkManager = require("./crNetworkManager");
|
|
|
51
48
|
var import_crPdf = require("./crPdf");
|
|
52
49
|
var import_crProtocolHelper = require("./crProtocolHelper");
|
|
53
50
|
var import_defaultFontFamilies = require("./defaultFontFamilies");
|
|
54
|
-
var import_videoRecorder = require("./videoRecorder");
|
|
55
51
|
var import_errors = require("../errors");
|
|
56
52
|
var import_protocolError = require("../protocolError");
|
|
57
53
|
class CRPage {
|
|
@@ -236,17 +232,16 @@ class CRPage {
|
|
|
236
232
|
async scrollRectIntoViewIfNeeded(handle, rect) {
|
|
237
233
|
return this._sessionForHandle(handle)._scrollRectIntoViewIfNeeded(handle, rect);
|
|
238
234
|
}
|
|
239
|
-
async
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
235
|
+
async startScreencast(options) {
|
|
236
|
+
await this._mainFrameSession._client.send("Page.startScreencast", {
|
|
237
|
+
format: "jpeg",
|
|
238
|
+
quality: options.quality,
|
|
239
|
+
maxWidth: options.width,
|
|
240
|
+
maxHeight: options.height
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async stopScreencast() {
|
|
244
|
+
await this._mainFrameSession._client._sendMayFail("Page.stopScreencast");
|
|
250
245
|
}
|
|
251
246
|
rafCountForStablePosition() {
|
|
252
247
|
return 1;
|
|
@@ -311,9 +306,6 @@ class FrameSession {
|
|
|
311
306
|
// Marks the oopif session that remote -> local transition has happened in the parent.
|
|
312
307
|
// See Target.detachedFromTarget handler for details.
|
|
313
308
|
this._swappedIn = false;
|
|
314
|
-
this._videoRecorder = null;
|
|
315
|
-
this._screencastId = null;
|
|
316
|
-
this._screencastClients = /* @__PURE__ */ new Set();
|
|
317
309
|
this._workerSessions = /* @__PURE__ */ new Map();
|
|
318
310
|
this._initScriptIds = /* @__PURE__ */ new Map();
|
|
319
311
|
this._client = client;
|
|
@@ -366,22 +358,8 @@ class FrameSession {
|
|
|
366
358
|
this._windowId = windowId;
|
|
367
359
|
}
|
|
368
360
|
let screencastOptions;
|
|
369
|
-
if (!this._page.isStorageStatePage && this._isMainFrame() &&
|
|
370
|
-
|
|
371
|
-
const outputFile = import_path.default.join(this._crPage._browserContext._options.recordVideo.dir, screencastId + ".webm");
|
|
372
|
-
screencastOptions = {
|
|
373
|
-
// validateBrowserContextOptions ensures correct video size.
|
|
374
|
-
...this._crPage._browserContext._options.recordVideo.size,
|
|
375
|
-
outputFile
|
|
376
|
-
};
|
|
377
|
-
await this._crPage._browserContext._ensureVideosPath();
|
|
378
|
-
await this._createVideoRecorder(screencastId, screencastOptions);
|
|
379
|
-
this._crPage._page.waitForInitializedOrError().then((p) => {
|
|
380
|
-
if (p instanceof Error)
|
|
381
|
-
this._stopVideoRecording().catch(() => {
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
}
|
|
361
|
+
if (!this._page.isStorageStatePage && this._isMainFrame() && hasUIWindow)
|
|
362
|
+
screencastOptions = await this._crPage._page.screencast.initializeVideoRecorder();
|
|
385
363
|
let lifecycleEventsEnabled;
|
|
386
364
|
if (!this._isMainFrame())
|
|
387
365
|
this._addRendererListeners();
|
|
@@ -462,7 +440,7 @@ class FrameSession {
|
|
|
462
440
|
/* runImmediately */
|
|
463
441
|
));
|
|
464
442
|
if (screencastOptions)
|
|
465
|
-
promises.push(this.
|
|
443
|
+
promises.push(this._crPage._page.screencast.startVideoRecording(screencastOptions));
|
|
466
444
|
}
|
|
467
445
|
promises.push(this._client.send("Runtime.runIfWaitingForDebugger"));
|
|
468
446
|
promises.push(this._firstNonInitialNavigationCommittedPromise);
|
|
@@ -730,7 +708,7 @@ class FrameSession {
|
|
|
730
708
|
}
|
|
731
709
|
}
|
|
732
710
|
_onScreencastFrame(payload) {
|
|
733
|
-
this._page.
|
|
711
|
+
this._page.screencast.throttleFrameAck(() => {
|
|
734
712
|
this._client.send("Page.screencastFrameAck", { sessionId: payload.sessionId }).catch(() => {
|
|
735
713
|
});
|
|
736
714
|
});
|
|
@@ -742,51 +720,6 @@ class FrameSession {
|
|
|
742
720
|
height: payload.metadata.deviceHeight
|
|
743
721
|
});
|
|
744
722
|
}
|
|
745
|
-
async _createVideoRecorder(screencastId, options) {
|
|
746
|
-
(0, import_assert.assert)(!this._screencastId);
|
|
747
|
-
const ffmpegPath = import_registry.registry.findExecutable("ffmpeg").executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
|
|
748
|
-
this._videoRecorder = await import_videoRecorder.VideoRecorder.launch(this._crPage._page, ffmpegPath, options);
|
|
749
|
-
this._screencastId = screencastId;
|
|
750
|
-
}
|
|
751
|
-
async _startVideoRecording(options) {
|
|
752
|
-
const screencastId = this._screencastId;
|
|
753
|
-
(0, import_assert.assert)(screencastId);
|
|
754
|
-
this._page.once(import_page.Page.Events.Close, () => this._stopVideoRecording().catch(() => {
|
|
755
|
-
}));
|
|
756
|
-
const gotFirstFrame = new Promise((f) => this._client.once("Page.screencastFrame", f));
|
|
757
|
-
await this._startScreencast(this._videoRecorder, {
|
|
758
|
-
format: "jpeg",
|
|
759
|
-
quality: 90,
|
|
760
|
-
maxWidth: options.width,
|
|
761
|
-
maxHeight: options.height
|
|
762
|
-
});
|
|
763
|
-
gotFirstFrame.then(() => {
|
|
764
|
-
this._crPage._browserContext._browser._videoStarted(this._crPage._browserContext, screencastId, options.outputFile, this._crPage._page.waitForInitializedOrError());
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
async _stopVideoRecording() {
|
|
768
|
-
if (!this._screencastId)
|
|
769
|
-
return;
|
|
770
|
-
const screencastId = this._screencastId;
|
|
771
|
-
this._screencastId = null;
|
|
772
|
-
const recorder = this._videoRecorder;
|
|
773
|
-
this._videoRecorder = null;
|
|
774
|
-
await this._stopScreencast(recorder);
|
|
775
|
-
await recorder.stop().catch(() => {
|
|
776
|
-
});
|
|
777
|
-
const video = this._crPage._browserContext._browser._takeVideo(screencastId);
|
|
778
|
-
video?.reportFinished();
|
|
779
|
-
}
|
|
780
|
-
async _startScreencast(client, options = {}) {
|
|
781
|
-
this._screencastClients.add(client);
|
|
782
|
-
if (this._screencastClients.size === 1)
|
|
783
|
-
await this._client.send("Page.startScreencast", options);
|
|
784
|
-
}
|
|
785
|
-
async _stopScreencast(client) {
|
|
786
|
-
this._screencastClients.delete(client);
|
|
787
|
-
if (!this._screencastClients.size)
|
|
788
|
-
await this._client._sendMayFail("Page.stopScreencast");
|
|
789
|
-
}
|
|
790
723
|
async _updateGeolocation(initial) {
|
|
791
724
|
const geolocation = this._crPage._browserContext._options.geolocation;
|
|
792
725
|
if (!initial || geolocation)
|
|
@@ -1702,7 +1702,7 @@
|
|
|
1702
1702
|
"defaultBrowserType": "chromium"
|
|
1703
1703
|
},
|
|
1704
1704
|
"Desktop Firefox HiDPI": {
|
|
1705
|
-
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0.
|
|
1705
|
+
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0.2) Gecko/20100101 Firefox/145.0.2",
|
|
1706
1706
|
"screen": {
|
|
1707
1707
|
"width": 1792,
|
|
1708
1708
|
"height": 1120
|
|
@@ -1762,7 +1762,7 @@
|
|
|
1762
1762
|
"defaultBrowserType": "chromium"
|
|
1763
1763
|
},
|
|
1764
1764
|
"Desktop Firefox": {
|
|
1765
|
-
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0.
|
|
1765
|
+
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0.2) Gecko/20100101 Firefox/145.0.2",
|
|
1766
1766
|
"screen": {
|
|
1767
1767
|
"width": 1920,
|
|
1768
1768
|
"height": 1080
|
|
@@ -170,9 +170,9 @@ const causeToResourceType = {
|
|
|
170
170
|
TYPE_FONT: "font",
|
|
171
171
|
TYPE_MEDIA: "media",
|
|
172
172
|
TYPE_WEBSOCKET: "websocket",
|
|
173
|
-
TYPE_CSP_REPORT: "
|
|
173
|
+
TYPE_CSP_REPORT: "cspreport",
|
|
174
174
|
TYPE_XSLT: "other",
|
|
175
|
-
TYPE_BEACON: "
|
|
175
|
+
TYPE_BEACON: "beacon",
|
|
176
176
|
TYPE_FETCH: "fetch",
|
|
177
177
|
TYPE_IMAGESET: "image",
|
|
178
178
|
TYPE_WEB_MANIFEST: "manifest"
|
|
@@ -417,24 +417,25 @@ class FFPage {
|
|
|
417
417
|
throw e;
|
|
418
418
|
});
|
|
419
419
|
}
|
|
420
|
-
async
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
420
|
+
async startScreencast(options) {
|
|
421
|
+
const { screencastId } = await this._session.send("Page.startScreencast", options);
|
|
422
|
+
this._screencastId = screencastId;
|
|
423
|
+
}
|
|
424
|
+
async stopScreencast() {
|
|
425
|
+
await this._session.send("Page.stopScreencast");
|
|
427
426
|
}
|
|
428
427
|
_onScreencastFrame(event) {
|
|
429
428
|
if (!this._screencastId)
|
|
430
429
|
return;
|
|
431
430
|
const screencastId = this._screencastId;
|
|
432
|
-
this._page.
|
|
431
|
+
this._page.screencast.throttleFrameAck(() => {
|
|
433
432
|
this._session.send("Page.screencastFrameAck", { screencastId }).catch((e) => import_debugLogger.debugLogger.log("error", e));
|
|
434
433
|
});
|
|
435
434
|
const buffer = Buffer.from(event.data, "base64");
|
|
436
435
|
this._page.emit(import_page2.Page.Events.ScreencastFrame, {
|
|
437
436
|
buffer,
|
|
437
|
+
frameSwapWallTime: event.timestamp * 1e3,
|
|
438
|
+
// timestamp is in seconds, we need to convert to milliseconds.
|
|
438
439
|
width: event.deviceWidth,
|
|
439
440
|
height: event.deviceHeight
|
|
440
441
|
});
|
package/lib/server/frames.js
CHANGED
|
@@ -569,7 +569,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
569
569
|
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0;
|
|
570
570
|
return request ? progress.race(request._finalRequest().response()) : null;
|
|
571
571
|
}
|
|
572
|
-
async
|
|
572
|
+
async waitForLoadState(progress, state) {
|
|
573
573
|
const waitUntil = verifyLifecycle("state", state);
|
|
574
574
|
if (!this._firedLifecycleEvents.has(waitUntil))
|
|
575
575
|
await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
|
|
@@ -736,7 +736,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
736
736
|
this._onClearLifecycle();
|
|
737
737
|
tagPromise.resolve();
|
|
738
738
|
});
|
|
739
|
-
const lifecyclePromise = progress.race(tagPromise).then(() => this.
|
|
739
|
+
const lifecyclePromise = progress.race(tagPromise).then(() => this.waitForLoadState(progress, waitUntil));
|
|
740
740
|
const contentPromise = progress.race(context.evaluate(({ html: html2, tag: tag2 }) => {
|
|
741
741
|
document.open();
|
|
742
742
|
console.debug(tag2);
|
package/lib/server/network.js
CHANGED
package/lib/server/page.js
CHANGED
|
@@ -53,6 +53,7 @@ var import_manualPromise = require("../utils/isomorphic/manualPromise");
|
|
|
53
53
|
var import_utilityScriptSerializers = require("../utils/isomorphic/utilityScriptSerializers");
|
|
54
54
|
var import_callLog = require("./callLog");
|
|
55
55
|
var rawBindingsControllerSource = __toESM(require("../generated/bindingsControllerSource"));
|
|
56
|
+
var import_screencast = require("./screencast");
|
|
56
57
|
class Page extends import_instrumentation.SdkObject {
|
|
57
58
|
constructor(delegate, browserContext) {
|
|
58
59
|
super(browserContext, "page");
|
|
@@ -74,9 +75,6 @@ class Page extends import_instrumentation.SdkObject {
|
|
|
74
75
|
this._lastLocatorHandlerUid = 0;
|
|
75
76
|
this._locatorHandlerRunningCounter = 0;
|
|
76
77
|
this._networkRequests = [];
|
|
77
|
-
// Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms.
|
|
78
|
-
// When throttling for tracing, 200ms between frames, except for 10 frames around the action.
|
|
79
|
-
this._frameThrottler = new FrameThrottler(10, 35, 200);
|
|
80
78
|
this.attribution.page = this;
|
|
81
79
|
this.delegate = delegate;
|
|
82
80
|
this.browserContext = browserContext;
|
|
@@ -85,6 +83,7 @@ class Page extends import_instrumentation.SdkObject {
|
|
|
85
83
|
this.touchscreen = new input.Touchscreen(delegate.rawTouchscreen, this);
|
|
86
84
|
this.screenshotter = new import_screenshotter.Screenshotter(this);
|
|
87
85
|
this.frameManager = new frames.FrameManager(this);
|
|
86
|
+
this.screencast = new import_screencast.Screencast(this);
|
|
88
87
|
if (delegate.pdf)
|
|
89
88
|
this.pdf = delegate.pdf.bind(delegate);
|
|
90
89
|
this.coverage = delegate.coverage ? delegate.coverage() : null;
|
|
@@ -158,7 +157,7 @@ class Page extends import_instrumentation.SdkObject {
|
|
|
158
157
|
}
|
|
159
158
|
_didClose() {
|
|
160
159
|
this.frameManager.dispose();
|
|
161
|
-
this.
|
|
160
|
+
this.screencast.stopFrameThrottler();
|
|
162
161
|
(0, import_utils.assert)(this._closedState !== "closed", "Page closed twice");
|
|
163
162
|
this._closedState = "closed";
|
|
164
163
|
this.emit(Page.Events.Close);
|
|
@@ -168,7 +167,7 @@ class Page extends import_instrumentation.SdkObject {
|
|
|
168
167
|
}
|
|
169
168
|
_didCrash() {
|
|
170
169
|
this.frameManager.dispose();
|
|
171
|
-
this.
|
|
170
|
+
this.screencast.stopFrameThrottler();
|
|
172
171
|
this.emit(Page.Events.Crash);
|
|
173
172
|
this._crashed = true;
|
|
174
173
|
this.instrumentation.onPageClose(this);
|
|
@@ -641,16 +640,6 @@ class Page extends import_instrumentation.SdkObject {
|
|
|
641
640
|
getBinding(name) {
|
|
642
641
|
return this._pageBindings.get(name) || this.browserContext._pageBindings.get(name);
|
|
643
642
|
}
|
|
644
|
-
setScreencastOptions(options) {
|
|
645
|
-
this.delegate.setScreencastOptions(options).catch((e) => import_debugLogger.debugLogger.log("error", e));
|
|
646
|
-
this._frameThrottler.setThrottlingEnabled(!!options);
|
|
647
|
-
}
|
|
648
|
-
throttleScreencastFrameAck(ack) {
|
|
649
|
-
this._frameThrottler.ack(ack);
|
|
650
|
-
}
|
|
651
|
-
temporarilyDisableTracingScreencastThrottling() {
|
|
652
|
-
this._frameThrottler.recharge();
|
|
653
|
-
}
|
|
654
643
|
async safeNonStallingEvaluateInAllFrames(expression, world, options = {}) {
|
|
655
644
|
await Promise.all(this.frames().map(async (frame) => {
|
|
656
645
|
try {
|
|
@@ -763,55 +752,6 @@ class InitScript {
|
|
|
763
752
|
})();`;
|
|
764
753
|
}
|
|
765
754
|
}
|
|
766
|
-
class FrameThrottler {
|
|
767
|
-
constructor(nonThrottledFrames, defaultInterval, throttlingInterval) {
|
|
768
|
-
this._acks = [];
|
|
769
|
-
this._throttlingEnabled = false;
|
|
770
|
-
this._nonThrottledFrames = nonThrottledFrames;
|
|
771
|
-
this._budget = nonThrottledFrames;
|
|
772
|
-
this._defaultInterval = defaultInterval;
|
|
773
|
-
this._throttlingInterval = throttlingInterval;
|
|
774
|
-
this._tick();
|
|
775
|
-
}
|
|
776
|
-
dispose() {
|
|
777
|
-
if (this._timeoutId) {
|
|
778
|
-
clearTimeout(this._timeoutId);
|
|
779
|
-
this._timeoutId = void 0;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
setThrottlingEnabled(enabled) {
|
|
783
|
-
this._throttlingEnabled = enabled;
|
|
784
|
-
}
|
|
785
|
-
recharge() {
|
|
786
|
-
for (const ack of this._acks)
|
|
787
|
-
ack();
|
|
788
|
-
this._acks = [];
|
|
789
|
-
this._budget = this._nonThrottledFrames;
|
|
790
|
-
if (this._timeoutId) {
|
|
791
|
-
clearTimeout(this._timeoutId);
|
|
792
|
-
this._tick();
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
ack(ack) {
|
|
796
|
-
if (!this._timeoutId) {
|
|
797
|
-
ack();
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
this._acks.push(ack);
|
|
801
|
-
}
|
|
802
|
-
_tick() {
|
|
803
|
-
const ack = this._acks.shift();
|
|
804
|
-
if (ack) {
|
|
805
|
-
--this._budget;
|
|
806
|
-
ack();
|
|
807
|
-
}
|
|
808
|
-
if (this._throttlingEnabled && this._budget <= 0) {
|
|
809
|
-
this._timeoutId = setTimeout(() => this._tick(), this._throttlingInterval);
|
|
810
|
-
} else {
|
|
811
|
-
this._timeoutId = setTimeout(() => this._tick(), this._defaultInterval);
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
755
|
async function snapshotFrameForAI(progress, frame, options) {
|
|
816
756
|
const snapshot = await frame.retryWithProgressAndTimeouts(progress, [1e3, 2e3, 4e3, 8e3], async (continuePolling) => {
|
|
817
757
|
try {
|
package/lib/server/progress.js
CHANGED
|
@@ -53,9 +53,10 @@ class ProgressController {
|
|
|
53
53
|
this._onCallLog?.(message);
|
|
54
54
|
},
|
|
55
55
|
metadata: this.metadata,
|
|
56
|
-
race: (promise) => {
|
|
56
|
+
race: (promise, options) => {
|
|
57
57
|
const promises = Array.isArray(promise) ? promise : [promise];
|
|
58
|
-
|
|
58
|
+
const timerPromise = options?.timeout ? new Promise((f) => setTimeout(f, options.timeout)) : null;
|
|
59
|
+
return Promise.race([...promises, ...timerPromise ? [timerPromise] : [], this._forceAbortPromise]);
|
|
59
60
|
},
|
|
60
61
|
wait: async (timeout2) => {
|
|
61
62
|
let timer2;
|
|
@@ -1296,7 +1296,7 @@ ${e.stack}`);
|
|
|
1296
1296
|
faultyArguments.push(arg);
|
|
1297
1297
|
else
|
|
1298
1298
|
executables.push(executable);
|
|
1299
|
-
if (executable?.browserName
|
|
1299
|
+
if (executable?.browserName)
|
|
1300
1300
|
executables.push(this.findExecutable("ffmpeg"));
|
|
1301
1301
|
};
|
|
1302
1302
|
for (const alias of aliases) {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var screencast_exports = {};
|
|
30
|
+
__export(screencast_exports, {
|
|
31
|
+
Screencast: () => Screencast
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(screencast_exports);
|
|
34
|
+
var import_path = __toESM(require("path"));
|
|
35
|
+
var import_utils = require("../utils");
|
|
36
|
+
var import_utils2 = require("../utils");
|
|
37
|
+
var import_videoRecorder = require("./videoRecorder");
|
|
38
|
+
var import_page = require("./page");
|
|
39
|
+
var import_registry = require("./registry");
|
|
40
|
+
class Screencast {
|
|
41
|
+
constructor(page) {
|
|
42
|
+
this._videoRecorder = null;
|
|
43
|
+
this._screencastId = null;
|
|
44
|
+
this._screencastClients = /* @__PURE__ */ new Set();
|
|
45
|
+
// Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms.
|
|
46
|
+
// When throttling for tracing, 200ms between frames, except for 10 frames around the action.
|
|
47
|
+
this._frameThrottler = new FrameThrottler(10, 35, 200);
|
|
48
|
+
this._page = page;
|
|
49
|
+
}
|
|
50
|
+
stopFrameThrottler() {
|
|
51
|
+
this._frameThrottler.dispose();
|
|
52
|
+
}
|
|
53
|
+
setOptions(options) {
|
|
54
|
+
this._setOptions(options).catch((e) => import_utils2.debugLogger.log("error", e));
|
|
55
|
+
this._frameThrottler.setThrottlingEnabled(!!options);
|
|
56
|
+
}
|
|
57
|
+
throttleFrameAck(ack) {
|
|
58
|
+
this._frameThrottler.ack(ack);
|
|
59
|
+
}
|
|
60
|
+
temporarilyDisableThrottling() {
|
|
61
|
+
this._frameThrottler.recharge();
|
|
62
|
+
}
|
|
63
|
+
async initializeVideoRecorder() {
|
|
64
|
+
const recordVideo = this._page.browserContext._options.recordVideo;
|
|
65
|
+
if (!recordVideo)
|
|
66
|
+
return void 0;
|
|
67
|
+
const screencastId = (0, import_utils.createGuid)();
|
|
68
|
+
const outputFile = import_path.default.join(recordVideo.dir, screencastId + ".webm");
|
|
69
|
+
const screencastOptions = {
|
|
70
|
+
// validateBrowserContextOptions ensures correct video size.
|
|
71
|
+
...recordVideo.size,
|
|
72
|
+
outputFile
|
|
73
|
+
};
|
|
74
|
+
await (0, import_utils.mkdirIfNeeded)(import_path.default.join(recordVideo.dir, "dummy"));
|
|
75
|
+
await this._createVideoRecorder(screencastId, screencastOptions);
|
|
76
|
+
this._page.waitForInitializedOrError().then((p) => {
|
|
77
|
+
if (p instanceof Error)
|
|
78
|
+
this.stopVideoRecording().catch(() => {
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return screencastOptions;
|
|
82
|
+
}
|
|
83
|
+
async startVideoRecording(options) {
|
|
84
|
+
const screencastId = this._screencastId;
|
|
85
|
+
(0, import_utils.assert)(screencastId);
|
|
86
|
+
this._page.once(import_page.Page.Events.Close, () => this.stopVideoRecording().catch(() => {
|
|
87
|
+
}));
|
|
88
|
+
const gotFirstFrame = new Promise((f) => this._page.once(import_page.Page.Events.ScreencastFrame, f));
|
|
89
|
+
await this._startScreencast(this._videoRecorder, {
|
|
90
|
+
quality: 90,
|
|
91
|
+
width: options.width,
|
|
92
|
+
height: options.height
|
|
93
|
+
});
|
|
94
|
+
gotFirstFrame.then(() => {
|
|
95
|
+
this._page.browserContext._browser._videoStarted(this._page.browserContext, screencastId, options.outputFile, this._page.waitForInitializedOrError());
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async stopVideoRecording() {
|
|
99
|
+
if (!this._screencastId)
|
|
100
|
+
return;
|
|
101
|
+
const screencastId = this._screencastId;
|
|
102
|
+
this._screencastId = null;
|
|
103
|
+
const recorder = this._videoRecorder;
|
|
104
|
+
this._videoRecorder = null;
|
|
105
|
+
await this._stopScreencast(recorder);
|
|
106
|
+
await recorder.stop().catch(() => {
|
|
107
|
+
});
|
|
108
|
+
const video = this._page.browserContext._browser._takeVideo(screencastId);
|
|
109
|
+
video?.reportFinished();
|
|
110
|
+
}
|
|
111
|
+
async _setOptions(options) {
|
|
112
|
+
if (options)
|
|
113
|
+
await this._startScreencast(this, options);
|
|
114
|
+
else
|
|
115
|
+
await this._stopScreencast(this);
|
|
116
|
+
}
|
|
117
|
+
async _startScreencast(client, options) {
|
|
118
|
+
this._screencastClients.add(client);
|
|
119
|
+
if (this._screencastClients.size === 1) {
|
|
120
|
+
await this._page.delegate.startScreencast({
|
|
121
|
+
width: options.width,
|
|
122
|
+
height: options.height,
|
|
123
|
+
quality: options.quality
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async _stopScreencast(client) {
|
|
128
|
+
this._screencastClients.delete(client);
|
|
129
|
+
if (!this._screencastClients.size)
|
|
130
|
+
await this._page.delegate.stopScreencast();
|
|
131
|
+
}
|
|
132
|
+
async _createVideoRecorder(screencastId, options) {
|
|
133
|
+
(0, import_utils.assert)(!this._screencastId);
|
|
134
|
+
const ffmpegPath = import_registry.registry.findExecutable("ffmpeg").executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
|
|
135
|
+
this._videoRecorder = await import_videoRecorder.VideoRecorder.launch(this._page, ffmpegPath, options);
|
|
136
|
+
this._screencastId = screencastId;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
class FrameThrottler {
|
|
140
|
+
constructor(nonThrottledFrames, defaultInterval, throttlingInterval) {
|
|
141
|
+
this._acks = [];
|
|
142
|
+
this._throttlingEnabled = false;
|
|
143
|
+
this._nonThrottledFrames = nonThrottledFrames;
|
|
144
|
+
this._budget = nonThrottledFrames;
|
|
145
|
+
this._defaultInterval = defaultInterval;
|
|
146
|
+
this._throttlingInterval = throttlingInterval;
|
|
147
|
+
this._tick();
|
|
148
|
+
}
|
|
149
|
+
dispose() {
|
|
150
|
+
if (this._timeoutId) {
|
|
151
|
+
clearTimeout(this._timeoutId);
|
|
152
|
+
this._timeoutId = void 0;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
setThrottlingEnabled(enabled) {
|
|
156
|
+
this._throttlingEnabled = enabled;
|
|
157
|
+
}
|
|
158
|
+
recharge() {
|
|
159
|
+
for (const ack of this._acks)
|
|
160
|
+
ack();
|
|
161
|
+
this._acks = [];
|
|
162
|
+
this._budget = this._nonThrottledFrames;
|
|
163
|
+
if (this._timeoutId) {
|
|
164
|
+
clearTimeout(this._timeoutId);
|
|
165
|
+
this._tick();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
ack(ack) {
|
|
169
|
+
if (!this._timeoutId) {
|
|
170
|
+
ack();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this._acks.push(ack);
|
|
174
|
+
}
|
|
175
|
+
_tick() {
|
|
176
|
+
const ack = this._acks.shift();
|
|
177
|
+
if (ack) {
|
|
178
|
+
--this._budget;
|
|
179
|
+
ack();
|
|
180
|
+
}
|
|
181
|
+
if (this._throttlingEnabled && this._budget <= 0) {
|
|
182
|
+
this._timeoutId = setTimeout(() => this._tick(), this._throttlingInterval);
|
|
183
|
+
} else {
|
|
184
|
+
this._timeoutId = setTimeout(() => this._tick(), this._defaultInterval);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
189
|
+
0 && (module.exports = {
|
|
190
|
+
Screencast
|
|
191
|
+
});
|
|
@@ -222,7 +222,7 @@ class Tracing extends import_instrumentation.SdkObject {
|
|
|
222
222
|
if (!(this._context instanceof import_browserContext.BrowserContext))
|
|
223
223
|
return;
|
|
224
224
|
for (const page of this._context.pages())
|
|
225
|
-
page.
|
|
225
|
+
page.screencast.setOptions(null);
|
|
226
226
|
}
|
|
227
227
|
_allocateNewTraceFile(state) {
|
|
228
228
|
const suffix = state.chunkOrdinal ? `-chunk${state.chunkOrdinal}` : ``;
|
|
@@ -341,7 +341,7 @@ class Tracing extends import_instrumentation.SdkObject {
|
|
|
341
341
|
const event = createBeforeActionTraceEvent(metadata, this._currentGroupId());
|
|
342
342
|
if (!event)
|
|
343
343
|
return Promise.resolve();
|
|
344
|
-
sdkObject.attribution.page?.
|
|
344
|
+
sdkObject.attribution.page?.screencast.temporarilyDisableThrottling();
|
|
345
345
|
event.beforeSnapshot = `before@${metadata.id}`;
|
|
346
346
|
this._state?.callIds.add(metadata.id);
|
|
347
347
|
this._appendTraceEvent(event);
|
|
@@ -353,7 +353,7 @@ class Tracing extends import_instrumentation.SdkObject {
|
|
|
353
353
|
const event = createInputActionTraceEvent(metadata);
|
|
354
354
|
if (!event)
|
|
355
355
|
return Promise.resolve();
|
|
356
|
-
sdkObject.attribution.page?.
|
|
356
|
+
sdkObject.attribution.page?.screencast.temporarilyDisableThrottling();
|
|
357
357
|
event.inputSnapshot = `input@${metadata.id}`;
|
|
358
358
|
this._appendTraceEvent(event);
|
|
359
359
|
return this._captureSnapshot(event.inputSnapshot, sdkObject, metadata);
|
|
@@ -376,7 +376,7 @@ class Tracing extends import_instrumentation.SdkObject {
|
|
|
376
376
|
const event = createAfterActionTraceEvent(metadata);
|
|
377
377
|
if (!event)
|
|
378
378
|
return;
|
|
379
|
-
sdkObject.attribution.page?.
|
|
379
|
+
sdkObject.attribution.page?.screencast.temporarilyDisableThrottling();
|
|
380
380
|
event.afterSnapshot = `after@${metadata.id}`;
|
|
381
381
|
this._appendTraceEvent(event);
|
|
382
382
|
return this._captureSnapshot(event.afterSnapshot, sdkObject, metadata);
|
|
@@ -484,7 +484,7 @@ class Tracing extends import_instrumentation.SdkObject {
|
|
|
484
484
|
this._appendTraceEvent(event);
|
|
485
485
|
}
|
|
486
486
|
_startScreencastInPage(page) {
|
|
487
|
-
page.
|
|
487
|
+
page.screencast.setOptions(kScreencastOptions);
|
|
488
488
|
const prefix = page.guid;
|
|
489
489
|
this._screencastListeners.push(
|
|
490
490
|
import_eventsHelper.eventsHelper.addEventListener(page, import_page.Page.Events.ScreencastFrame, (params) => {
|