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.
Files changed (36) hide show
  1. package/browsers.json +8 -8
  2. package/lib/client/browserContext.js +7 -0
  3. package/lib/protocol/validator.js +10 -5
  4. package/lib/server/agent/actionRunner.js +16 -6
  5. package/lib/server/agent/agent.js +12 -10
  6. package/lib/server/agent/context.js +34 -10
  7. package/lib/server/agent/tools.js +51 -9
  8. package/lib/server/bidi/bidiBrowser.js +13 -4
  9. package/lib/server/bidi/bidiNetworkManager.js +1 -1
  10. package/lib/server/bidi/bidiPage.js +12 -2
  11. package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
  12. package/lib/server/browserContext.js +1 -1
  13. package/lib/server/chromium/chromiumSwitches.js +0 -2
  14. package/lib/server/chromium/crBrowser.js +1 -1
  15. package/lib/server/chromium/crNetworkManager.js +39 -2
  16. package/lib/server/chromium/crPage.js +14 -81
  17. package/lib/server/deviceDescriptorsSource.json +2 -2
  18. package/lib/server/firefox/ffNetworkManager.js +2 -2
  19. package/lib/server/firefox/ffPage.js +9 -8
  20. package/lib/server/frames.js +2 -2
  21. package/lib/server/network.js +3 -0
  22. package/lib/server/page.js +4 -64
  23. package/lib/server/progress.js +3 -2
  24. package/lib/server/registry/index.js +1 -1
  25. package/lib/server/screencast.js +191 -0
  26. package/lib/server/trace/recorder/tracing.js +5 -5
  27. package/lib/server/utils/network.js +18 -26
  28. package/lib/server/{chromium/videoRecorder.js → videoRecorder.js} +3 -3
  29. package/lib/server/webkit/wkBrowser.js +1 -1
  30. package/lib/server/webkit/wkInterceptableRequest.js +29 -1
  31. package/lib/server/webkit/wkPage.js +20 -40
  32. package/lib/vite/traceViewer/uiMode.DiEbKRwa.js +5 -0
  33. package/lib/vite/traceViewer/uiMode.html +1 -1
  34. package/package.json +1 -1
  35. package/types/types.d.ts +8 -3
  36. 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 setScreencastOptions(options) {
240
- if (options) {
241
- await this._mainFrameSession._startScreencast(this, {
242
- format: "jpeg",
243
- quality: options.quality,
244
- maxWidth: options.width,
245
- maxHeight: options.height
246
- });
247
- } else {
248
- await this._mainFrameSession._stopScreencast(this);
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() && this._crPage._browserContext._options.recordVideo && hasUIWindow) {
370
- const screencastId = (0, import_crypto.createGuid)();
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._startVideoRecording(screencastOptions));
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.throttleScreencastFrameAck(() => {
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.1) Gecko/20100101 Firefox/145.0.1",
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.1) Gecko/20100101 Firefox/145.0.1",
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: "other",
173
+ TYPE_CSP_REPORT: "cspreport",
174
174
  TYPE_XSLT: "other",
175
- TYPE_BEACON: "other",
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 setScreencastOptions(options) {
421
- if (options) {
422
- const { screencastId } = await this._session.send("Page.startScreencast", options);
423
- this._screencastId = screencastId;
424
- } else {
425
- await this._session.send("Page.stopScreencast");
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.throttleScreencastFrameAck(() => {
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
  });
@@ -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 _waitForLoadState(progress, state) {
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._waitForLoadState(progress, waitUntil));
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);
@@ -436,6 +436,9 @@ class Response extends import_instrumentation.SdkObject {
436
436
  request() {
437
437
  return this._request;
438
438
  }
439
+ finished() {
440
+ return this._finishedPromise;
441
+ }
439
442
  frame() {
440
443
  return this._request.frame();
441
444
  }
@@ -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._frameThrottler.dispose();
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._frameThrottler.dispose();
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 {
@@ -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
- return Promise.race([...promises, this._forceAbortPromise]);
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 === "chromium")
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.setScreencastOptions(null);
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?.temporarilyDisableTracingScreencastThrottling();
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?.temporarilyDisableTracingScreencastThrottling();
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?.temporarilyDisableTracingScreencastThrottling();
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.setScreencastOptions(kScreencastOptions);
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) => {