playwright-core 1.58.0-alpha-2025-12-07 → 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 +3 -3
- package/lib/client/browserContext.js +7 -0
- package/lib/client/page.js +4 -3
- package/lib/client/platform.js +3 -0
- package/lib/protocol/validator.js +29 -10
- package/lib/server/agent/actionRunner.js +68 -0
- package/lib/server/agent/actions.js +16 -0
- package/lib/server/agent/agent.js +122 -0
- package/lib/server/agent/backend.js +77 -0
- package/lib/server/agent/context.js +115 -0
- package/lib/server/agent/tools.js +229 -0
- package/lib/server/bidi/bidiNetworkManager.js +1 -1
- package/lib/server/bidi/bidiPage.js +3 -1
- package/lib/server/browserContext.js +2 -2
- package/lib/server/chromium/chromium.js +1 -3
- 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/dispatchers/browserDispatcher.js +1 -0
- package/lib/server/dispatchers/pageDispatcher.js +7 -0
- 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 +5 -65
- 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/nodePlatform.js +2 -0
- 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/utils/isomorphic/protocolMetainfo.js +2 -0
- package/lib/vite/traceViewer/assets/{codeMirrorModule-JphNwIdM.js → codeMirrorModule-CPNe-I5g.js} +1 -1
- package/lib/vite/traceViewer/assets/{defaultSettingsView-C7iHYOsw.js → defaultSettingsView-V7hnXDpz.js} +3 -3
- package/lib/vite/traceViewer/{index.C5w1selH.js → index.YskCIlQ-.js} +1 -1
- package/lib/vite/traceViewer/index.html +2 -2
- package/lib/vite/traceViewer/{uiMode.CCu4MMfA.js → uiMode.CmFFBCQb.js} +1 -1
- package/lib/vite/traceViewer/uiMode.html +2 -2
- package/package.json +1 -1
- package/types/types.d.ts +10 -5
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var tools_exports = {};
|
|
20
|
+
__export(tools_exports, {
|
|
21
|
+
default: () => tools_default
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(tools_exports);
|
|
24
|
+
var import_mcpBundle = require("../../mcpBundle");
|
|
25
|
+
function defineTool(tool) {
|
|
26
|
+
return tool;
|
|
27
|
+
}
|
|
28
|
+
const snapshot = defineTool({
|
|
29
|
+
schema: {
|
|
30
|
+
name: "browser_snapshot",
|
|
31
|
+
title: "Page snapshot",
|
|
32
|
+
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
|
|
33
|
+
inputSchema: import_mcpBundle.z.object({})
|
|
34
|
+
},
|
|
35
|
+
handle: async (context, params) => {
|
|
36
|
+
return await context.snapshotResult();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const elementSchema = import_mcpBundle.z.object({
|
|
40
|
+
element: import_mcpBundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element"),
|
|
41
|
+
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
|
42
|
+
});
|
|
43
|
+
const clickSchema = elementSchema.extend({
|
|
44
|
+
doubleClick: import_mcpBundle.z.boolean().optional().describe("Whether to perform a double click instead of a single click"),
|
|
45
|
+
button: import_mcpBundle.z.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
|
|
46
|
+
modifiers: import_mcpBundle.z.array(import_mcpBundle.z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
|
|
47
|
+
});
|
|
48
|
+
const click = defineTool({
|
|
49
|
+
schema: {
|
|
50
|
+
name: "browser_click",
|
|
51
|
+
title: "Click",
|
|
52
|
+
description: "Perform click on a web page",
|
|
53
|
+
inputSchema: clickSchema
|
|
54
|
+
},
|
|
55
|
+
handle: async (context, params) => {
|
|
56
|
+
const [selector] = await context.refSelectors([params]);
|
|
57
|
+
return await context.runActionAndWait({
|
|
58
|
+
method: "click",
|
|
59
|
+
selector,
|
|
60
|
+
options: {
|
|
61
|
+
button: params.button,
|
|
62
|
+
modifiers: params.modifiers,
|
|
63
|
+
clickCount: params.doubleClick ? 2 : void 0
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const drag = defineTool({
|
|
69
|
+
schema: {
|
|
70
|
+
name: "browser_drag",
|
|
71
|
+
title: "Drag mouse",
|
|
72
|
+
description: "Perform drag and drop between two elements",
|
|
73
|
+
inputSchema: import_mcpBundle.z.object({
|
|
74
|
+
startElement: import_mcpBundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
|
|
75
|
+
startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
|
|
76
|
+
endElement: import_mcpBundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
|
|
77
|
+
endRef: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
|
78
|
+
})
|
|
79
|
+
},
|
|
80
|
+
handle: async (context, params) => {
|
|
81
|
+
const [sourceSelector, targetSelector] = await context.refSelectors([
|
|
82
|
+
{ ref: params.startRef, element: params.startElement },
|
|
83
|
+
{ ref: params.endRef, element: params.endElement }
|
|
84
|
+
]);
|
|
85
|
+
return await context.runActionAndWait({
|
|
86
|
+
method: "drag",
|
|
87
|
+
sourceSelector,
|
|
88
|
+
targetSelector
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const hoverSchema = elementSchema.extend({
|
|
93
|
+
modifiers: import_mcpBundle.z.array(import_mcpBundle.z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
|
|
94
|
+
});
|
|
95
|
+
const hover = defineTool({
|
|
96
|
+
schema: {
|
|
97
|
+
name: "browser_hover",
|
|
98
|
+
title: "Hover mouse",
|
|
99
|
+
description: "Hover over element on page",
|
|
100
|
+
inputSchema: hoverSchema
|
|
101
|
+
},
|
|
102
|
+
handle: async (context, params) => {
|
|
103
|
+
const [selector] = await context.refSelectors([params]);
|
|
104
|
+
return await context.runActionAndWait({
|
|
105
|
+
method: "hover",
|
|
106
|
+
selector,
|
|
107
|
+
options: {
|
|
108
|
+
modifiers: params.modifiers
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const selectOptionSchema = elementSchema.extend({
|
|
114
|
+
values: import_mcpBundle.z.array(import_mcpBundle.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
|
|
115
|
+
});
|
|
116
|
+
const selectOption = defineTool({
|
|
117
|
+
schema: {
|
|
118
|
+
name: "browser_select_option",
|
|
119
|
+
title: "Select option",
|
|
120
|
+
description: "Select an option in a dropdown",
|
|
121
|
+
inputSchema: selectOptionSchema
|
|
122
|
+
},
|
|
123
|
+
handle: async (context, params) => {
|
|
124
|
+
const [selector] = await context.refSelectors([params]);
|
|
125
|
+
return await context.runActionAndWait({
|
|
126
|
+
method: "selectOption",
|
|
127
|
+
selector,
|
|
128
|
+
labels: params.values
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const pressKey = defineTool({
|
|
133
|
+
schema: {
|
|
134
|
+
name: "browser_press_key",
|
|
135
|
+
title: "Press a key",
|
|
136
|
+
description: "Press a key on the keyboard",
|
|
137
|
+
inputSchema: import_mcpBundle.z.object({
|
|
138
|
+
key: import_mcpBundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
handle: async (context, params) => {
|
|
142
|
+
return await context.runActionAndWait({
|
|
143
|
+
method: "pressKey",
|
|
144
|
+
key: params.key
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const typeSchema = elementSchema.extend({
|
|
149
|
+
text: import_mcpBundle.z.string().describe("Text to type into the element"),
|
|
150
|
+
submit: import_mcpBundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)"),
|
|
151
|
+
slowly: import_mcpBundle.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
|
|
152
|
+
});
|
|
153
|
+
const type = defineTool({
|
|
154
|
+
schema: {
|
|
155
|
+
name: "browser_type",
|
|
156
|
+
title: "Type text",
|
|
157
|
+
description: "Type text into editable element",
|
|
158
|
+
inputSchema: typeSchema
|
|
159
|
+
},
|
|
160
|
+
handle: async (context, params) => {
|
|
161
|
+
const [selector] = await context.refSelectors([params]);
|
|
162
|
+
if (params.slowly) {
|
|
163
|
+
return await context.runActionAndWait({
|
|
164
|
+
method: "pressSequentially",
|
|
165
|
+
selector,
|
|
166
|
+
text: params.text,
|
|
167
|
+
submit: params.submit
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
return await context.runActionAndWait({
|
|
171
|
+
method: "fill",
|
|
172
|
+
selector,
|
|
173
|
+
text: params.text,
|
|
174
|
+
submit: params.submit
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
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
|
+
});
|
|
220
|
+
var tools_default = [
|
|
221
|
+
snapshot,
|
|
222
|
+
click,
|
|
223
|
+
drag,
|
|
224
|
+
hover,
|
|
225
|
+
selectOption,
|
|
226
|
+
pressKey,
|
|
227
|
+
type,
|
|
228
|
+
fillForm
|
|
229
|
+
];
|
|
@@ -145,7 +145,7 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
|
|
|
145
145
|
return true;
|
|
146
146
|
}
|
|
147
147
|
static reusableContextHash(params) {
|
|
148
|
-
const paramsCopy = { ...params
|
|
148
|
+
const paramsCopy = { ...params };
|
|
149
149
|
if (paramsCopy.selectorEngines?.length === 0)
|
|
150
150
|
delete paramsCopy.selectorEngines;
|
|
151
151
|
for (const k of Object.keys(paramsCopy)) {
|
|
@@ -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().
|
|
325
|
+
await page.mainFrame().waitForLoadState(progress, "load");
|
|
326
326
|
return page;
|
|
327
327
|
}
|
|
328
328
|
async _loadDefaultContext(progress) {
|
|
@@ -279,9 +279,7 @@ class Chromium extends import_browserType.BrowserType {
|
|
|
279
279
|
if (args.find((arg) => !arg.startsWith("-")))
|
|
280
280
|
throw new Error("Arguments can not specify page to be opened");
|
|
281
281
|
const chromeArguments = [...(0, import_chromiumSwitches.chromiumSwitches)(options.assistantMode, options.channel)];
|
|
282
|
-
|
|
283
|
-
chromeArguments.push("--enable-unsafe-swiftshader");
|
|
284
|
-
}
|
|
282
|
+
chromeArguments.push("--enable-unsafe-swiftshader");
|
|
285
283
|
if (options.headless) {
|
|
286
284
|
chromeArguments.push("--headless");
|
|
287
285
|
chromeArguments.push(
|
|
@@ -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.
|
|
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
|
|
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
|
|
@@ -62,6 +62,7 @@ class BrowserDispatcher extends import_dispatcher.Dispatcher {
|
|
|
62
62
|
return { context: contextDispatcher };
|
|
63
63
|
}
|
|
64
64
|
async newContextForReuse(params, progress) {
|
|
65
|
+
delete params.agent;
|
|
65
66
|
const context = await this._object.newContextForReuse(progress, params);
|
|
66
67
|
const contextDispatcher = import_browserContextDispatcher.BrowserContextDispatcher.from(this, context);
|
|
67
68
|
this._dispatchEvent("context", { context: contextDispatcher });
|
|
@@ -36,6 +36,7 @@ var import_networkDispatchers3 = require("./networkDispatchers");
|
|
|
36
36
|
var import_webSocketRouteDispatcher = require("./webSocketRouteDispatcher");
|
|
37
37
|
var import_instrumentation = require("../instrumentation");
|
|
38
38
|
var import_urlMatch = require("../../utils/isomorphic/urlMatch");
|
|
39
|
+
var import_agent = require("../agent/agent");
|
|
39
40
|
class PageDispatcher extends import_dispatcher.Dispatcher {
|
|
40
41
|
constructor(parentScope, page) {
|
|
41
42
|
const mainFrame = import_frameDispatcher.FrameDispatcher.from(parentScope, page.mainFrame());
|
|
@@ -258,6 +259,12 @@ class PageDispatcher extends import_dispatcher.Dispatcher {
|
|
|
258
259
|
const buffer = await progress.race(this._page.pdf(params));
|
|
259
260
|
return { pdf: buffer };
|
|
260
261
|
}
|
|
262
|
+
async perform(params, progress) {
|
|
263
|
+
await (0, import_agent.pagePerform)(progress, this._page, params);
|
|
264
|
+
}
|
|
265
|
+
async extract(params, progress) {
|
|
266
|
+
return { result: await (0, import_agent.pageExtract)(progress, this._page, params) };
|
|
267
|
+
}
|
|
261
268
|
async requests(params, progress) {
|
|
262
269
|
this._subscriptions.add("request");
|
|
263
270
|
return { requests: this._page.networkRequests().map((request) => import_networkDispatchers.RequestDispatcher.from(this.parentScope(), request)) };
|
|
@@ -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