brave-real-playwright-core 1.55.1 → 1.56.1-patch.1

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 (94) hide show
  1. package/README.md +5 -5
  2. package/advanced-stealth.js +1 -1
  3. package/lib/browserServerImpl.js +3 -6
  4. package/lib/client/browser.js +0 -10
  5. package/lib/client/browserContext.js +2 -8
  6. package/lib/client/channelOwner.js +0 -7
  7. package/lib/client/consoleMessage.js +2 -3
  8. package/lib/client/electron.js +1 -1
  9. package/lib/client/events.js +1 -0
  10. package/lib/client/network.js +3 -0
  11. package/lib/client/page.js +13 -1
  12. package/lib/generated/bindingsControllerSource.js +1 -1
  13. package/lib/generated/injectedScriptSource.js +1 -1
  14. package/lib/generated/pollingRecorderSource.js +1 -1
  15. package/lib/generated/utilityScriptSource.js +1 -1
  16. package/lib/protocol/validator.js +26 -15
  17. package/lib/remote/playwrightServer.js +12 -14
  18. package/lib/server/bidi/bidiBrowser.js +54 -10
  19. package/lib/server/bidi/bidiChromium.js +1 -1
  20. package/lib/server/bidi/bidiConnection.js +31 -6
  21. package/lib/server/bidi/bidiExecutionContext.js +4 -4
  22. package/lib/server/bidi/bidiFirefox.js +16 -1
  23. package/lib/server/bidi/bidiNetworkManager.js +82 -16
  24. package/lib/server/bidi/bidiPage.js +84 -18
  25. package/lib/server/browserType.js +3 -3
  26. package/lib/server/chromium/chromium.js +1 -1
  27. package/lib/server/chromium/chromiumSwitches.js +3 -1
  28. package/lib/server/chromium/crBrowser.js +4 -34
  29. package/lib/server/chromium/crPage.js +22 -31
  30. package/lib/server/codegen/csharp.js +19 -26
  31. package/lib/server/codegen/java.js +4 -0
  32. package/lib/server/codegen/javascript.js +3 -1
  33. package/lib/server/codegen/python.js +2 -0
  34. package/lib/server/debugController.js +8 -36
  35. package/lib/server/deviceDescriptorsSource.json +62 -62
  36. package/lib/server/dispatchers/androidDispatcher.js +17 -0
  37. package/lib/server/dispatchers/browserContextDispatcher.js +1 -15
  38. package/lib/server/dispatchers/networkDispatchers.js +6 -3
  39. package/lib/server/dispatchers/pageDispatcher.js +24 -0
  40. package/lib/server/dom.js +8 -3
  41. package/lib/server/firefox/ffPage.js +1 -2
  42. package/lib/server/firefox/firefox.js +1 -1
  43. package/lib/server/frames.js +12 -4
  44. package/lib/server/har/harTracer.js +7 -8
  45. package/lib/server/network.js +12 -0
  46. package/lib/server/page.js +39 -17
  47. package/lib/server/recorder/chat.js +2 -2
  48. package/lib/server/recorder/recorderRunner.js +4 -0
  49. package/lib/server/registry/browserFetcher.js +3 -3
  50. package/lib/server/registry/index.js +27 -0
  51. package/lib/server/trace/recorder/snapshotter.js +13 -2
  52. package/lib/server/trace/recorder/snapshotterInjected.js +3 -1
  53. package/lib/server/utils/comparators.js +2 -2
  54. package/lib/server/utils/env.js +7 -2
  55. package/lib/server/utils/wsServer.js +2 -7
  56. package/lib/server/webkit/webkit.js +24 -8
  57. package/lib/server/webkit/wkBrowser.js +7 -3
  58. package/lib/server/webkit/wkPage.js +7 -8
  59. package/lib/server/webkit/wsl/webkit-wsl-transport-client.js +74 -0
  60. package/lib/server/webkit/wsl/webkit-wsl-transport-server.js +113 -0
  61. package/lib/utils/isomorphic/ariaSnapshot.js +12 -10
  62. package/lib/utils/isomorphic/protocolMetainfo.js +3 -0
  63. package/lib/utils/isomorphic/urlMatch.js +3 -8
  64. package/lib/utilsBundle.js +3 -0
  65. package/lib/utilsBundleImpl/index.js +80 -80
  66. package/lib/vite/htmlReport/index.html +37 -28
  67. package/lib/vite/recorder/assets/codeMirrorModule-RJCXzfmE.js +24 -0
  68. package/lib/vite/recorder/assets/index-Ri0uHF7I.css +1 -0
  69. package/lib/vite/recorder/assets/index-Y-X2TGJv.js +193 -0
  70. package/lib/vite/recorder/index.html +2 -2
  71. package/lib/vite/traceViewer/assets/codeMirrorModule-eyVcHN77.js +24 -0
  72. package/lib/vite/traceViewer/assets/defaultSettingsView-w0zYjHsW.js +265 -0
  73. package/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +9 -0
  74. package/lib/vite/traceViewer/defaultSettingsView.TQ8_7ybu.css +1 -0
  75. package/lib/vite/traceViewer/index.Bx16ehp1.js +2 -0
  76. package/lib/vite/traceViewer/index.I8N9v4jT.css +1 -0
  77. package/lib/vite/traceViewer/index.html +4 -4
  78. package/lib/vite/traceViewer/sw.bundle.js +3 -3
  79. package/lib/vite/traceViewer/uiMode.Btcz36p_.css +1 -0
  80. package/lib/vite/traceViewer/uiMode.DRQ310U5.js +5 -0
  81. package/lib/vite/traceViewer/uiMode.html +4 -4
  82. package/lib/vite/traceViewer/{xtermModule.Beg8tuEN.css → xtermModule.DYP7pi_n.css} +1 -1
  83. package/package.json +9 -9
  84. package/lib/vite/recorder/assets/codeMirrorModule-DzQ0k89p.js +0 -24
  85. package/lib/vite/recorder/assets/index-CI4HQ-Zb.css +0 -1
  86. package/lib/vite/recorder/assets/index-D7C7daHH.js +0 -184
  87. package/lib/vite/traceViewer/assets/codeMirrorModule-CEFqZ5b3.js +0 -24
  88. package/lib/vite/traceViewer/assets/defaultSettingsView-BA25Usqk.js +0 -256
  89. package/lib/vite/traceViewer/assets/xtermModule-BoAIEibi.js +0 -9
  90. package/lib/vite/traceViewer/defaultSettingsView.DVJHpiGt.css +0 -1
  91. package/lib/vite/traceViewer/index.BFsek2M6.css +0 -1
  92. package/lib/vite/traceViewer/index.CheexZ4_.js +0 -2
  93. package/lib/vite/traceViewer/uiMode.BatfzHMG.css +0 -1
  94. package/lib/vite/traceViewer/uiMode.Dy4dnPNW.js +0 -5
