playwright-core 1.58.0-alpha-2025-12-08 → 1.58.0-alpha-2025-12-09

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 CHANGED
@@ -31,9 +31,9 @@
31
31
  },
32
32
  {
33
33
  "name": "firefox",
34
- "revision": "1502",
34
+ "revision": "1504",
35
35
  "installByDefault": true,
36
- "browserVersion": "145.0.1",
36
+ "browserVersion": "145.0.2",
37
37
  "title": "Firefox"
38
38
  },
39
39
  {
@@ -45,7 +45,7 @@
45
45
  },
46
46
  {
47
47
  "name": "webkit",
48
- "revision": "2235",
48
+ "revision": "2237",
49
49
  "installByDefault": true,
50
50
  "revisionOverrides": {
51
51
  "debian11-x64": "2105",
@@ -493,6 +493,7 @@ async function prepareBrowserContextParams(platform, options) {
493
493
  network.validateHeaders(options.extraHTTPHeaders);
494
494
  const contextParams = {
495
495
  ...options,
496
+ agent: toAgentProtocol(options.agent),
496
497
  viewport: options.viewport === null ? void 0 : options.viewport,
497
498
  noDefaultViewport: options.viewport === null,
498
499
  extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
@@ -515,6 +516,12 @@ async function prepareBrowserContextParams(platform, options) {
515
516
  contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir);
516
517
  return contextParams;
517
518
  }
519
+ function toAgentProtocol(agent) {
520
+ if (!agent)
521
+ return void 0;
522
+ const secrets = agent.secrets ? Object.entries(agent.secrets).map(([name, value]) => ({ name, value })) : void 0;
523
+ return { ...agent, secrets };
524
+ }
518
525
  function toAcceptDownloadsProtocol(acceptDownloads) {
519
526
  if (acceptDownloads === void 0)
520
527
  return void 0;
@@ -612,7 +612,8 @@ import_validatorPrimitives.scheme.BrowserTypeLaunchPersistentContextParams = (0,
612
612
  provider: import_validatorPrimitives.tString,
613
613
  model: import_validatorPrimitives.tString,
614
614
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
615
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"]))
615
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
616
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
616
617
  })),
617
618
  userDataDir: import_validatorPrimitives.tString,
618
619
  slowMo: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tFloat)
@@ -710,7 +711,8 @@ import_validatorPrimitives.scheme.BrowserNewContextParams = (0, import_validator
710
711
  provider: import_validatorPrimitives.tString,
711
712
  model: import_validatorPrimitives.tString,
712
713
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
713
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"]))
714
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
715
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
714
716
  })),
715
717
  proxy: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
716
718
  server: import_validatorPrimitives.tString,
@@ -787,7 +789,8 @@ import_validatorPrimitives.scheme.BrowserNewContextForReuseParams = (0, import_v
787
789
  provider: import_validatorPrimitives.tString,
788
790
  model: import_validatorPrimitives.tString,
789
791
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
790
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"]))
792
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
793
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
791
794
  })),
792
795
  proxy: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
793
796
  server: import_validatorPrimitives.tString,
@@ -909,7 +912,8 @@ import_validatorPrimitives.scheme.BrowserContextInitializer = (0, import_validat
909
912
  provider: import_validatorPrimitives.tString,
910
913
  model: import_validatorPrimitives.tString,
911
914
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
912
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"]))
915
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
916
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
913
917
  }))
914
918
  })
915
919
  });
@@ -2818,7 +2822,8 @@ import_validatorPrimitives.scheme.AndroidDeviceLaunchBrowserParams = (0, import_
2818
2822
  provider: import_validatorPrimitives.tString,
2819
2823
  model: import_validatorPrimitives.tString,
2820
2824
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
2821
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"]))
2825
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
2826
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
2822
2827
  })),
2823
2828
  pkg: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
2824
2829
  args: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)(import_validatorPrimitives.tString)),
@@ -21,7 +21,7 @@ __export(actionRunner_exports, {
21
21
  runAction: () => runAction
22
22
  });
23
23
  module.exports = __toCommonJS(actionRunner_exports);
24
- async function runAction(progress, page, action) {
24
+ async function runAction(progress, page, action, secrets) {
25
25
  const frame = page.mainFrame();
26
26
  switch (action.method) {
27
27
  case "click":
@@ -34,21 +34,31 @@ async function runAction(progress, page, action) {
34
34
  await frame.hover(progress, action.selector, { ...action.options, ...strictTrue });
35
35
  break;
36
36
  case "selectOption":
37
- await frame.selectOption(progress, action.selector, [], action.values.map((a) => ({ value: a })), { ...strictTrue });
37
+ await frame.selectOption(progress, action.selector, [], action.labels.map((a) => ({ label: a })), { ...strictTrue });
38
38
  break;
39
39
  case "pressKey":
40
40
  await page.keyboard.press(progress, action.key);
41
41
  break;
42
- case "pressSequentially":
43
- await frame.type(progress, action.selector, action.text, { ...strictTrue });
42
+ case "pressSequentially": {
43
+ const secret = secrets?.find((s) => s.name === action.text)?.value ?? action.text;
44
+ await frame.type(progress, action.selector, secret, { ...strictTrue });
44
45
  if (action.submit)
45
46
  await page.keyboard.press(progress, "Enter");
46
47
  break;
47
- case "fill":
48
- await frame.fill(progress, action.selector, action.text, { ...strictTrue });
48
+ }
49
+ case "fill": {
50
+ const secret = secrets?.find((s) => s.name === action.text)?.value ?? action.text;
51
+ await frame.fill(progress, action.selector, secret, { ...strictTrue });
49
52
  if (action.submit)
50
53
  await page.keyboard.press(progress, "Enter");
51
54
  break;
55
+ }
56
+ case "setChecked":
57
+ if (action.checked)
58
+ await frame.check(progress, action.selector, { ...strictTrue });
59
+ else
60
+ await frame.uncheck(progress, action.selector, { ...strictTrue });
61
+ break;
52
62
  }
53
63
  }
54
64
  const strictTrue = { strict: true };
@@ -83,23 +83,22 @@ ${full}
83
83
  }
84
84
  const allCaches = /* @__PURE__ */ new Map();
85
85
  async function cachedPerform(context, options) {
86
- const agentSettings = context.page.browserContext._options.agent;
87
- if (!agentSettings?.cacheFile || agentSettings.cacheMode === "ignore")
86
+ if (!context.options?.cacheFile || context.options.cacheMode === "ignore")
88
87
  return false;
89
- const cache = await cachedActions(agentSettings.cacheFile);
88
+ const cache = await cachedActions(context.options.cacheFile);
90
89
  const cacheKey = options.key ?? options.task;
91
90
  const actions = cache[cacheKey];
92
91
  if (!actions) {
93
- if (agentSettings.cacheMode === "force")
92
+ if (context.options.cacheMode === "force")
94
93
  throw new Error(`No cached actions for key "${cacheKey}", but cache mode is set to "force"`);
95
94
  return false;
96
95
  }
97
96
  for (const action of actions)
98
- await (0, import_actionRunner.runAction)(context.progress, context.page, action);
97
+ await (0, import_actionRunner.runAction)(context.progress, context.page, action, context.options.secrets ?? []);
99
98
  return true;
100
99
  }
101
100
  async function updateCache(context, options) {
102
- const cacheFile = context.page.browserContext._options.agent?.cacheFile;
101
+ const cacheFile = context.options?.cacheFile;
103
102
  if (!cacheFile)
104
103
  return;
105
104
  const cache = await cachedActions(cacheFile);
@@ -28,10 +28,18 @@ class Context {
28
28
  this.actions = [];
29
29
  this.progress = progress;
30
30
  this.page = page;
31
+ this.options = page.browserContext._options.agent;
31
32
  }
32
- async runAction(action) {
33
- await this.waitForCompletion(() => (0, import_actionRunner.runAction)(this.progress, this.page, action));
34
- this.actions.push(action);
33
+ async runActionAndWait(action) {
34
+ return await this.runActionsAndWait([action]);
35
+ }
36
+ async runActionsAndWait(action) {
37
+ await this.waitForCompletion(async () => {
38
+ for (const a of action) {
39
+ await (0, import_actionRunner.runAction)(this.progress, this.page, a, this.options?.secrets ?? []);
40
+ this.actions.push(a);
41
+ }
42
+ });
35
43
  return await this.snapshotResult();
36
44
  }
37
45
  async waitForCompletion(callback) {
@@ -40,6 +48,7 @@ class Context {
40
48
  const disposeListeners = () => {
41
49
  this.page.browserContext.off(import_browserContext.BrowserContext.Events.Request, requestListener);
42
50
  };
51
+ this.page.browserContext.on(import_browserContext.BrowserContext.Events.Request, requestListener);
43
52
  let result;
44
53
  try {
45
54
  result = await callback();
@@ -49,20 +58,24 @@ class Context {
49
58
  }
50
59
  const requestedNavigation = requests.some((request) => request.isNavigationRequest());
51
60
  if (requestedNavigation) {
52
- await this.page.performActionPreChecks(this.progress);
61
+ await this.page.mainFrame().waitForLoadState(this.progress, "load");
53
62
  return result;
54
63
  }
55
- const fiveSeconds = new Promise((resolve) => setTimeout(resolve, 1e3));
64
+ const promises = [];
56
65
  for (const request of requests) {
57
- if (request.failure())
58
- continue;
59
- const response = Promise.race([request.response(), fiveSeconds]);
60
- await this.progress.race(response);
66
+ if (["document", "stylesheet", "script", "xhr", "fetch"].includes(request.resourceType()))
67
+ promises.push(request.response().then((r) => r?.finished()));
68
+ else
69
+ promises.push(request.response());
61
70
  }
71
+ await this.progress.race(promises, { timeout: 5e3 });
72
+ if (requests.length)
73
+ await this.progress.wait(500);
62
74
  return result;
63
75
  }
64
76
  async snapshotResult() {
65
- const { full } = await this.page.snapshotForAI(this.progress);
77
+ let { full } = await this.page.snapshotForAI(this.progress);
78
+ full = this._redactText(full);
66
79
  const text = [`# Page snapshot
67
80
  ${full}`];
68
81
  return {
@@ -84,6 +97,17 @@ ${full}`];
84
97
  }
85
98
  }));
86
99
  }
100
+ _redactText(text) {
101
+ const secrets = this.options?.secrets;
102
+ if (!secrets)
103
+ return text;
104
+ const redactText = (text2) => {
105
+ for (const { name, value } of secrets)
106
+ text2 = text2.replaceAll(value, `<secret>${name}</secret>`);
107
+ return text2;
108
+ };
109
+ return redactText(text);
110
+ }
87
111
  }
88
112
  // Annotate the CommonJS export names for ESM import in node:
89
113
  0 && (module.exports = {
@@ -54,7 +54,7 @@ const click = defineTool({
54
54
  },
55
55
  handle: async (context, params) => {
56
56
  const [selector] = await context.refSelectors([params]);
57
- return await context.runAction({
57
+ return await context.runActionAndWait({
58
58
  method: "click",
59
59
  selector,
60
60
  options: {
@@ -82,7 +82,7 @@ const drag = defineTool({
82
82
  { ref: params.startRef, element: params.startElement },
83
83
  { ref: params.endRef, element: params.endElement }
84
84
  ]);
85
- return await context.runAction({
85
+ return await context.runActionAndWait({
86
86
  method: "drag",
87
87
  sourceSelector,
88
88
  targetSelector
@@ -101,7 +101,7 @@ const hover = defineTool({
101
101
  },
102
102
  handle: async (context, params) => {
103
103
  const [selector] = await context.refSelectors([params]);
104
- return await context.runAction({
104
+ return await context.runActionAndWait({
105
105
  method: "hover",
106
106
  selector,
107
107
  options: {
@@ -122,10 +122,10 @@ const selectOption = defineTool({
122
122
  },
123
123
  handle: async (context, params) => {
124
124
  const [selector] = await context.refSelectors([params]);
125
- return await context.runAction({
125
+ return await context.runActionAndWait({
126
126
  method: "selectOption",
127
127
  selector,
128
- values: params.values
128
+ labels: params.values
129
129
  });
130
130
  }
131
131
  });
@@ -139,7 +139,7 @@ const pressKey = defineTool({
139
139
  })
140
140
  },
141
141
  handle: async (context, params) => {
142
- return await context.runAction({
142
+ return await context.runActionAndWait({
143
143
  method: "pressKey",
144
144
  key: params.key
145
145
  });
@@ -160,14 +160,14 @@ const type = defineTool({
160
160
  handle: async (context, params) => {
161
161
  const [selector] = await context.refSelectors([params]);
162
162
  if (params.slowly) {
163
- return await context.runAction({
163
+ return await context.runActionAndWait({
164
164
  method: "pressSequentially",
165
165
  selector,
166
166
  text: params.text,
167
167
  submit: params.submit
168
168
  });
169
169
  } else {
170
- return await context.runAction({
170
+ return await context.runActionAndWait({
171
171
  method: "fill",
172
172
  selector,
173
173
  text: params.text,
@@ -176,6 +176,47 @@ const type = defineTool({
176
176
  }
177
177
  }
178
178
  });
179
+ const fillForm = defineTool({
180
+ schema: {
181
+ name: "browser_fill_form",
182
+ title: "Fill form",
183
+ description: "Fill multiple form fields",
184
+ inputSchema: import_mcpBundle.z.object({
185
+ fields: import_mcpBundle.z.array(import_mcpBundle.z.object({
186
+ name: import_mcpBundle.z.string().describe("Human-readable field name"),
187
+ type: import_mcpBundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"),
188
+ ref: import_mcpBundle.z.string().describe("Exact target field reference from the page snapshot"),
189
+ value: import_mcpBundle.z.string().describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.")
190
+ })).describe("Fields to fill in")
191
+ })
192
+ },
193
+ handle: async (context, params) => {
194
+ const actions = [];
195
+ for (const field of params.fields) {
196
+ const [selector] = await context.refSelectors([{ ref: field.ref, element: field.name }]);
197
+ if (field.type === "textbox" || field.type === "slider") {
198
+ actions.push({
199
+ method: "fill",
200
+ selector,
201
+ text: field.value
202
+ });
203
+ } else if (field.type === "checkbox" || field.type === "radio") {
204
+ actions.push({
205
+ method: "setChecked",
206
+ selector,
207
+ checked: field.value === "true"
208
+ });
209
+ } else if (field.type === "combobox") {
210
+ actions.push({
211
+ method: "selectOption",
212
+ selector,
213
+ labels: [field.value]
214
+ });
215
+ }
216
+ }
217
+ return await context.runActionsAndWait(actions);
218
+ }
219
+ });
179
220
  var tools_default = [
180
221
  snapshot,
181
222
  click,
@@ -183,5 +224,6 @@ var tools_default = [
183
224
  hover,
184
225
  selectOption,
185
226
  pressKey,
186
- type
227
+ type,
228
+ fillForm
187
229
  ];
@@ -342,7 +342,7 @@ function resourceTypeFromBidi(requestDestination, requestInitiatorType, eventIni
342
342
  case "image":
343
343
  return "image";
344
344
  case "object":
345
- return "object";
345
+ return "other";
346
346
  case "paintworklet":
347
347
  return "script";
348
348
  case "script":
@@ -482,7 +482,9 @@ ${params.stackTrace?.callFrames.map((f) => {
482
482
  throw e;
483
483
  });
484
484
  }
485
- async setScreencastOptions(options) {
485
+ async startScreencast(options) {
486
+ }
487
+ async stopScreencast() {
486
488
  }
487
489
  rafCountForStablePosition() {
488
490
  return 1;
@@ -322,7 +322,7 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
322
322
  const pageOrError = await progress.race(page.waitForInitializedOrError());
323
323
  if (pageOrError instanceof Error)
324
324
  throw pageOrError;
325
- await page.mainFrame()._waitForLoadState(progress, "load");
325
+ await page.mainFrame().waitForLoadState(progress, "load");
326
326
  return page;
327
327
  }
328
328
  async _loadDefaultContext(progress) {
@@ -22,8 +22,6 @@ __export(chromiumSwitches_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(chromiumSwitches_exports);
24
24
  const disabledFeatures = (assistantMode) => [
25
- // See https://github.com/microsoft/playwright/pull/10380
26
- "AcceptCHFrame",
27
25
  // See https://github.com/microsoft/playwright/issues/14047
28
26
  "AvoidUnnecessaryBeforeUnloadCheckSync",
29
27
  "DestroyProfileOnBrowserClose",
@@ -470,7 +470,7 @@ class CRBrowserContext extends import_browserContext.BrowserContext {
470
470
  }
471
471
  }
472
472
  async stopVideoRecording() {
473
- await Promise.all(this._crPages().map((crPage) => crPage._mainFrameSession._stopVideoRecording()));
473
+ await Promise.all(this._crPages().map((crPage) => crPage._page.screencast.stopVideoRecording()));
474
474
  }
475
475
  onClosePersistent() {
476
476
  }
@@ -482,12 +482,11 @@ class InterceptableRequest {
482
482
  url,
483
483
  postDataEntries = null
484
484
  } = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request;
485
- const type = (requestWillBeSentEvent.type || "").toLowerCase();
486
485
  let postDataBuffer = null;
487
486
  const entries = postDataEntries?.filter((entry) => entry.bytes);
488
487
  if (entries && entries.length)
489
488
  postDataBuffer = Buffer.concat(entries.map((entry) => Buffer.from(entry.bytes, "base64")));
490
- this.request = new network.Request(context, frame, serviceWorker, redirectedFrom?.request || null, documentId, url, type, method, postDataBuffer, headersOverride || (0, import_utils.headersObjectToArray)(headers));
489
+ this.request = new network.Request(context, frame, serviceWorker, redirectedFrom?.request || null, documentId, url, toResourceType(requestWillBeSentEvent.type || "Other"), method, postDataBuffer, headersOverride || (0, import_utils.headersObjectToArray)(headers));
491
490
  }
492
491
  }
493
492
  class RouteImpl {
@@ -660,6 +659,44 @@ class ResponseExtraInfoTracker {
660
659
  this._requests.delete(requestId);
661
660
  }
662
661
  }
662
+ function toResourceType(type) {
663
+ switch (type) {
664
+ case "Document":
665
+ return "document";
666
+ case "Stylesheet":
667
+ return "stylesheet";
668
+ case "Image":
669
+ return "image";
670
+ case "Media":
671
+ return "media";
672
+ case "Font":
673
+ return "font";
674
+ case "Script":
675
+ return "script";
676
+ case "TextTrack":
677
+ return "texttrack";
678
+ case "XHR":
679
+ return "xhr";
680
+ case "Fetch":
681
+ return "fetch";
682
+ case "EventSource":
683
+ return "eventsource";
684
+ case "WebSocket":
685
+ return "websocket";
686
+ case "Manifest":
687
+ return "manifest";
688
+ case "Ping":
689
+ return "ping";
690
+ case "CSPViolationReport":
691
+ return "cspreport";
692
+ case "Prefetch":
693
+ case "SignedExchange":
694
+ case "Preflight":
695
+ case "FedCM":
696
+ default:
697
+ return "other";
698
+ }
699
+ }
663
700
  // Annotate the CommonJS export names for ESM import in node:
664
701
  0 && (module.exports = {
665
702
  CRNetworkManager
@@ -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