patchright-core 1.56.1 → 1.58.2
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/ThirdPartyNotices.txt +3134 -560
- package/bin/install_webkit_wsl.ps1 +1 -3
- package/browsers.json +21 -22
- package/lib/cli/program.js +16 -60
- package/lib/client/api.js +3 -3
- package/lib/client/browser.js +3 -5
- package/lib/client/browserContext.js +62 -8
- package/lib/client/browserType.js +4 -3
- package/lib/client/connection.js +4 -0
- package/lib/client/consoleMessage.js +5 -1
- package/lib/client/electron.js +1 -1
- package/lib/client/elementHandle.js +3 -0
- package/lib/client/events.js +5 -1
- package/lib/client/fetch.js +3 -4
- package/lib/client/frame.js +10 -1
- package/lib/client/locator.js +12 -1
- package/lib/client/network.js +5 -1
- package/lib/client/page.js +31 -6
- package/lib/client/pageAgent.js +64 -0
- package/lib/client/platform.js +3 -0
- package/lib/client/playwright.js +1 -5
- package/lib/client/tracing.js +7 -5
- package/lib/client/worker.js +22 -0
- package/lib/generated/clockSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +1 -1
- package/lib/inProcessFactory.js +0 -2
- package/lib/mcpBundle.js +84 -0
- package/lib/mcpBundleImpl/index.js +147 -0
- package/lib/protocol/serializers.js +5 -0
- package/lib/protocol/validator.js +112 -50
- package/lib/remote/playwrightServer.js +1 -2
- package/lib/server/agent/actionRunner.js +335 -0
- package/lib/server/agent/actions.js +128 -0
- package/lib/server/agent/codegen.js +111 -0
- package/lib/server/agent/context.js +150 -0
- package/lib/server/agent/expectTools.js +156 -0
- package/lib/server/agent/pageAgent.js +204 -0
- package/lib/server/agent/performTools.js +262 -0
- package/lib/server/agent/tool.js +109 -0
- package/lib/server/android/android.js +1 -1
- package/lib/server/artifact.js +1 -1
- package/lib/server/bidi/bidiBrowser.js +81 -22
- package/lib/server/bidi/bidiChromium.js +9 -13
- package/lib/server/bidi/bidiConnection.js +1 -0
- package/lib/server/bidi/bidiDeserializer.js +116 -0
- package/lib/server/bidi/bidiExecutionContext.js +75 -29
- package/lib/server/bidi/bidiFirefox.js +7 -9
- package/lib/server/bidi/bidiNetworkManager.js +1 -1
- package/lib/server/bidi/bidiPage.js +61 -30
- package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
- package/lib/server/browserContext.js +43 -36
- package/lib/server/browserType.js +12 -4
- package/lib/server/chromium/chromium.js +26 -21
- package/lib/server/chromium/chromiumSwitches.js +12 -3
- package/lib/server/chromium/crBrowser.js +30 -12
- package/lib/server/chromium/crConnection.js +0 -5
- package/lib/server/chromium/crCoverage.js +13 -1
- package/lib/server/chromium/crDevTools.js +0 -2
- package/lib/server/chromium/crNetworkManager.js +107 -18
- package/lib/server/chromium/crPage.js +68 -124
- package/lib/server/chromium/crServiceWorker.js +14 -1
- package/lib/server/codegen/javascript.js +6 -29
- package/lib/server/console.js +5 -1
- package/lib/server/deviceDescriptorsSource.json +56 -56
- package/lib/server/dispatchers/browserContextDispatcher.js +26 -8
- package/lib/server/dispatchers/dispatcher.js +6 -13
- package/lib/server/dispatchers/frameDispatcher.js +1 -1
- package/lib/server/dispatchers/jsHandleDispatcher.js +2 -2
- package/lib/server/dispatchers/pageAgentDispatcher.js +96 -0
- package/lib/server/dispatchers/pageDispatcher.js +14 -22
- package/lib/server/dispatchers/playwrightDispatcher.js +0 -4
- package/lib/server/dom.js +12 -3
- package/lib/server/electron/electron.js +6 -3
- package/lib/server/firefox/ffBrowser.js +10 -20
- package/lib/server/firefox/ffConnection.js +0 -5
- package/lib/server/firefox/ffNetworkManager.js +2 -2
- package/lib/server/firefox/ffPage.js +18 -24
- package/lib/server/firefox/firefox.js +18 -9
- package/lib/server/frameSelectors.js +18 -8
- package/lib/server/frames.js +257 -87
- package/lib/server/input.js +7 -3
- package/lib/server/instrumentation.js +3 -0
- package/lib/server/javascript.js +8 -4
- package/lib/server/launchApp.js +2 -1
- package/lib/server/localUtils.js +4 -8
- package/lib/server/network.js +50 -12
- package/lib/server/page.js +112 -126
- package/lib/server/playwright.js +2 -4
- package/lib/server/progress.js +26 -6
- package/lib/server/recorder/recorderApp.js +80 -101
- package/lib/server/recorder.js +3 -2
- package/lib/server/registry/browserFetcher.js +6 -4
- package/lib/server/registry/index.js +278 -189
- package/lib/server/registry/oopDownloadBrowserMain.js +9 -2
- package/lib/server/screencast.js +190 -0
- package/lib/server/screenshotter.js +6 -0
- package/lib/server/socksClientCertificatesInterceptor.js +1 -1
- package/lib/server/trace/recorder/snapshotter.js +17 -8
- package/lib/server/trace/recorder/snapshotterInjected.js +30 -72
- package/lib/server/trace/recorder/tracing.js +31 -21
- package/lib/server/trace/viewer/traceParser.js +72 -0
- package/lib/server/trace/viewer/traceViewer.js +45 -40
- package/lib/server/utils/comparators.js +3 -25
- package/lib/server/utils/expectUtils.js +87 -2
- package/lib/server/utils/hostPlatform.js +30 -3
- package/lib/server/utils/httpServer.js +5 -20
- package/lib/server/utils/imageUtils.js +141 -0
- package/lib/server/utils/network.js +55 -40
- package/lib/server/utils/nodePlatform.js +6 -0
- package/lib/server/{chromium/videoRecorder.js → videoRecorder.js} +35 -24
- package/lib/server/webkit/webkit.js +5 -16
- package/lib/server/webkit/wkBrowser.js +2 -6
- package/lib/server/webkit/wkConnection.js +1 -6
- package/lib/server/webkit/wkInterceptableRequest.js +29 -1
- package/lib/server/webkit/wkPage.js +76 -51
- package/lib/server/webkit/wkWorkers.js +2 -1
- package/lib/utils/isomorphic/ariaSnapshot.js +63 -0
- package/lib/utils/isomorphic/locatorGenerators.js +24 -8
- package/lib/utils/isomorphic/lruCache.js +51 -0
- package/lib/utils/isomorphic/mimeType.js +1 -1
- package/lib/utils/isomorphic/protocolFormatter.js +3 -0
- package/lib/utils/isomorphic/protocolMetainfo.js +11 -2
- package/lib/utils/isomorphic/stringUtils.js +49 -0
- package/lib/utils/isomorphic/trace/entries.js +16 -0
- package/lib/utils/isomorphic/trace/snapshotRenderer.js +499 -0
- package/lib/utils/isomorphic/trace/snapshotServer.js +120 -0
- package/lib/utils/isomorphic/trace/snapshotStorage.js +89 -0
- package/lib/utils/isomorphic/trace/traceLoader.js +131 -0
- package/lib/utils/isomorphic/trace/traceModel.js +365 -0
- package/lib/utils/isomorphic/trace/traceModernizer.js +400 -0
- package/lib/utils/isomorphic/trace/versions/traceV3.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV4.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV5.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV6.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV7.js +16 -0
- package/lib/utils/isomorphic/trace/versions/traceV8.js +16 -0
- package/lib/utils/isomorphic/urlMatch.js +19 -5
- package/lib/utils/isomorphic/yaml.js +84 -0
- package/lib/utils.js +4 -0
- package/lib/utilsBundle.js +1 -1
- package/lib/utilsBundleImpl/index.js +124 -124
- package/lib/vite/htmlReport/index.html +21 -21
- package/lib/vite/recorder/assets/codeMirrorModule-CFUTFUO7.js +32 -0
- package/lib/vite/recorder/assets/{codeMirrorModule-C3UTv-Ge.css → codeMirrorModule-DYBRYzYX.css} +1 -1
- package/lib/vite/recorder/assets/{index-Ri0uHF7I.css → index-BSjZa4pk.css} +1 -1
- package/lib/vite/recorder/assets/index-CVkBxsGf.js +193 -0
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/traceViewer/assets/codeMirrorModule-BVA4h_ZY.js +32 -0
- package/lib/vite/traceViewer/assets/defaultSettingsView-CjfmcdOz.js +266 -0
- package/lib/vite/traceViewer/{codeMirrorModule.C3UTv-Ge.css → codeMirrorModule.DYBRYzYX.css} +1 -1
- package/lib/vite/traceViewer/defaultSettingsView.7ch9cixO.css +1 -0
- package/lib/vite/traceViewer/index.BVu7tZDe.css +1 -0
- package/lib/vite/traceViewer/index.BtyWtaE-.js +2 -0
- package/lib/vite/traceViewer/index.html +6 -6
- package/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/lib/vite/traceViewer/snapshot.html +3 -3
- package/lib/vite/traceViewer/sw.bundle.js +5 -3
- package/lib/vite/traceViewer/uiMode.fyrXARf2.js +5 -0
- package/lib/vite/traceViewer/uiMode.html +3 -3
- package/package.json +2 -1
- package/types/protocol.d.ts +939 -245
- package/types/types.d.ts +143 -153
- package/lib/client/accessibility.js +0 -49
- package/lib/server/accessibility.js +0 -69
- package/lib/server/bidi/third_party/bidiDeserializer.js +0 -98
- package/lib/server/chromium/crAccessibility.js +0 -263
- package/lib/server/firefox/ffAccessibility.js +0 -238
- package/lib/server/trace/test/inMemorySnapshotter.js +0 -87
- package/lib/server/webkit/wkAccessibility.js +0 -237
- package/lib/server/webkit/wsl/webkit-wsl-transport-client.js +0 -74
- package/lib/server/webkit/wsl/webkit-wsl-transport-server.js +0 -113
- package/lib/vite/recorder/assets/codeMirrorModule-RJCXzfmE.js +0 -24
- package/lib/vite/recorder/assets/index-Y-X2TGJv.js +0 -193
- package/lib/vite/traceViewer/assets/codeMirrorModule-rbQPefq7.js +0 -24
- package/lib/vite/traceViewer/assets/defaultSettingsView-CLbol9XR.js +0 -265
- package/lib/vite/traceViewer/defaultSettingsView.TQ8_7ybu.css +0 -1
- package/lib/vite/traceViewer/index.I8N9v4jT.css +0 -1
- package/lib/vite/traceViewer/index.zIVi6mN9.js +0 -2
- package/lib/vite/traceViewer/uiMode.B_CpmIpF.js +0 -5
|
@@ -35,22 +35,20 @@ __export(network_exports, {
|
|
|
35
35
|
createProxyAgent: () => createProxyAgent,
|
|
36
36
|
fetchData: () => fetchData,
|
|
37
37
|
httpRequest: () => httpRequest,
|
|
38
|
-
isURLAvailable: () => isURLAvailable
|
|
38
|
+
isURLAvailable: () => isURLAvailable,
|
|
39
|
+
startHttpServer: () => startHttpServer
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(network_exports);
|
|
41
42
|
var import_http = __toESM(require("http"));
|
|
42
43
|
var import_http2 = __toESM(require("http2"));
|
|
43
44
|
var import_https = __toESM(require("https"));
|
|
44
|
-
var import_url = __toESM(require("url"));
|
|
45
45
|
var import_utilsBundle = require("../../utilsBundle");
|
|
46
46
|
var import_happyEyeballs = require("./happyEyeballs");
|
|
47
47
|
var import_manualPromise = require("../../utils/isomorphic/manualPromise");
|
|
48
48
|
const NET_DEFAULT_TIMEOUT = 3e4;
|
|
49
49
|
function httpRequest(params, onResponse, onError) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
...parsedUrl,
|
|
53
|
-
agent: parsedUrl.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent,
|
|
50
|
+
let url = new URL(params.url);
|
|
51
|
+
const options = {
|
|
54
52
|
method: params.method || "GET",
|
|
55
53
|
headers: params.headers
|
|
56
54
|
};
|
|
@@ -58,21 +56,16 @@ function httpRequest(params, onResponse, onError) {
|
|
|
58
56
|
options.rejectUnauthorized = params.rejectUnauthorized;
|
|
59
57
|
const proxyURL = (0, import_utilsBundle.getProxyForUrl)(params.url);
|
|
60
58
|
if (proxyURL) {
|
|
61
|
-
const parsedProxyURL =
|
|
59
|
+
const parsedProxyURL = normalizeProxyURL(proxyURL);
|
|
62
60
|
if (params.url.startsWith("http:")) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
host: parsedProxyURL.hostname,
|
|
66
|
-
port: parsedProxyURL.port,
|
|
67
|
-
headers: options.headers,
|
|
68
|
-
method: options.method
|
|
69
|
-
};
|
|
61
|
+
parsedProxyURL.pathname = url.toString();
|
|
62
|
+
url = parsedProxyURL;
|
|
70
63
|
} else {
|
|
71
|
-
parsedProxyURL.secureProxy = parsedProxyURL.protocol === "https:";
|
|
72
64
|
options.agent = new import_utilsBundle.HttpsProxyAgent(parsedProxyURL);
|
|
73
65
|
options.rejectUnauthorized = false;
|
|
74
66
|
}
|
|
75
67
|
}
|
|
68
|
+
options.agent ??= url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent;
|
|
76
69
|
let cancelRequest;
|
|
77
70
|
const requestCallback = (res) => {
|
|
78
71
|
const statusCode = res.statusCode || 0;
|
|
@@ -83,7 +76,7 @@ function httpRequest(params, onResponse, onError) {
|
|
|
83
76
|
onResponse(res);
|
|
84
77
|
}
|
|
85
78
|
};
|
|
86
|
-
const request =
|
|
79
|
+
const request = url.protocol === "https:" ? import_https.default.request(url, options, requestCallback) : import_http.default.request(url, options, requestCallback);
|
|
87
80
|
request.on("error", onError);
|
|
88
81
|
if (params.socketTimeout !== void 0) {
|
|
89
82
|
request.setTimeout(params.socketTimeout, () => {
|
|
@@ -122,7 +115,7 @@ async function fetchData(progress, params, onError) {
|
|
|
122
115
|
throw error;
|
|
123
116
|
}
|
|
124
117
|
}
|
|
125
|
-
function shouldBypassProxy(
|
|
118
|
+
function shouldBypassProxy(url, bypass) {
|
|
126
119
|
if (!bypass)
|
|
127
120
|
return false;
|
|
128
121
|
const domains = bypass.split(",").map((s) => {
|
|
@@ -131,30 +124,36 @@ function shouldBypassProxy(url2, bypass) {
|
|
|
131
124
|
s = "." + s;
|
|
132
125
|
return s;
|
|
133
126
|
});
|
|
134
|
-
const domain = "." +
|
|
127
|
+
const domain = "." + url.hostname;
|
|
135
128
|
return domains.some((d) => domain.endsWith(d));
|
|
136
129
|
}
|
|
130
|
+
function normalizeProxyURL(proxy) {
|
|
131
|
+
proxy = proxy.trim();
|
|
132
|
+
if (!/^\w+:\/\//.test(proxy))
|
|
133
|
+
proxy = "http://" + proxy;
|
|
134
|
+
return new URL(proxy);
|
|
135
|
+
}
|
|
137
136
|
function createProxyAgent(proxy, forUrl) {
|
|
138
137
|
if (!proxy)
|
|
139
138
|
return;
|
|
140
139
|
if (forUrl && proxy.bypass && shouldBypassProxy(forUrl, proxy.bypass))
|
|
141
140
|
return;
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
const proxyURL = normalizeProxyURL(proxy.server);
|
|
142
|
+
if (proxyURL.protocol?.startsWith("socks")) {
|
|
143
|
+
if (proxyURL.protocol === "socks5:")
|
|
144
|
+
proxyURL.protocol = "socks5h:";
|
|
145
|
+
else if (proxyURL.protocol === "socks4:")
|
|
146
|
+
proxyURL.protocol = "socks4a:";
|
|
147
|
+
return new import_utilsBundle.SocksProxyAgent(proxyURL);
|
|
148
|
+
}
|
|
149
|
+
if (proxy.username) {
|
|
150
|
+
proxyURL.username = proxy.username;
|
|
151
|
+
proxyURL.password = proxy.password || "";
|
|
151
152
|
}
|
|
152
|
-
if (proxy.username)
|
|
153
|
-
proxyOpts.auth = `${proxy.username}:${proxy.password || ""}`;
|
|
154
153
|
if (forUrl && ["ws:", "wss:"].includes(forUrl.protocol)) {
|
|
155
|
-
return new import_utilsBundle.HttpsProxyAgent(
|
|
154
|
+
return new import_utilsBundle.HttpsProxyAgent(proxyURL);
|
|
156
155
|
}
|
|
157
|
-
return new import_utilsBundle.HttpsProxyAgent(
|
|
156
|
+
return new import_utilsBundle.HttpsProxyAgent(proxyURL);
|
|
158
157
|
}
|
|
159
158
|
function createHttpServer(...args) {
|
|
160
159
|
const server = import_http.default.createServer(...args);
|
|
@@ -171,20 +170,35 @@ function createHttp2Server(...args) {
|
|
|
171
170
|
decorateServer(server);
|
|
172
171
|
return server;
|
|
173
172
|
}
|
|
174
|
-
async function
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
async function startHttpServer(server, options) {
|
|
174
|
+
const { host = "localhost", port = 0 } = options;
|
|
175
|
+
const errorPromise = new import_manualPromise.ManualPromise();
|
|
176
|
+
const errorListener = (error) => errorPromise.reject(error);
|
|
177
|
+
server.on("error", errorListener);
|
|
178
|
+
try {
|
|
179
|
+
server.listen(port, host);
|
|
180
|
+
await Promise.race([
|
|
181
|
+
new Promise((cb) => server.once("listening", cb)),
|
|
182
|
+
errorPromise
|
|
183
|
+
]);
|
|
184
|
+
} finally {
|
|
185
|
+
server.removeListener("error", errorListener);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function isURLAvailable(url, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
189
|
+
let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
|
|
190
|
+
if (statusCode === 404 && url.pathname === "/") {
|
|
191
|
+
const indexUrl = new URL(url);
|
|
178
192
|
indexUrl.pathname = "/index.html";
|
|
179
193
|
statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr);
|
|
180
194
|
}
|
|
181
195
|
return statusCode >= 200 && statusCode < 404;
|
|
182
196
|
}
|
|
183
|
-
async function httpStatusCode(
|
|
197
|
+
async function httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
184
198
|
return new Promise((resolve) => {
|
|
185
|
-
onLog?.(`HTTP GET: ${
|
|
199
|
+
onLog?.(`HTTP GET: ${url}`);
|
|
186
200
|
httpRequest({
|
|
187
|
-
url:
|
|
201
|
+
url: url.toString(),
|
|
188
202
|
headers: { Accept: "*/*" },
|
|
189
203
|
rejectUnauthorized: !ignoreHTTPSErrors
|
|
190
204
|
}, (res) => {
|
|
@@ -195,7 +209,7 @@ async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
|
195
209
|
}, (error) => {
|
|
196
210
|
if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT")
|
|
197
211
|
onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`);
|
|
198
|
-
onLog?.(`Error while checking if ${
|
|
212
|
+
onLog?.(`Error while checking if ${url} is available: ${error.message}`);
|
|
199
213
|
resolve(0);
|
|
200
214
|
});
|
|
201
215
|
});
|
|
@@ -223,5 +237,6 @@ function decorateServer(server) {
|
|
|
223
237
|
createProxyAgent,
|
|
224
238
|
fetchData,
|
|
225
239
|
httpRequest,
|
|
226
|
-
isURLAvailable
|
|
240
|
+
isURLAvailable,
|
|
241
|
+
startHttpServer
|
|
227
242
|
});
|
|
@@ -42,6 +42,7 @@ var import_utilsBundle = require("../../utilsBundle");
|
|
|
42
42
|
var import_debugLogger = require("./debugLogger");
|
|
43
43
|
var import_zones = require("./zones");
|
|
44
44
|
var import_debug = require("./debug");
|
|
45
|
+
var import_mcpBundle = require("../../mcpBundle");
|
|
45
46
|
const pipelineAsync = util.promisify(import_stream.pipeline);
|
|
46
47
|
class NodeZone {
|
|
47
48
|
constructor(zone) {
|
|
@@ -105,6 +106,11 @@ const nodePlatform = {
|
|
|
105
106
|
streamWritable: (channel) => {
|
|
106
107
|
return new WritableStreamImpl(channel);
|
|
107
108
|
},
|
|
109
|
+
zodToJsonSchema: (schema) => {
|
|
110
|
+
if ("_zod" in schema)
|
|
111
|
+
return import_mcpBundle.z.toJSONSchema(schema);
|
|
112
|
+
return (0, import_mcpBundle.zodToJsonSchema)(schema);
|
|
113
|
+
},
|
|
108
114
|
zones: {
|
|
109
115
|
current: () => new NodeZone((0, import_zones.currentZone)()),
|
|
110
116
|
empty: new NodeZone(import_zones.emptyZone)
|
|
@@ -21,31 +21,26 @@ __export(videoRecorder_exports, {
|
|
|
21
21
|
VideoRecorder: () => VideoRecorder
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(videoRecorder_exports);
|
|
24
|
-
var import_utils = require("
|
|
25
|
-
var
|
|
26
|
-
var import_processLauncher = require("../utils/processLauncher");
|
|
24
|
+
var import_utils = require("../utils");
|
|
25
|
+
var import_processLauncher = require("./utils/processLauncher");
|
|
27
26
|
const fps = 25;
|
|
28
27
|
class VideoRecorder {
|
|
29
|
-
constructor(
|
|
28
|
+
constructor(ffmpegPath, options) {
|
|
30
29
|
this._process = null;
|
|
31
30
|
this._gracefullyClose = null;
|
|
32
31
|
this._lastWritePromise = Promise.resolve();
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
32
|
+
this._firstFrameTimestamp = 0;
|
|
33
|
+
this._lastFrame = null;
|
|
34
|
+
this._lastWriteNodeTime = 0;
|
|
36
35
|
this._frameQueue = [];
|
|
37
36
|
this._isStopped = false;
|
|
38
37
|
this._ffmpegPath = ffmpegPath;
|
|
39
|
-
page.on(import_page.Page.Events.ScreencastFrame, (frame) => this.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3));
|
|
40
|
-
}
|
|
41
|
-
static async launch(page, ffmpegPath, options) {
|
|
42
38
|
if (!options.outputFile.endsWith(".webm"))
|
|
43
39
|
throw new Error("File must have .webm extension");
|
|
44
|
-
|
|
45
|
-
await recorder._launch(options);
|
|
46
|
-
return recorder;
|
|
40
|
+
this._launchPromise = this._launch(options).catch((e) => e);
|
|
47
41
|
}
|
|
48
42
|
async _launch(options) {
|
|
43
|
+
await (0, import_utils.mkdirIfNeeded)(options.outputFile);
|
|
49
44
|
const w = options.width;
|
|
50
45
|
const h = options.height;
|
|
51
46
|
const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" ");
|
|
@@ -74,19 +69,27 @@ class VideoRecorder {
|
|
|
74
69
|
this._gracefullyClose = gracefullyClose;
|
|
75
70
|
}
|
|
76
71
|
writeFrame(frame, timestamp) {
|
|
72
|
+
this._launchPromise.then((error) => {
|
|
73
|
+
if (error)
|
|
74
|
+
return;
|
|
75
|
+
this._writeFrame(frame, timestamp);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
_writeFrame(frame, timestamp) {
|
|
77
79
|
(0, import_utils.assert)(this._process);
|
|
78
80
|
if (this._isStopped)
|
|
79
81
|
return;
|
|
80
|
-
if (this.
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
if (!this._firstFrameTimestamp)
|
|
83
|
+
this._firstFrameTimestamp = timestamp;
|
|
84
|
+
const frameNumber = Math.floor((timestamp - this._firstFrameTimestamp) * fps);
|
|
85
|
+
if (this._lastFrame) {
|
|
86
|
+
const repeatCount = frameNumber - this._lastFrame.frameNumber;
|
|
83
87
|
for (let i = 0; i < repeatCount; ++i)
|
|
84
|
-
this._frameQueue.push(this.
|
|
88
|
+
this._frameQueue.push(this._lastFrame.buffer);
|
|
85
89
|
this._lastWritePromise = this._lastWritePromise.then(() => this._sendFrames());
|
|
86
90
|
}
|
|
87
|
-
this.
|
|
88
|
-
this.
|
|
89
|
-
this._lastWriteTimestamp = (0, import_utils.monotonicTime)();
|
|
91
|
+
this._lastFrame = { buffer: frame, timestamp, frameNumber };
|
|
92
|
+
this._lastWriteNodeTime = (0, import_utils.monotonicTime)();
|
|
90
93
|
}
|
|
91
94
|
async _sendFrames() {
|
|
92
95
|
while (this._frameQueue.length)
|
|
@@ -99,12 +102,20 @@ class VideoRecorder {
|
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
async stop() {
|
|
102
|
-
|
|
105
|
+
const error = await this._launchPromise;
|
|
106
|
+
if (error)
|
|
107
|
+
throw error;
|
|
108
|
+
if (this._isStopped || !this._lastFrame)
|
|
103
109
|
return;
|
|
104
|
-
|
|
110
|
+
const addTime = Math.max(((0, import_utils.monotonicTime)() - this._lastWriteNodeTime) / 1e3, 1);
|
|
111
|
+
this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
|
|
105
112
|
this._isStopped = true;
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
try {
|
|
114
|
+
await this._lastWritePromise;
|
|
115
|
+
await this._gracefullyClose();
|
|
116
|
+
} catch (e) {
|
|
117
|
+
import_utils.debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
|
|
118
|
+
}
|
|
108
119
|
}
|
|
109
120
|
}
|
|
110
121
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -38,7 +38,6 @@ var import_ascii = require("../utils/ascii");
|
|
|
38
38
|
var import_browserType = require("../browserType");
|
|
39
39
|
var import_wkBrowser = require("../webkit/wkBrowser");
|
|
40
40
|
var import_spawnAsync = require("../utils/spawnAsync");
|
|
41
|
-
var import_registry = require("../registry");
|
|
42
41
|
class WebKit extends import_browserType.BrowserType {
|
|
43
42
|
constructor(parent) {
|
|
44
43
|
super(parent, "webkit");
|
|
@@ -49,16 +48,13 @@ class WebKit extends import_browserType.BrowserType {
|
|
|
49
48
|
amendEnvironment(env, userDataDir, isPersistent, options) {
|
|
50
49
|
return {
|
|
51
50
|
...env,
|
|
52
|
-
CURL_COOKIE_JAR_PATH: process.platform === "win32" && isPersistent ? import_path.default.join(userDataDir, "cookiejar.db") : void 0
|
|
53
|
-
WEBKIT_EXECUTABLE: options.channel === "webkit-wsl" ? import_registry.registry.findExecutable("webkit-wsl").wslExecutablePath : void 0
|
|
51
|
+
CURL_COOKIE_JAR_PATH: process.platform === "win32" && isPersistent ? import_path.default.join(userDataDir, "cookiejar.db") : void 0
|
|
54
52
|
};
|
|
55
53
|
}
|
|
56
|
-
doRewriteStartupLog(
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
error.logs = "\n" + (0, import_ascii.wrapInASCIIBox)(import_browserType.kNoXServerRunningError, 1);
|
|
61
|
-
return error;
|
|
54
|
+
doRewriteStartupLog(logs) {
|
|
55
|
+
if (logs.includes("Failed to open display") || logs.includes("cannot open display"))
|
|
56
|
+
logs = "\n" + (0, import_ascii.wrapInASCIIBox)(import_browserType.kNoXServerRunningError, 1);
|
|
57
|
+
return logs;
|
|
62
58
|
}
|
|
63
59
|
attemptToGracefullyCloseBrowser(transport) {
|
|
64
60
|
transport.send({ method: "Playwright.close", params: {}, id: import_wkConnection.kBrowserCloseMessageId });
|
|
@@ -71,13 +67,6 @@ class WebKit extends import_browserType.BrowserType {
|
|
|
71
67
|
if (args.find((arg) => !arg.startsWith("-")))
|
|
72
68
|
throw new Error("Arguments can not specify page to be opened");
|
|
73
69
|
const webkitArguments = ["--inspector-pipe"];
|
|
74
|
-
if (options.channel === "webkit-wsl") {
|
|
75
|
-
if (options.executablePath)
|
|
76
|
-
throw new Error('Cannot specify executablePath when using the "webkit-wsl" channel.');
|
|
77
|
-
webkitArguments.unshift(
|
|
78
|
-
import_path.default.join(__dirname, "wsl/webkit-wsl-transport-server.js")
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
70
|
if (process.platform === "win32" && options.channel !== "webkit-wsl")
|
|
82
71
|
webkitArguments.push("--disable-accelerated-compositing");
|
|
83
72
|
if (headless)
|
|
@@ -56,7 +56,6 @@ class WKBrowser extends import_browser.Browser {
|
|
|
56
56
|
this._browserSession.on("Playwright.downloadCreated", this._onDownloadCreated.bind(this));
|
|
57
57
|
this._browserSession.on("Playwright.downloadFilenameSuggested", this._onDownloadFilenameSuggested.bind(this));
|
|
58
58
|
this._browserSession.on("Playwright.downloadFinished", this._onDownloadFinished.bind(this));
|
|
59
|
-
this._browserSession.on("Playwright.screencastFinished", this._onScreencastFinished.bind(this));
|
|
60
59
|
this._browserSession.on(import_wkConnection.kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this));
|
|
61
60
|
}
|
|
62
61
|
static async connect(parent, transport, options) {
|
|
@@ -79,7 +78,7 @@ class WKBrowser extends import_browser.Browser {
|
|
|
79
78
|
wkPage.didClose();
|
|
80
79
|
this._wkPages.clear();
|
|
81
80
|
for (const video of this._idToVideo.values())
|
|
82
|
-
video.artifact.reportFinished(new import_errors.TargetClosedError());
|
|
81
|
+
video.artifact.reportFinished(new import_errors.TargetClosedError(this.closeReason()));
|
|
83
82
|
this._idToVideo.clear();
|
|
84
83
|
this._didClose();
|
|
85
84
|
}
|
|
@@ -131,9 +130,6 @@ class WKBrowser extends import_browser.Browser {
|
|
|
131
130
|
_onDownloadFinished(payload) {
|
|
132
131
|
this._downloadFinished(payload.uuid, payload.error);
|
|
133
132
|
}
|
|
134
|
-
_onScreencastFinished(payload) {
|
|
135
|
-
this._takeVideo(payload.screencastId)?.reportFinished();
|
|
136
|
-
}
|
|
137
133
|
_onPageProxyCreated(event) {
|
|
138
134
|
const pageProxyId = event.pageProxyId;
|
|
139
135
|
let context = null;
|
|
@@ -315,7 +311,7 @@ class WKBrowserContext extends import_browserContext.BrowserContext {
|
|
|
315
311
|
}
|
|
316
312
|
async doClose(reason) {
|
|
317
313
|
if (!this._browserContextId) {
|
|
318
|
-
await Promise.all(this._wkPages().map((wkPage) => wkPage.
|
|
314
|
+
await Promise.all(this._wkPages().map((wkPage) => wkPage._page.screencast.stopVideoRecording()));
|
|
319
315
|
await this._browser.close({ reason });
|
|
320
316
|
} else {
|
|
321
317
|
await this._browser._browserSession.send("Playwright.deleteContext", { browserContextId: this._browserContextId });
|
|
@@ -89,11 +89,6 @@ class WKSession extends import_events.EventEmitter {
|
|
|
89
89
|
this.connection = connection;
|
|
90
90
|
this.sessionId = sessionId;
|
|
91
91
|
this._rawSend = rawSend;
|
|
92
|
-
this.on = super.on;
|
|
93
|
-
this.off = super.removeListener;
|
|
94
|
-
this.addListener = super.addListener;
|
|
95
|
-
this.removeListener = super.removeListener;
|
|
96
|
-
this.once = super.once;
|
|
97
92
|
}
|
|
98
93
|
async send(method, params) {
|
|
99
94
|
if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs)
|
|
@@ -134,7 +129,7 @@ class WKSession extends import_events.EventEmitter {
|
|
|
134
129
|
callback.resolve(object.result);
|
|
135
130
|
}
|
|
136
131
|
} else if (object.id && !object.error) {
|
|
137
|
-
(0, import_utils.assert)(this.isDisposed());
|
|
132
|
+
(0, import_utils.assert)(this.isDisposed(), JSON.stringify(object));
|
|
138
133
|
} else {
|
|
139
134
|
Promise.resolve().then(() => this.emit(object.method, object.params));
|
|
140
135
|
}
|
|
@@ -54,7 +54,7 @@ class WKInterceptableRequest {
|
|
|
54
54
|
constructor(session, frame, event, redirectedFrom, documentId) {
|
|
55
55
|
this._session = session;
|
|
56
56
|
this._requestId = event.requestId;
|
|
57
|
-
const resourceType = event.type ? event.type
|
|
57
|
+
const resourceType = event.type ? toResourceType(event.type) : redirectedFrom ? redirectedFrom.request.resourceType() : "other";
|
|
58
58
|
let postDataBuffer = null;
|
|
59
59
|
this._timestamp = event.timestamp;
|
|
60
60
|
this._wallTime = event.walltime * 1e3;
|
|
@@ -162,6 +162,34 @@ function wkMillisToRoundishMillis(value) {
|
|
|
162
162
|
}
|
|
163
163
|
return (value * 1e3 | 0) / 1e3;
|
|
164
164
|
}
|
|
165
|
+
function toResourceType(type) {
|
|
166
|
+
switch (type) {
|
|
167
|
+
case "Document":
|
|
168
|
+
return "document";
|
|
169
|
+
case "StyleSheet":
|
|
170
|
+
return "stylesheet";
|
|
171
|
+
case "Image":
|
|
172
|
+
return "image";
|
|
173
|
+
case "Font":
|
|
174
|
+
return "font";
|
|
175
|
+
case "Script":
|
|
176
|
+
return "script";
|
|
177
|
+
case "XHR":
|
|
178
|
+
return "xhr";
|
|
179
|
+
case "Fetch":
|
|
180
|
+
return "fetch";
|
|
181
|
+
case "Ping":
|
|
182
|
+
return "ping";
|
|
183
|
+
case "Beacon":
|
|
184
|
+
return "beacon";
|
|
185
|
+
case "WebSocket":
|
|
186
|
+
return "websocket";
|
|
187
|
+
case "EventSource":
|
|
188
|
+
return "eventsource";
|
|
189
|
+
default:
|
|
190
|
+
return "other";
|
|
191
|
+
}
|
|
192
|
+
}
|
|
165
193
|
// Annotate the CommonJS export names for ESM import in node:
|
|
166
194
|
0 && (module.exports = {
|
|
167
195
|
WKInterceptableRequest,
|