@@ -103,6 +103,19 @@ class PageDispatcher extends import_dispatcher.Dispatcher {
103
103
  page() {
104
104
  return this._page;
105
105
  }
106
+ serializeConsoleMessage(message) {
107
+ return {
108
+ type: message.type(),
109
+ text: message.text(),
110
+ args: message.args().map((a) => {
111
+ const elementHandle = a.asElement();
112
+ if (elementHandle)
113
+ return import_elementHandlerDispatcher.ElementHandleDispatcher.from(import_frameDispatcher.FrameDispatcher.from(this.parentScope(), elementHandle._frame), elementHandle);
114
+ return import_jsHandleDispatcher.JSHandleDispatcher.fromJSHandle(this, a);
115
+ }),
116
+ location: message.location()
117
+ };
118
+ }
106
119
  async exposeBinding(params, progress) {
107
120
  const binding = await this._page.exposeBinding(progress, params.name, !!params.needsHandle, (source, ...args) => {
108
121
  if (this._disposed)
@@ -222,6 +235,13 @@ class PageDispatcher extends import_dispatcher.Dispatcher {
222
235
  async keyboardPress(params, progress) {
223
236
  await this._page.keyboard.press(progress, params.key, params);
224
237
  }
238
+ async consoleMessages(params, progress) {
239
+ this._subscriptions.add("console");
240
+ return { messages: this._page.consoleMessages().map((message) => this.serializeConsoleMessage(message)) };
241
+ }
242
+ async pageErrors(params, progress) {
243
+ return { errors: this._page.pageErrors().map((error) => (0, import_errors.serializeError)(error)) };
244
+ }
225
245
  async mouseMove(params, progress) {
226
246
  progress.metadata.point = { x: params.x, y: params.y };
227
247
  await this._page.mouse.move(progress, params.x, params.y, params);
@@ -258,6 +278,10 @@ class PageDispatcher extends import_dispatcher.Dispatcher {
258
278
  const buffer = await progress.race(this._page.pdf(params));
259
279
  return { pdf: buffer };
260
280
  }
281
+ async requests(params, progress) {
282
+ this._subscriptions.add("request");
283
+ return { requests: this._page.networkRequests().map((request) => import_networkDispatchers.RequestDispatcher.from(this.parentScope(), request)) };
284
+ }
261
285
  async snapshotForAI(params, progress) {
262
286
  return { snapshot: await this._page.snapshotForAI(progress) };
263
287
  }
package/lib/server/dom.js CHANGED
@@ -84,6 +84,7 @@ class FrameExecutionContext extends js.ExecutionContext {
84
84
  testIdAttributeName: selectorsRegistry.testIdAttributeName(),
85
85
  stableRafCount: this.frame._page.delegate.rafCountForStablePosition(),
86
86
  browserName: this.frame._page.browserContext._browser.options.name,
87
+ isUtilityWorld: this.world === "utility",
87
88
  customEngines
88
89
  };
89
90
  const source = `
@@ -635,17 +636,21 @@ class ElementHandle extends js.JSHandle {
635
636
  const result2 = await progress.race(this.evaluateInUtility(([injected, node]) => injected.elementState(node, "checked"), {}));
636
637
  if (result2 === "error:notconnected" || result2.received === "error:notconnected")
637
638
  throwElementIsNotAttached();
638
- return result2.matches;
639
+ return { matches: result2.matches, isRadio: result2.isRadio };
639
640
  };
640
641
  await this._markAsTargetElement(progress);
641
- if (await isChecked() === state)
642
+ const checkedState = await isChecked();
643
+ if (checkedState.matches === state)
642
644
  return "done";
645
+ if (!state && checkedState.isRadio)
646
+ throw new NonRecoverableDOMError("Cannot uncheck radio button. Radio buttons can only be unchecked by selecting another radio button in the same group.");
643
647
  const result = await this._click(progress, { ...options, waitAfter: "disabled" });
644
648
  if (result !== "done")
645
649
  return result;
646
650
  if (options.trial)
647
651
  return "done";
648
- if (await isChecked() !== state)
652
+ const finalState = await isChecked();
653
+ if (finalState.matches !== state)
649
654
  throw new NonRecoverableDOMError("Clicking the checkbox did not change its state");
650
655
  return "done";
651
656
  }
@@ -44,7 +44,6 @@ var import_ffInput = require("./ffInput");
44
44
  var import_ffNetworkManager = require("./ffNetworkManager");
45
45
  var import_debugLogger = require("../utils/debugLogger");
46
46
  var import_stackTrace = require("../../utils/isomorphic/stackTrace");
47
- var import_browserContext = require("../browserContext");
48
47
  var import_errors = require("../errors");
49
48
  const UTILITY_WORLD_NAME = "__playwright_utility_world__";
50
49
  class FFPage {
@@ -195,7 +194,7 @@ class FFPage {
195
194
  const error = new Error(message);
196
195
  error.stack = params.message + "\n" + params.stack.split("\n").filter(Boolean).map((a) => a.replace(/([^@]*)@(.*)/, " at $1 ($2)")).join("\n");
197
196
  error.name = name;
198
- this._page.emitOnContextOnceInitialized(import_browserContext.BrowserContext.Events.PageError, error, this._page);
197
+ this._page.addPageError(error);
199
198
  }
200
199
  _onConsole(payload) {
201
200
  const { type, args, executionContextId, location } = payload;
@@ -67,7 +67,7 @@ Workaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ?
67
67
  const message = { method: "Browser.close", params: {}, id: import_ffConnection.kBrowserCloseMessageId };
68
68
  transport.send(message);
69
69
  }
70
- defaultArgs(options, isPersistent, userDataDir) {
70
+ async defaultArgs(options, isPersistent, userDataDir) {
71
71
  const { args = [], headless } = options;
72
72
  const userDataDirArg = args.find((arg) => arg.startsWith("-profile") || arg.startsWith("--profile"));
73
73
  if (userDataDirArg)
@@ -231,6 +231,7 @@ class FrameManager {
231
231
  });
232
232
  return;
233
233
  }
234
+ this._page.addNetworkRequest(request);
234
235
  this._page.emitOnContext(import_browserContext.BrowserContext.Events.Request, request);
235
236
  if (route)
236
237
  new network.Route(request, route).handle([...this._page.requestInterceptors, ...this._page.browserContext.requestInterceptors]);
@@ -1180,11 +1181,15 @@ class Frame extends import_instrumentation.SdkObject {
1180
1181
  fixupMetadataError(result);
1181
1182
  return result;
1182
1183
  } catch (e) {
1183
- if (js.isJavaScriptErrorInEvaluate(e) || (0, import_selectorParser.isInvalidSelectorError)(e))
1184
- throw e;
1185
1184
  const result = { matches: options.isNot, log: (0, import_callLog.compressCallLog)(progress.metadata.log) };
1186
- if (lastIntermediateResult.isSet)
1185
+ if ((0, import_selectorParser.isInvalidSelectorError)(e)) {
1186
+ result.errorMessage = "Error: " + e.message;
1187
+ } else if (js.isJavaScriptErrorInEvaluate(e)) {
1188
+ result.errorMessage = e.message;
1189
+ } else if (lastIntermediateResult.isSet) {
1187
1190
  result.received = lastIntermediateResult.received;
1191
+ result.errorMessage = lastIntermediateResult.errorMessage;
1192
+ }
1188
1193
  if (e instanceof import_errors.TimeoutError)
1189
1194
  result.timedOut = true;
1190
1195
  fixupMetadataError(result);
@@ -1215,7 +1220,10 @@ class Frame extends import_instrumentation.SdkObject {
1215
1220
  if (log)
1216
1221
  progress.log(log);
1217
1222
  if (matches === options.isNot) {
1218
- lastIntermediateResult.received = missingReceived ? "<element(s) not found>" : received;
1223
+ if (missingReceived)
1224
+ lastIntermediateResult.errorMessage = "Error: element(s) not found";
1225
+ else
1226
+ lastIntermediateResult.received = received;
1219
1227
  lastIntermediateResult.isSet = true;
1220
1228
  if (!missingReceived && !Array.isArray(received))
1221
1229
  progress.log(` unexpected value "${renderUnexpectedValue(options.expression, received)}"`);
@@ -165,7 +165,7 @@ class HarTracer {
165
165
  _onAPIRequest(event) {
166
166
  if (!this._shouldIncludeEntryWithUrl(event.url.toString()))
167
167
  return;
168
- const harEntry = createHarEntry(event.method, event.url, void 0, this._options);
168
+ const harEntry = createHarEntry(void 0, event.method, event.url, void 0, this._options);
169
169
  harEntry._apiRequest = true;
170
170
  if (!this._options.omitCookies)
171
171
  harEntry.request.cookies = event.cookies;
@@ -227,9 +227,7 @@ class HarTracer {
227
227
  if (!url)
228
228
  return;
229
229
  const pageEntry = this._createPageEntryIfNeeded(page);
230
- const harEntry = createHarEntry(request.method(), url, request.frame()?.guid, this._options);
231
- if (pageEntry)
232
- harEntry.pageref = pageEntry.id;
230
+ const harEntry = createHarEntry(pageEntry?.id, request.method(), url, request.frame()?.guid, this._options);
233
231
  this._recordRequestHeadersAndCookies(harEntry, request.headers());
234
232
  harEntry.request.postData = this._postDataForRequest(request, this._options.content);
235
233
  if (!this._options.omitSizes)
@@ -522,10 +520,9 @@ class HarTracer {
522
520
  return result;
523
521
  }
524
522
  }
525
- function createHarEntry(method, url, frameref, options) {
523
+ function createHarEntry(pageRef, method, url, frameref, options) {
526
524
  const harEntry = {
527
- _frameref: options.includeTraceInfo ? frameref : void 0,
528
- _monotonicTime: options.includeTraceInfo ? (0, import_utils.monotonicTime)() : void 0,
525
+ pageref: pageRef,
529
526
  startedDateTime: (/* @__PURE__ */ new Date()).toISOString(),
530
527
  time: -1,
531
528
  request: {
@@ -558,7 +555,9 @@ function createHarEntry(method, url, frameref, options) {
558
555
  send: -1,
559
556
  wait: -1,
560
557
  receive: -1
561
- }
558
+ },
559
+ _frameref: options.includeTraceInfo ? frameref : void 0,
560
+ _monotonicTime: options.includeTraceInfo ? (0, import_utils.monotonicTime)() : void 0
562
561
  };
563
562
  return harEntry;
564
563
  }
@@ -121,6 +121,11 @@ class Request extends import_instrumentation.SdkObject {
121
121
  this._updateHeadersMap();
122
122
  this._isFavicon = url.endsWith("/favicon.ico") || !!redirectedFrom?._isFavicon;
123
123
  }
124
+ static {
125
+ this.Events = {
126
+ Response: "response"
127
+ };
128
+ }
124
129
  _setFailureText(failureText) {
125
130
  this._failureText = failureText;
126
131
  this._waitForResponsePromise.resolve(null);
@@ -172,6 +177,7 @@ class Request extends import_instrumentation.SdkObject {
172
177
  _setResponse(response) {
173
178
  this._response = response;
174
179
  this._waitForResponsePromise.resolve(response);
180
+ this.emit(Request.Events.Response, response);
175
181
  }
176
182
  _finalRequest() {
177
183
  return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
@@ -264,6 +270,8 @@ class Route extends import_instrumentation.SdkObject {
264
270
  body = "";
265
271
  isBase64 = false;
266
272
  }
273
+ } else if (!overrides.status || overrides.status < 200 || overrides.status >= 400) {
274
+ this._request._responseBodyOverride = { body, isBase64 };
267
275
  }
268
276
  const headers = [...overrides.headers || []];
269
277
  this._maybeAddCorsHeaders(headers);
@@ -416,6 +424,10 @@ class Response extends import_instrumentation.SdkObject {
416
424
  this._contentPromise = this._finishedPromise.then(async () => {
417
425
  if (this._status >= 300 && this._status <= 399)
418
426
  throw new Error("Response body is unavailable for redirect responses");
427
+ if (this._request._responseBodyOverride) {
428
+ const { body, isBase64 } = this._request._responseBodyOverride;
429
+ return Buffer.from(body, isBase64 ? "base64" : "utf-8");
430
+ }
419
431
  return this._getResponseBodyCallback();
420
432
  });
421
433
  }
@@ -60,7 +60,8 @@ class Page extends import_instrumentation.SdkObject {
60
60
  this._closedState = "open";
61
61
  this._closedPromise = new import_manualPromise.ManualPromise();
62
62
  this._initializedPromise = new import_manualPromise.ManualPromise();
63
- this._eventsToEmitAfterInitialized = [];
63
+ this._consoleMessages = [];
64
+ this._pageErrors = [];
64
65
  this._crashed = false;
65
66
  this.openScope = new import_utils.LongStandingScope();
66
67
  this._emulatedMedia = {};
@@ -73,6 +74,7 @@ class Page extends import_instrumentation.SdkObject {
73
74
  this._locatorHandlers = /* @__PURE__ */ new Map();
74
75
  this._lastLocatorHandlerUid = 0;
75
76
  this._locatorHandlerRunningCounter = 0;
77
+ this._networkRequests = [];
76
78
  // Aiming at 25 fps by default - each frame is 40ms, but we give some slack with 35ms.
77
79
  // When throttling for tracing, 200ms between frames, except for 10 frames around the action.
78
80
  this._frameThrottler = new FrameThrottler(10, 35, 200);
@@ -108,25 +110,26 @@ class Page extends import_instrumentation.SdkObject {
108
110
  Worker: "worker"
109
111
  };
110
112
  }
111
- async reportAsNew(opener, error = void 0, contextEvent = import_browserContext.BrowserContext.Events.Page) {
113
+ async reportAsNew(opener, error) {
112
114
  if (opener) {
113
115
  const openerPageOrError = await opener.waitForInitializedOrError();
114
116
  if (openerPageOrError instanceof Page && !openerPageOrError.isClosed())
115
117
  this._opener = openerPageOrError;
116
118
  }
117
- this._markInitialized(error, contextEvent);
119
+ this._markInitialized(error);
118
120
  }
119
- _markInitialized(error = void 0, contextEvent = import_browserContext.BrowserContext.Events.Page) {
121
+ _markInitialized(error = void 0) {
120
122
  if (error) {
121
123
  if (this.browserContext.isClosingOrClosed())
122
124
  return;
123
125
  this.frameManager.createDummyMainFrameIfNeeded();
124
126
  }
125
127
  this._initialized = error || this;
126
- this.emitOnContext(contextEvent, this);
127
- for (const { event, args } of this._eventsToEmitAfterInitialized)
128
- this.browserContext.emit(event, ...args);
129
- this._eventsToEmitAfterInitialized = [];
128
+ this.emitOnContext(import_browserContext.BrowserContext.Events.Page, this);
129
+ for (const pageError of this._pageErrors)
130
+ this.emitOnContext(import_browserContext.BrowserContext.Events.PageError, pageError, this);
131
+ for (const message of this._consoleMessages)
132
+ this.emitOnContext(import_browserContext.BrowserContext.Events.Console, message);
130
133
  if (this.isClosed())
131
134
  this.emit(Page.Events.Close);
132
135
  else
@@ -144,14 +147,6 @@ class Page extends import_instrumentation.SdkObject {
144
147
  return;
145
148
  this.browserContext.emit(event, ...args);
146
149
  }
147
- emitOnContextOnceInitialized(event, ...args) {
148
- if (this.isStorageStatePage)
149
- return;
150
- if (this._initialized)
151
- this.browserContext.emit(event, ...args);
152
- else
153
- this._eventsToEmitAfterInitialized.push({ event, args });
154
- }
155
150
  async resetForReuse(progress) {
156
151
  await this.mainFrame().gotoImpl(progress, "about:blank", {});
157
152
  this._emulatedSize = void 0;
@@ -246,6 +241,13 @@ class Page extends import_instrumentation.SdkObject {
246
241
  extraHTTPHeaders() {
247
242
  return this._extraHTTPHeaders;
248
243
  }
244
+ addNetworkRequest(request) {
245
+ this._networkRequests.push(request);
246
+ ensureArrayLimit(this._networkRequests, 100);
247
+ }
248
+ networkRequests() {
249
+ return this._networkRequests;
250
+ }
249
251
  async onBindingCalled(payload, context) {
250
252
  if (this._closedState === "closed")
251
253
  return;
@@ -258,7 +260,22 @@ class Page extends import_instrumentation.SdkObject {
258
260
  args.forEach((arg) => arg.dispose());
259
261
  return;
260
262
  }
261
- this.emitOnContextOnceInitialized(import_browserContext.BrowserContext.Events.Console, message);
263
+ this._consoleMessages.push(message);
264
+ ensureArrayLimit(this._consoleMessages, 200);
265
+ if (this._initialized)
266
+ this.emitOnContext(import_browserContext.BrowserContext.Events.Console, message);
267
+ }
268
+ consoleMessages() {
269
+ return this._consoleMessages;
270
+ }
271
+ addPageError(pageError) {
272
+ this._pageErrors.push(pageError);
273
+ ensureArrayLimit(this._pageErrors, 200);
274
+ if (this._initialized)
275
+ this.emitOnContext(import_browserContext.BrowserContext.Events.PageError, pageError, this);
276
+ }
277
+ pageErrors() {
278
+ return this._pageErrors;
262
279
  }
263
280
  async reload(progress, options) {
264
281
  return this.mainFrame().raceNavigationAction(progress, async () => {
@@ -853,6 +870,11 @@ async function snapshotFrameForAI(progress, frame, frameOrdinal, frameIds) {
853
870
  }
854
871
  return result;
855
872
  }
873
+ function ensureArrayLimit(array, limit) {
874
+ if (array.length > limit)
875
+ return array.splice(0, limit / 10);
876
+ return [];
877
+ }
856
878
  // Annotate the CommonJS export names for ESM import in node:
857
879
  0 && (module.exports = {
858
880
  InitScript,
@@ -85,7 +85,7 @@ async function asString(stream) {
85
85
  function iterablePump() {
86
86
  let controller;
87
87
  const stream = new ReadableStream({ start: (c) => controller = c });
88
- const iterable = async function* () {
88
+ const iterable = (async function* () {
89
89
  const reader = stream.getReader();
90
90
  while (true) {
91
91
  const { done, value } = await reader.read();
@@ -93,7 +93,7 @@ function iterablePump() {
93
93
  break;
94
94
  yield value;
95
95
  }
96
- }();
96
+ })();
97
97
  return {
98
98
  iterable,
99
99
  addChunk: (chunk) => {
@@ -50,6 +50,10 @@ async function performActionImpl(progress, mainFrame, actionInContext) {
50
50
  await mainFrame.click(progress, selector, { ...options, strict: true });
51
51
  return;
52
52
  }
53
+ if (action.name === "hover") {
54
+ await mainFrame.hover(progress, selector, { position: action.position, strict: true });
55
+ return;
56
+ }
53
57
  if (action.name === "press") {
54
58
  const modifiers = (0, import_language.toKeyboardModifiers)(action.modifiers);
55
59
  const shortcut = [...modifiers, action.key].join("+");
@@ -47,7 +47,8 @@ async function downloadBrowserWithProgressBar(title, browserDirectory, executabl
47
47
  import_debugLogger.debugLogger.log("install", `${title} is already downloaded.`);
48
48
  return false;
49
49
  }
50
- const zipPath = import_path.default.join(import_os.default.tmpdir(), downloadFileName);
50
+ const uniqueTempDir = await import_fs.default.promises.mkdtemp(import_path.default.join(import_os.default.tmpdir(), "playwright-download-"));
51
+ const zipPath = import_path.default.join(uniqueTempDir, downloadFileName);
51
52
  try {
52
53
  const retryCount = 5;
53
54
  for (let attempt = 1; attempt <= retryCount; ++attempt) {
@@ -73,8 +74,7 @@ async function downloadBrowserWithProgressBar(title, browserDirectory, executabl
73
74
  process.exitCode = 1;
74
75
  throw e;
75
76
  } finally {
76
- if (await (0, import_fileUtils.existsAsync)(zipPath))
77
- await import_fs.default.promises.unlink(zipPath);
77
+ await (0, import_fileUtils.removeFolders)([uniqueTempDir]);
78
78
  }
79
79
  logPolitely(`${title} downloaded to ${browserDirectory}`);
80
80
  return true;
@@ -749,6 +749,33 @@ ${(0, import_ascii.wrapInASCIIBox)(prettyMessage, 1)}`);
749
749
  _dependencyGroup: "webkit",
750
750
  _isHermeticInstallation: true
751
751
  });
752
+ this._executables.push({
753
+ type: "channel",
754
+ name: "webkit-wsl",
755
+ browserName: "webkit",
756
+ directory: webkit.dir,
757
+ executablePath: () => process.execPath,
758
+ executablePathOrDie: () => process.execPath,
759
+ wslExecutablePath: `/home/pwuser/.cache/ms-playwright/webkit-${webkit.revision}/pw_run.sh`,
760
+ installType: "download-on-demand",
761
+ _validateHostRequirements: (sdkLanguage) => Promise.resolve(),
762
+ _isHermeticInstallation: true,
763
+ _install: async () => {
764
+ if (process.platform !== "win32")
765
+ throw new Error(`WebKit via WSL is only supported on Windows`);
766
+ const script = import_path.default.join(BIN_PATH, "install_webkit_wsl.ps1");
767
+ const { code } = await (0, import_spawnAsync.spawnAsync)("powershell.exe", [
768
+ "-ExecutionPolicy",
769
+ "Bypass",
770
+ "-File",
771
+ script
772
+ ], {
773
+ stdio: "inherit"
774
+ });
775
+ if (code !== 0)
776
+ throw new Error(`Failed to install WebKit via WSL`);
777
+ }
778
+ });
752
779
  const ffmpeg = descriptors.find((d) => d.name === "ffmpeg");
753
780
  const ffmpegExecutable = findExecutablePath(ffmpeg.dir, "ffmpeg");
754
781
  this._executables.push({
@@ -75,10 +75,20 @@ class Snapshotter {
75
75
  dispose() {
76
76
  import_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
77
77
  }
78
+ async _captureFrameSnapshot(frame) {
79
+ const needsReset = !!frame[kNeedsResetSymbol];
80
+ frame[kNeedsResetSymbol] = false;
81
+ const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${needsReset ? "true" : "false"})`;
82
+ try {
83
+ return await frame.nonStallingRawEvaluateInExistingMainContext(expression);
84
+ } catch (e) {
85
+ frame[kNeedsResetSymbol] = true;
86
+ import_debugLogger.debugLogger.log("error", e);
87
+ }
88
+ }
78
89
  async captureSnapshot(page, callId, snapshotName) {
79
- const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(snapshotName)})`;
80
90
  const snapshots = page.frames().map(async (frame) => {
81
- const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch((e) => import_debugLogger.debugLogger.log("error", e));
91
+ const data = await this._captureFrameSnapshot(frame);
82
92
  if (!data || !this._started)
83
93
  return;
84
94
  const snapshot = {
@@ -130,6 +140,7 @@ class Snapshotter {
130
140
  }
131
141
  }
132
142
  }
143
+ const kNeedsResetSymbol = Symbol("kNeedsReset");
133
144
  // Annotate the CommonJS export names for ESM import in node:
134
145
  0 && (module.exports = {
135
146
  Snapshotter
@@ -256,9 +256,11 @@ function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
256
256
  this._readingStyleSheet = false;
257
257
  }
258
258
  }
259
- captureSnapshot() {
259
+ captureSnapshot(needsReset) {
260
260
  const timestamp = performance.now();
261
261
  const snapshotNumber = ++this._lastSnapshotNumber;
262
+ if (needsReset)
263
+ this.reset();
262
264
  let nodeCounter = 0;
263
265
  let shadowDomNesting = 0;
264
266
  let headNesting = 0;
@@ -101,11 +101,11 @@ function validateBuffer(buffer, mimeType) {
101
101
  if (mimeType === "image/png") {
102
102
  const pngMagicNumber = [137, 80, 78, 71, 13, 10, 26, 10];
103
103
  if (buffer.length < pngMagicNumber.length || !pngMagicNumber.every((byte, index) => buffer[index] === byte))
104
- throw new Error("could not decode image as PNG.");
104
+ throw new Error("Could not decode expected image as PNG.");
105
105
  } else if (mimeType === "image/jpeg") {
106
106
  const jpegMagicNumber = [255, 216];
107
107
  if (buffer.length < jpegMagicNumber.length || !jpegMagicNumber.every((byte, index) => buffer[index] === byte))
108
- throw new Error("could not decode image as JPEG.");
108
+ throw new Error("Could not decode expected image as JPEG.");
109
109
  }
110
110
  }
111
111
  function compareText(actual, expectedBuffer) {
@@ -22,7 +22,8 @@ __export(env_exports, {
22
22
  getFromENV: () => getFromENV,
23
23
  getPackageManager: () => getPackageManager,
24
24
  getPackageManagerExecCommand: () => getPackageManagerExecCommand,
25
- isLikelyNpxGlobal: () => isLikelyNpxGlobal
25
+ isLikelyNpxGlobal: () => isLikelyNpxGlobal,
26
+ setPlaywrightTestProcessEnv: () => setPlaywrightTestProcessEnv
26
27
  });
27
28
  module.exports = __toCommonJS(env_exports);
28
29
  function getFromENV(name) {
@@ -58,11 +59,15 @@ function getPackageManagerExecCommand() {
58
59
  function isLikelyNpxGlobal() {
59
60
  return process.argv.length >= 2 && process.argv[1].includes("_npx");
60
61
  }
62
+ function setPlaywrightTestProcessEnv() {
63
+ return process.env["PLAYWRIGHT_TEST"] = "1";
64
+ }
61
65
  // Annotate the CommonJS export names for ESM import in node:
62
66
  0 && (module.exports = {
63
67
  getAsBooleanFromENV,
64
68
  getFromENV,
65
69
  getPackageManager,
66
70
  getPackageManagerExecCommand,
67
- isLikelyNpxGlobal
71
+ isLikelyNpxGlobal,
72
+ setPlaywrightTestProcessEnv
68
73
  });
@@ -85,13 +85,8 @@ class WSServer {
85
85
  const url = new URL("http://localhost" + (request.url || ""));
86
86
  const id = String(++lastConnectionId);
87
87
  import_debugLogger.debugLogger.log("server", `[${id}] serving connection: ${request.url}`);
88
- try {
89
- const connection = this._delegate.onConnection(request, url, ws, id);
90
- ws[kConnectionSymbol] = connection;
91
- } catch (error) {
92
- import_debugLogger.debugLogger.log("server", `[${id}] connection error: ${error}`);
93
- ws.close(1011, String(error));
94
- }
88
+ const connection = this._delegate.onConnection(request, url, ws, id);
89
+ ws[kConnectionSymbol] = connection;
95
90
  });
96
91
  return wsEndpoint;
97
92
  }
@@ -28,7 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var webkit_exports = {};
30
30
  __export(webkit_exports, {
31
- WebKit: () => WebKit
31
+ WebKit: () => WebKit,
32
+ translatePathToWSL: () => translatePathToWSL
32
33
  });
33
34
  module.exports = __toCommonJS(webkit_exports);
34
35
  var import_path = __toESM(require("path"));
@@ -36,6 +37,8 @@ var import_wkConnection = require("./wkConnection");
36
37
  var import_ascii = require("../utils/ascii");
37
38
  var import_browserType = require("../browserType");
38
39
  var import_wkBrowser = require("../webkit/wkBrowser");
40
+ var import_spawnAsync = require("../utils/spawnAsync");
41
+ var import_registry = require("../registry");
39
42
  class WebKit extends import_browserType.BrowserType {
40
43
  constructor(parent) {
41
44
  super(parent, "webkit");
@@ -43,10 +46,11 @@ class WebKit extends import_browserType.BrowserType {
43
46
  connectToTransport(transport, options) {
44
47
  return import_wkBrowser.WKBrowser.connect(this.attribution.playwright, transport, options);
45
48
  }
46
- amendEnvironment(env, userDataDir, isPersistent) {
49
+ amendEnvironment(env, userDataDir, isPersistent, options) {
47
50
  return {
48
51
  ...env,
49
- CURL_COOKIE_JAR_PATH: process.platform === "win32" && isPersistent ? import_path.default.join(userDataDir, "cookiejar.db") : void 0
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
50
54
  };
51
55
  }
52
56
  doRewriteStartupLog(error) {
@@ -59,7 +63,7 @@ class WebKit extends import_browserType.BrowserType {
59
63
  attemptToGracefullyCloseBrowser(transport) {
60
64
  transport.send({ method: "Playwright.close", params: {}, id: import_wkConnection.kBrowserCloseMessageId });
61
65
  }
62
- defaultArgs(options, isPersistent, userDataDir) {
66
+ async defaultArgs(options, isPersistent, userDataDir) {
63
67
  const { args = [], headless } = options;
64
68
  const userDataDirArg = args.find((arg) => arg.startsWith("--user-data-dir"));
65
69
  if (userDataDirArg)
@@ -67,12 +71,19 @@ class WebKit extends import_browserType.BrowserType {
67
71
  if (args.find((arg) => !arg.startsWith("-")))
68
72
  throw new Error("Arguments can not specify page to be opened");
69
73
  const webkitArguments = ["--inspector-pipe"];
70
- if (process.platform === "win32")
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
+ if (process.platform === "win32" && options.channel !== "webkit-wsl")
71
82
  webkitArguments.push("--disable-accelerated-compositing");
72
83
  if (headless)
73
84
  webkitArguments.push("--headless");
74
85
  if (isPersistent)
75
- webkitArguments.push(`--user-data-dir=${userDataDir}`);
86
+ webkitArguments.push(`--user-data-dir=${options.channel === "webkit-wsl" ? await translatePathToWSL(userDataDir) : userDataDir}`);
76
87
  else
77
88
  webkitArguments.push(`--no-startup-window`);
78
89
  const proxy = options.proxyOverride || options.proxy;
@@ -81,7 +92,7 @@ class WebKit extends import_browserType.BrowserType {
81
92
  webkitArguments.push(`--proxy=${proxy.server}`);
82
93
  if (proxy.bypass)
83
94
  webkitArguments.push(`--proxy-bypass-list=${proxy.bypass}`);
84
- } else if (process.platform === "linux") {
95
+ } else if (process.platform === "linux" || process.platform === "win32" && options.channel === "webkit-wsl") {
85
96
  webkitArguments.push(`--proxy=${proxy.server}`);
86
97
  if (proxy.bypass)
87
98
  webkitArguments.push(...proxy.bypass.split(",").map((t) => `--ignore-host=${t}`));
@@ -97,7 +108,12 @@ class WebKit extends import_browserType.BrowserType {
97
108
  return webkitArguments;
98
109
  }
99
110
  }
111
+ async function translatePathToWSL(path2) {
112
+ const { stdout } = await (0, import_spawnAsync.spawnAsync)("wsl.exe", ["-d", "playwright", "--cd", "/home/pwuser", "wslpath", path2.replace(/\\/g, "\\\\")]);
113
+ return stdout.toString().trim();
114
+ }
100
115
  // Annotate the CommonJS export names for ESM import in node:
101
116
  0 && (module.exports = {
102
- WebKit
117
+ WebKit,
118
+ translatePathToWSL
103
119
  });
@@ -39,6 +39,7 @@ var network = __toESM(require("../network"));
39
39
  var import_wkConnection = require("./wkConnection");
40
40
  var import_wkPage = require("./wkPage");
41
41
  var import_errors = require("../errors");
42
+ var import_webkit = require("./webkit");
42
43
  const BROWSER_VERSION = "26.0";
43
44
  const DEFAULT_USER_AGENT = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/${BROWSER_VERSION} Safari/605.1.15`;
44
45
  class WKBrowser extends import_browser.Browser {
@@ -87,7 +88,7 @@ class WKBrowser extends import_browser.Browser {
87
88
  const createOptions = proxy ? {
88
89
  // Enable socks5 hostname resolution on Windows.
89
90
  // See https://github.com/microsoft/playwright/issues/20451
90
- proxyServer: process.platform === "win32" ? proxy.server.replace(/^socks5:\/\//, "socks5h://") : proxy.server,
91
+ proxyServer: process.platform === "win32" && this.attribution.browser?.options.channel !== "webkit-wsl" ? proxy.server.replace(/^socks5:\/\//, "socks5h://") : proxy.server,
91
92
  proxyBypassList: proxy.bypass
92
93
  } : void 0;
93
94
  const { browserContextId } = await this._browserSession.send("Playwright.createContext", createOptions);
@@ -110,7 +111,10 @@ class WKBrowser extends import_browser.Browser {
110
111
  const page = this._wkPages.get(payload.pageProxyId);
111
112
  if (!page)
112
113
  return;
113
- page._page.frameManager.frameAbortedNavigation(payload.frameId, "Download is starting");
114
+ let frameId = payload.frameId;
115
+ if (!page._page.frameManager.frame(frameId))
116
+ frameId = page._page.mainFrame()._id;
117
+ page._page.frameManager.frameAbortedNavigation(frameId, "Download is starting");
114
118
  let originPage = page._page.initializedOrUndefined();
115
119
  if (!originPage) {
116
120
  page._firstNonInitialNavigationCommittedReject(new Error("Starting new page download"));
@@ -189,7 +193,7 @@ class WKBrowserContext extends import_browserContext.BrowserContext {
189
193
  const promises = [super._initialize()];
190
194
  promises.push(this._browser._browserSession.send("Playwright.setDownloadBehavior", {
191
195
  behavior: this._options.acceptDownloads === "accept" ? "allow" : "deny",
192
- downloadPath: this._browser.options.downloadsPath,
196
+ downloadPath: this._browser.options.channel === "webkit-wsl" ? await (0, import_webkit.translatePathToWSL)(this._browser.options.downloadsPath) : this._browser.options.downloadsPath,
193
197
  browserContextId
194
198
  }));
195
199
  if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors)