playwright-core 1.54.0-alpha-2025-06-20 → 1.54.0-alpha-1750421372000

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 (38) hide show
  1. package/lib/client/playwright.js +2 -2
  2. package/lib/inProcessFactory.js +2 -2
  3. package/lib/protocol/validator.js +2 -2
  4. package/lib/remote/playwrightConnection.js +27 -168
  5. package/lib/remote/playwrightServer.js +173 -27
  6. package/lib/server/android/android.js +31 -25
  7. package/lib/server/bidi/bidiChromium.js +1 -1
  8. package/lib/server/bidi/bidiFirefox.js +4 -1
  9. package/lib/server/browser.js +13 -12
  10. package/lib/server/browserContext.js +49 -46
  11. package/lib/server/browserType.js +26 -19
  12. package/lib/server/chromium/chromium.js +9 -7
  13. package/lib/server/chromium/videoRecorder.js +2 -1
  14. package/lib/server/debugController.js +1 -1
  15. package/lib/server/dispatchers/androidDispatcher.js +4 -4
  16. package/lib/server/dispatchers/browserContextDispatcher.js +1 -1
  17. package/lib/server/dispatchers/browserDispatcher.js +2 -2
  18. package/lib/server/dispatchers/electronDispatcher.js +2 -2
  19. package/lib/server/dispatchers/frameDispatcher.js +1 -1
  20. package/lib/server/dispatchers/localUtilsDispatcher.js +1 -2
  21. package/lib/server/dispatchers/playwrightDispatcher.js +5 -21
  22. package/lib/server/dom.js +3 -4
  23. package/lib/server/electron/electron.js +15 -16
  24. package/lib/server/fetch.js +12 -26
  25. package/lib/server/firefox/ffPage.js +0 -1
  26. package/lib/server/frames.js +93 -92
  27. package/lib/server/helper.js +1 -1
  28. package/lib/server/page.js +48 -42
  29. package/lib/server/playwright.js +2 -2
  30. package/lib/server/recorder/recorderApp.js +1 -1
  31. package/lib/server/registry/index.js +7 -20
  32. package/lib/server/screenshotter.js +17 -31
  33. package/lib/server/socksClientCertificatesInterceptor.js +7 -3
  34. package/lib/server/trace/viewer/traceViewer.js +1 -1
  35. package/lib/server/transport.js +3 -7
  36. package/lib/server/utils/processLauncher.js +1 -1
  37. package/lib/vite/htmlReport/index.html +9 -9
  38. package/package.json +1 -1
@@ -126,22 +126,20 @@ class ElectronApplication extends import_instrumentation.SdkObject {
126
126
  class Electron extends import_instrumentation.SdkObject {
127
127
  constructor(playwright) {
128
128
  super(playwright, "electron");
129
+ this.logName = "browser";
129
130
  }
130
- async launch(options) {
131
- const {
132
- args = []
133
- } = options;
134
- const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this);
135
- controller.setLogName("browser");
131
+ async launch(metadata, options) {
132
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
136
133
  return controller.run(async (progress) => {
137
134
  let app = void 0;
138
- let electronArguments = ["--inspect=0", "--remote-debugging-port=0", ...args];
135
+ let electronArguments = ["--inspect=0", "--remote-debugging-port=0", ...options.args || []];
139
136
  if (import_os.default.platform() === "linux") {
140
137
  const runningAsRoot = process.geteuid && process.geteuid() === 0;
141
138
  if (runningAsRoot && electronArguments.indexOf("--no-sandbox") === -1)
142
139
  electronArguments.unshift("--no-sandbox");
143
140
  }
144
- const artifactsDir = await import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
141
+ const artifactsDir = await progress.race(import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER));
142
+ progress.cleanupWhenAborted(() => (0, import_utils.removeFolders)([artifactsDir]));
145
143
  const browserLogsCollector = new import_debugLogger.RecentLogsCollector();
146
144
  const env = options.env ? (0, import_processLauncher.envArrayToObject)(options.env) : process.env;
147
145
  let command;
@@ -186,8 +184,8 @@ class Electron extends import_instrumentation.SdkObject {
186
184
  handleSIGHUP: true,
187
185
  onExit: () => app?.emit(ElectronApplication.Events.Close)
188
186
  });
189
- const waitForXserverError = new Promise(async (resolve, reject) => {
190
- waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => reject(new Error([
187
+ const waitForXserverError = waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => {
188
+ throw new Error([
191
189
  "Unable to open X display!",
192
190
  `================================`,
193
191
  "Most likely this is because there is no X server available.",
@@ -195,14 +193,14 @@ class Electron extends import_instrumentation.SdkObject {
195
193
  "For example: 'xvfb-run npm run test:e2e'",
196
194
  `================================`,
197
195
  progress.metadata.log
198
- ].join("\n")))).catch(() => {
199
- });
196
+ ].join("\n"));
200
197
  });
201
198
  const nodeMatchPromise = waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
202
199
  const chromeMatchPromise = waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
203
200
  const debuggerDisconnectPromise = waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./);
204
201
  const nodeMatch = await nodeMatchPromise;
205
202
  const nodeTransport = await import_transport.WebSocketTransport.connect(progress, nodeMatch[1]);
203
+ progress.cleanupWhenAborted(() => nodeTransport.close());
206
204
  const nodeConnection = new import_crConnection.CRConnection(this, nodeTransport, import_helper.helper.debugProtocolLogger(), browserLogsCollector);
207
205
  debuggerDisconnectPromise.then(() => {
208
206
  nodeTransport.close();
@@ -213,6 +211,7 @@ class Electron extends import_instrumentation.SdkObject {
213
211
  waitForXserverError
214
212
  ]);
215
213
  const chromeTransport = await import_transport.WebSocketTransport.connect(progress, chromeMatch[1]);
214
+ progress.cleanupWhenAborted(() => chromeTransport.close());
216
215
  const browserProcess = {
217
216
  onclose: void 0,
218
217
  process: launchedProcess,
@@ -237,15 +236,15 @@ class Electron extends import_instrumentation.SdkObject {
237
236
  originalLaunchOptions: { timeout: options.timeout }
238
237
  };
239
238
  (0, import_browserContext.validateBrowserContextOptions)(contextOptions, browserOptions);
240
- const browser = await import_crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
239
+ const browser = await progress.race(import_crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions));
241
240
  app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
242
- await app.initialize();
241
+ await progress.race(app.initialize());
243
242
  return app;
244
243
  }, options.timeout);
245
244
  }
246
245
  }
247
246
  function waitForLine(progress, process2, regex) {
248
- return new Promise((resolve, reject) => {
247
+ return progress.race(new Promise((resolve, reject) => {
249
248
  const rl = readline.createInterface({ input: process2.stderr });
250
249
  const failError = new Error("Process failed to launch!");
251
250
  const listeners = [
@@ -266,7 +265,7 @@ function waitForLine(progress, process2, regex) {
266
265
  function cleanup() {
267
266
  import_eventsHelper.eventsHelper.removeEventListeners(listeners);
268
267
  }
269
- });
268
+ }));
270
269
  }
271
270
  // Annotate the CommonJS export names for ESM import in node:
272
271
  0 && (module.exports = {
@@ -122,15 +122,11 @@ class APIRequestContext extends import_instrumentation.SdkObject {
122
122
  agent = (0, import_utils.createProxyAgent)(proxy, requestUrl);
123
123
  let maxRedirects = params.maxRedirects ?? (defaults.maxRedirects ?? 20);
124
124
  maxRedirects = maxRedirects === 0 ? -1 : maxRedirects;
125
- const timeout = params.timeout;
126
- const deadline = timeout && (0, import_utils.monotonicTime)() + timeout;
127
125
  const options = {
128
126
  method,
129
127
  headers,
130
128
  agent,
131
129
  maxRedirects,
132
- timeout,
133
- deadline,
134
130
  ...(0, import_socksClientCertificatesInterceptor.getMatchingTLSOptionsForOrigin)(this._defaultOptions().clientCertificates, requestUrl.origin),
135
131
  __testHookLookup: params.__testHookLookup
136
132
  };
@@ -139,10 +135,10 @@ class APIRequestContext extends import_instrumentation.SdkObject {
139
135
  const postData = serializePostData(params, headers);
140
136
  if (postData)
141
137
  setHeader(headers, "content-length", String(postData.byteLength));
142
- const controller = new import_progress.ProgressController(metadata, this);
138
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
143
139
  const fetchResponse = await controller.run((progress) => {
144
140
  return this._sendRequestWithRetries(progress, requestUrl, options, postData, params.maxRetries);
145
- }, timeout);
141
+ }, params.timeout);
146
142
  const fetchUid = this._storeResponseBody(fetchResponse.body);
147
143
  this.fetchLog.set(fetchUid, controller.metadata.log);
148
144
  const failOnStatusCode = params.failOnStatusCode !== void 0 ? params.failOnStatusCode : !!defaults.failOnStatusCode;
@@ -182,10 +178,10 @@ ${text}`;
182
178
  }
183
179
  return cookies;
184
180
  }
185
- async _updateRequestCookieHeader(url, headers) {
181
+ async _updateRequestCookieHeader(progress, url, headers) {
186
182
  if (getHeader(headers, "cookie") !== void 0)
187
183
  return;
188
- const contextCookies = await this._cookies(url);
184
+ const contextCookies = await progress.race(this._cookies(url));
189
185
  const cookies = contextCookies.filter((c) => new import_cookieStore.Cookie(c).matches(url));
190
186
  if (cookies.length) {
191
187
  const valueArray = cookies.map((c) => `${c.name}=${c.value}`);
@@ -199,22 +195,24 @@ ${text}`;
199
195
  try {
200
196
  return await this._sendRequest(progress, url, options, postData);
201
197
  } catch (e) {
198
+ if ((0, import_progress.isAbortError)(e))
199
+ throw e;
202
200
  e = (0, import_socksClientCertificatesInterceptor.rewriteOpenSSLErrorIfNeeded)(e);
203
201
  if (maxRetries === 0)
204
202
  throw e;
205
- if (i === maxRetries || options.deadline && (0, import_utils.monotonicTime)() + backoff > options.deadline)
203
+ if (i === maxRetries)
206
204
  throw new Error(`Failed after ${i + 1} attempt(s): ${e}`);
207
205
  if (e.code !== "ECONNRESET")
208
206
  throw e;
209
207
  progress.log(` Received ECONNRESET, will retry after ${backoff}ms.`);
210
- await new Promise((f) => setTimeout(f, backoff));
208
+ await progress.wait(backoff);
211
209
  backoff *= 2;
212
210
  }
213
211
  }
214
212
  throw new Error("Unreachable");
215
213
  }
216
214
  async _sendRequest(progress, url, options, postData) {
217
- await this._updateRequestCookieHeader(url, options.headers);
215
+ await this._updateRequestCookieHeader(progress, url, options.headers);
218
216
  const requestCookies = getHeader(options.headers, "cookie")?.split(";").map((p) => {
219
217
  const [name, value] = p.split("=").map((v) => v.trim());
220
218
  return { name, value };
@@ -227,7 +225,7 @@ ${text}`;
227
225
  postData
228
226
  };
229
227
  this.emit(APIRequestContext.Events.Request, requestEvent);
230
- return new Promise((fulfill, reject) => {
228
+ const resultPromise = new Promise((fulfill, reject) => {
231
229
  const requestConstructor = (url.protocol === "https:" ? import_https.default : import_http.default).request;
232
230
  const agent = options.agent || (url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent);
233
231
  const requestOptions = { ...options, agent };
@@ -308,8 +306,6 @@ ${text}`;
308
306
  headers,
309
307
  agent: options.agent,
310
308
  maxRedirects: options.maxRedirects - 1,
311
- timeout: options.timeout,
312
- deadline: options.deadline,
313
309
  ...(0, import_socksClientCertificatesInterceptor.getMatchingTLSOptionsForOrigin)(this._defaultOptions().clientCertificates, url.origin),
314
310
  __testHookLookup: options.__testHookLookup
315
311
  };
@@ -387,6 +383,7 @@ ${text}`;
387
383
  body.on("end", notifyBodyFinished);
388
384
  });
389
385
  request.on("error", reject);
386
+ progress.cleanupWhenAborted(() => request.destroy());
390
387
  listeners.push(
391
388
  import_utils.eventsHelper.addEventListener(this, APIRequestContext.Events.Dispose, () => {
392
389
  reject(new Error("Request context disposed."));
@@ -434,22 +431,11 @@ ${text}`;
434
431
  for (const [name, value] of Object.entries(options.headers))
435
432
  progress.log(` ${name}: ${value}`);
436
433
  }
437
- if (options.deadline) {
438
- const rejectOnTimeout = () => {
439
- reject(new Error(`Request timed out after ${options.timeout}ms`));
440
- request.destroy();
441
- };
442
- const remaining = options.deadline - (0, import_utils.monotonicTime)();
443
- if (remaining <= 0) {
444
- rejectOnTimeout();
445
- return;
446
- }
447
- request.setTimeout(remaining, rejectOnTimeout);
448
- }
449
434
  if (postData)
450
435
  request.write(postData);
451
436
  request.end();
452
437
  });
438
+ return progress.race(resultPromise);
453
439
  }
454
440
  _getHttpCredentials(url) {
455
441
  if (!this._defaultOptions().httpCredentials?.origin || url.origin.toLowerCase() === this._defaultOptions().httpCredentials?.origin?.toLowerCase())
@@ -363,7 +363,6 @@ class FFPage {
363
363
  height: viewportRect.height
364
364
  };
365
365
  }
366
- progress.throwIfAborted();
367
366
  const { data } = await this._session.send("Page.screenshot", {
368
367
  mimeType: "image/" + format,
369
368
  clip: documentRect,
@@ -468,7 +468,7 @@ class Frame extends import_instrumentation.SdkObject {
468
468
  this.emit(Frame.Events.RemoveLifecycle, "networkidle");
469
469
  }
470
470
  }
471
- async raceNavigationAction(progress, options, action) {
471
+ async raceNavigationAction(progress, action) {
472
472
  return import_utils.LongStandingScope.raceMultiple([
473
473
  this._detachedScope,
474
474
  this._page.openScope
@@ -477,14 +477,14 @@ class Frame extends import_instrumentation.SdkObject {
477
477
  const data = this._redirectedNavigations.get(e.documentId);
478
478
  if (data) {
479
479
  progress.log(`waiting for redirected navigation to "${data.url}"`);
480
- return data.gotoPromise;
480
+ return progress.race(data.gotoPromise);
481
481
  }
482
482
  }
483
483
  throw e;
484
484
  }));
485
485
  }
486
486
  redirectNavigation(url, documentId, referer) {
487
- const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this);
487
+ const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this, "strict");
488
488
  const data = {
489
489
  url,
490
490
  gotoPromise: controller.run((progress) => this.gotoImpl(progress, url, { referer }), 0)
@@ -493,10 +493,10 @@ class Frame extends import_instrumentation.SdkObject {
493
493
  data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
494
494
  }
495
495
  async goto(metadata, url, options) {
496
- const constructedNavigationURL = (0, import_utils.constructURLBasedOnBaseURL)(this._page.browserContext._options.baseURL, url);
497
- const controller = new import_progress.ProgressController(metadata, this);
496
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
498
497
  return controller.run((progress) => {
499
- return this.raceNavigationAction(progress, options, async () => this.gotoImpl(progress, constructedNavigationURL, options));
498
+ const constructedNavigationURL = (0, import_utils.constructURLBasedOnBaseURL)(this._page.browserContext._options.baseURL, url);
499
+ return this.raceNavigationAction(progress, async () => this.gotoImpl(progress, constructedNavigationURL, options));
500
500
  }, options.timeout);
501
501
  }
502
502
  async gotoImpl(progress, url, options) {
@@ -514,7 +514,7 @@ class Frame extends import_instrumentation.SdkObject {
514
514
  const navigationEvents = [];
515
515
  const collectNavigations = (arg) => navigationEvents.push(arg);
516
516
  this.on(Frame.Events.InternalNavigation, collectNavigations);
517
- const navigateResult = await this._page.delegate.navigateFrame(this, url, referer).finally(
517
+ const navigateResult = await progress.race(this._page.delegate.navigateFrame(this, url, referer)).finally(
518
518
  () => this.off(Frame.Events.InternalNavigation, collectNavigations)
519
519
  );
520
520
  let event;
@@ -543,7 +543,7 @@ class Frame extends import_instrumentation.SdkObject {
543
543
  if (!this._firedLifecycleEvents.has(waitUntil))
544
544
  await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
545
545
  const request = event.newDocument ? event.newDocument.request : void 0;
546
- const response = request ? request._finalRequest().response() : null;
546
+ const response = request ? progress.race(request._finalRequest().response()) : null;
547
547
  return response;
548
548
  }
549
549
  async _waitForNavigation(progress, requiresNewDocument, options) {
@@ -562,7 +562,7 @@ class Frame extends import_instrumentation.SdkObject {
562
562
  if (!this._firedLifecycleEvents.has(waitUntil))
563
563
  await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
564
564
  const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0;
565
- return request ? request._finalRequest().response() : null;
565
+ return request ? progress.race(request._finalRequest().response()) : null;
566
566
  }
567
567
  async _waitForLoadState(progress, state) {
568
568
  const waitUntil = verifyLifecycle("state", state);
@@ -714,31 +714,32 @@ class Frame extends import_instrumentation.SdkObject {
714
714
  return retVal;
715
715
  });
716
716
  } catch (e) {
717
- if ((0, import_progress.isAbortError)(e) || js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
717
+ if (this.isNonRetriableError(e))
718
718
  throw e;
719
719
  throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`);
720
720
  }
721
721
  }
722
722
  async setContent(metadata, html, options) {
723
- const controller = new import_progress.ProgressController(metadata, this);
723
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
724
724
  return controller.run(async (progress) => {
725
- await this.raceNavigationAction(progress, options, async () => {
725
+ await this.raceNavigationAction(progress, async () => {
726
726
  const waitUntil = options.waitUntil === void 0 ? "load" : options.waitUntil;
727
727
  progress.log(`setting frame content, waiting until "${waitUntil}"`);
728
728
  const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
729
- const context = await this._utilityContext();
730
- const lifecyclePromise = new Promise((resolve, reject) => {
731
- this._page.frameManager._consoleMessageTags.set(tag, () => {
732
- this._onClearLifecycle();
733
- this._waitForLoadState(progress, waitUntil).then(resolve).catch(reject);
734
- });
729
+ const context = await progress.race(this._utilityContext());
730
+ const tagPromise = new import_manualPromise.ManualPromise();
731
+ this._page.frameManager._consoleMessageTags.set(tag, () => {
732
+ this._onClearLifecycle();
733
+ tagPromise.resolve();
735
734
  });
736
- const contentPromise = context.evaluate(({ html: html2, tag: tag2 }) => {
735
+ progress.cleanupWhenAborted(() => this._page.frameManager._consoleMessageTags.delete(tag));
736
+ const lifecyclePromise = progress.race(tagPromise).then(() => this._waitForLoadState(progress, waitUntil));
737
+ const contentPromise = progress.race(context.evaluate(({ html: html2, tag: tag2 }) => {
737
738
  document.open();
738
739
  console.debug(tag2);
739
740
  document.write(html2);
740
741
  document.close();
741
- }, { html, tag });
742
+ }, { html, tag }));
742
743
  await Promise.all([contentPromise, lifecyclePromise]);
743
744
  return null;
744
745
  });
@@ -884,13 +885,13 @@ class Frame extends import_instrumentation.SdkObject {
884
885
  continue;
885
886
  return result;
886
887
  } catch (e) {
887
- if (this._isErrorThatCannotBeRetried(e))
888
+ if (this.isNonRetriableError(e))
888
889
  throw e;
889
890
  continue;
890
891
  }
891
892
  }
892
893
  }
893
- _isErrorThatCannotBeRetried(e) {
894
+ isNonRetriableError(e) {
894
895
  if ((0, import_progress.isAbortError)(e))
895
896
  return true;
896
897
  if (js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
@@ -947,7 +948,7 @@ class Frame extends import_instrumentation.SdkObject {
947
948
  }
948
949
  async rafrafTimeoutScreenshotElementWithProgress(progress, selector, timeout, options) {
949
950
  return await this._retryWithProgressIfNotConnected(progress, selector, true, true, async (handle) => {
950
- await handle._frame.rafrafTimeout(timeout);
951
+ await handle._frame.rafrafTimeout(progress, timeout);
951
952
  return await this._page.screenshotter.screenshotElement(progress, handle, options);
952
953
  });
953
954
  }
@@ -1081,7 +1082,7 @@ class Frame extends import_instrumentation.SdkObject {
1081
1082
  return state.matches;
1082
1083
  }, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
1083
1084
  } catch (e) {
1084
- if ((0, import_progress.isAbortError)(e) || js.isJavaScriptErrorInEvaluate(e) || (0, import_selectorParser.isInvalidSelectorError)(e) || (0, import_protocolError.isSessionClosedError)(e))
1085
+ if (this.isNonRetriableError(e))
1085
1086
  throw e;
1086
1087
  return false;
1087
1088
  }
@@ -1149,9 +1150,9 @@ class Frame extends import_instrumentation.SdkObject {
1149
1150
  return controller.run((progress) => progress.wait(timeout));
1150
1151
  }
1151
1152
  async ariaSnapshot(metadata, selector, options) {
1152
- const controller = new import_progress.ProgressController(metadata, this);
1153
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
1153
1154
  return controller.run(async (progress) => {
1154
- return await this._retryWithProgressIfNotConnected(progress, selector, true, true, (handle) => handle.ariaSnapshot(options));
1155
+ return await this._retryWithProgressIfNotConnected(progress, selector, true, true, (handle) => progress.race(handle.ariaSnapshot(options)));
1155
1156
  }, options.timeout);
1156
1157
  }
1157
1158
  async expect(metadata, selector, options) {
@@ -1165,20 +1166,20 @@ class Frame extends import_instrumentation.SdkObject {
1165
1166
  try {
1166
1167
  let timeout = options.timeout;
1167
1168
  const start = timeout > 0 ? (0, import_utils.monotonicTime)() : 0;
1168
- await new import_progress.ProgressController(metadata, this).run(async (progress) => {
1169
+ await new import_progress.ProgressController(metadata, this, "strict").run(async (progress) => {
1169
1170
  progress.log(`${(0, import_utils.renderTitleForCall)(metadata)}${timeout ? ` with timeout ${timeout}ms` : ""}`);
1170
1171
  if (selector)
1171
1172
  progress.log(`waiting for ${this._asLocator(selector)}`);
1172
1173
  await this._page.performActionPreChecks(progress);
1173
1174
  }, timeout);
1174
1175
  try {
1175
- const resultOneShot = await new import_progress.ProgressController(metadata, this).run(async (progress) => {
1176
+ const resultOneShot = await new import_progress.ProgressController(metadata, this, "strict").run(async (progress) => {
1176
1177
  return await this._expectInternal(progress, selector, options, lastIntermediateResult);
1177
1178
  });
1178
1179
  if (resultOneShot.matches !== options.isNot)
1179
1180
  return resultOneShot;
1180
1181
  } catch (e) {
1181
- if ((0, import_progress.isAbortError)(e) || js.isJavaScriptErrorInEvaluate(e) || (0, import_selectorParser.isInvalidSelectorError)(e))
1182
+ if (this.isNonRetriableError(e))
1182
1183
  throw e;
1183
1184
  }
1184
1185
  if (timeout > 0) {
@@ -1187,7 +1188,7 @@ class Frame extends import_instrumentation.SdkObject {
1187
1188
  }
1188
1189
  if (timeout < 0)
1189
1190
  return { matches: options.isNot, log: (0, import_callLog.compressCallLog)(metadata.log), timedOut: true, received: lastIntermediateResult.received };
1190
- return await new import_progress.ProgressController(metadata, this).run(async (progress) => {
1191
+ return await new import_progress.ProgressController(metadata, this, "strict").run(async (progress) => {
1191
1192
  return await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1e3], async (continuePolling) => {
1192
1193
  await this._page.performActionPreChecks(progress);
1193
1194
  const { matches, received } = await this._expectInternal(progress, selector, options, lastIntermediateResult);
@@ -1209,14 +1210,12 @@ class Frame extends import_instrumentation.SdkObject {
1209
1210
  }
1210
1211
  }
1211
1212
  async _expectInternal(progress, selector, options, lastIntermediateResult) {
1212
- const selectorInFrame = selector ? await this.selectors.resolveFrameForSelector(selector, { strict: true }) : void 0;
1213
- progress.throwIfAborted();
1213
+ const selectorInFrame = selector ? await progress.race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
1214
1214
  const { frame, info } = selectorInFrame || { frame: this, info: void 0 };
1215
1215
  const world = options.expression === "to.have.property" ? "main" : info?.world ?? "utility";
1216
- const context = await frame._context(world);
1217
- const injected = await context.injectedScript();
1218
- progress.throwIfAborted();
1219
- const { log, matches, received, missingReceived } = await injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
1216
+ const context = await progress.race(frame._context(world));
1217
+ const injected = await progress.race(context.injectedScript());
1218
+ const { log, matches, received, missingReceived } = await progress.race(injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
1220
1219
  const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : [];
1221
1220
  if (callId)
1222
1221
  injected2.markTargetElements(new Set(elements), callId);
@@ -1229,7 +1228,7 @@ class Frame extends import_instrumentation.SdkObject {
1229
1228
  else if (elements.length)
1230
1229
  log2 = ` locator resolved to ${injected2.previewNode(elements[0])}`;
1231
1230
  return { log: log2, ...await injected2.expect(elements[0], options2, elements) };
1232
- }, { info, options, callId: progress.metadata.id });
1231
+ }, { info, options, callId: progress.metadata.id }));
1233
1232
  if (log)
1234
1233
  progress.log(log);
1235
1234
  if (matches === options.isNot) {
@@ -1240,60 +1239,62 @@ class Frame extends import_instrumentation.SdkObject {
1240
1239
  }
1241
1240
  return { matches, received };
1242
1241
  }
1243
- async _waitForFunctionExpression(metadata, expression, isFunction, arg, options, world = "main") {
1244
- const controller = new import_progress.ProgressController(metadata, this);
1242
+ async waitForFunctionExpression(metadata, expression, isFunction, arg, options) {
1243
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
1244
+ return controller.run((progress) => this.waitForFunctionExpressionImpl(progress, expression, isFunction, arg, options, "main"), options.timeout);
1245
+ }
1246
+ async waitForFunctionExpressionImpl(progress, expression, isFunction, arg, options, world = "main") {
1245
1247
  if (typeof options.pollingInterval === "number")
1246
1248
  (0, import_utils.assert)(options.pollingInterval > 0, "Cannot poll with non-positive interval: " + options.pollingInterval);
1247
1249
  expression = js.normalizeEvaluationExpression(expression, isFunction);
1248
- return controller.run(async (progress) => {
1249
- return this.retryWithProgressAndTimeouts(progress, [100], async () => {
1250
- const context = world === "main" ? await this._mainContext() : await this._utilityContext();
1251
- const injectedScript = await context.injectedScript();
1252
- const handle = await injectedScript.evaluateHandle((injected, { expression: expression2, isFunction: isFunction2, polling, arg: arg2 }) => {
1253
- const predicate = () => {
1254
- let result2 = globalThis.eval(expression2);
1255
- if (isFunction2 === true) {
1256
- result2 = result2(arg2);
1257
- } else if (isFunction2 === false) {
1258
- result2 = result2;
1259
- } else {
1260
- if (typeof result2 === "function")
1261
- result2 = result2(arg2);
1262
- }
1263
- return result2;
1264
- };
1265
- let fulfill;
1266
- let reject;
1267
- let aborted = false;
1268
- const result = new Promise((f, r) => {
1269
- fulfill = f;
1270
- reject = r;
1271
- });
1272
- const next = () => {
1273
- if (aborted)
1250
+ return this.retryWithProgressAndTimeouts(progress, [100], async () => {
1251
+ const context = world === "main" ? await progress.race(this._mainContext()) : await progress.race(this._utilityContext());
1252
+ const injectedScript = await progress.race(context.injectedScript());
1253
+ const handle = await progress.race(injectedScript.evaluateHandle((injected, { expression: expression2, isFunction: isFunction2, polling, arg: arg2 }) => {
1254
+ const predicate = () => {
1255
+ let result3 = globalThis.eval(expression2);
1256
+ if (isFunction2 === true) {
1257
+ result3 = result3(arg2);
1258
+ } else if (isFunction2 === false) {
1259
+ result3 = result3;
1260
+ } else {
1261
+ if (typeof result3 === "function")
1262
+ result3 = result3(arg2);
1263
+ }
1264
+ return result3;
1265
+ };
1266
+ let fulfill;
1267
+ let reject;
1268
+ let aborted = false;
1269
+ const result2 = new Promise((f, r) => {
1270
+ fulfill = f;
1271
+ reject = r;
1272
+ });
1273
+ const next = () => {
1274
+ if (aborted)
1275
+ return;
1276
+ try {
1277
+ const success = predicate();
1278
+ if (success) {
1279
+ fulfill(success);
1274
1280
  return;
1275
- try {
1276
- const success = predicate();
1277
- if (success) {
1278
- fulfill(success);
1279
- return;
1280
- }
1281
- if (typeof polling !== "number")
1282
- injected.utils.builtins.requestAnimationFrame(next);
1283
- else
1284
- injected.utils.builtins.setTimeout(next, polling);
1285
- } catch (e) {
1286
- reject(e);
1287
1281
  }
1288
- };
1289
- next();
1290
- return { result, abort: () => aborted = true };
1291
- }, { expression, isFunction, polling: options.pollingInterval, arg });
1292
- progress.cleanupWhenAborted(() => handle.evaluate((h) => h.abort()).catch(() => {
1293
- }));
1294
- return handle.evaluateHandle((h) => h.result);
1295
- });
1296
- }, options.timeout);
1282
+ if (typeof polling !== "number")
1283
+ injected.utils.builtins.requestAnimationFrame(next);
1284
+ else
1285
+ injected.utils.builtins.setTimeout(next, polling);
1286
+ } catch (e) {
1287
+ reject(e);
1288
+ }
1289
+ };
1290
+ next();
1291
+ return { result: result2, abort: () => aborted = true };
1292
+ }, { expression, isFunction, polling: options.pollingInterval, arg }));
1293
+ progress.cleanupWhenAborted(() => handle.evaluate((h) => h.abort()).finally(() => handle.dispose()));
1294
+ const result = await progress.race(handle.evaluateHandle((h) => h.result));
1295
+ handle.dispose();
1296
+ return result;
1297
+ });
1297
1298
  }
1298
1299
  async waitForFunctionValueInUtility(progress, pageFunction) {
1299
1300
  const expression = `() => {
@@ -1302,25 +1303,25 @@ class Frame extends import_instrumentation.SdkObject {
1302
1303
  return result;
1303
1304
  return JSON.stringify(result);
1304
1305
  }`;
1305
- const handle = await this._waitForFunctionExpression((0, import_instrumentation.serverSideCallMetadata)(), expression, true, void 0, { timeout: progress.timeUntilDeadline() }, "utility");
1306
+ const handle = await this.waitForFunctionExpressionImpl(progress, expression, true, void 0, {}, "utility");
1306
1307
  return JSON.parse(handle.rawValue());
1307
1308
  }
1308
1309
  async title() {
1309
1310
  const context = await this._utilityContext();
1310
1311
  return context.evaluate(() => document.title);
1311
1312
  }
1312
- async rafrafTimeout(timeout) {
1313
+ async rafrafTimeout(progress, timeout) {
1313
1314
  if (timeout === 0)
1314
1315
  return;
1315
- const context = await this._utilityContext();
1316
+ const context = await progress.race(this._utilityContext());
1316
1317
  await Promise.all([
1317
1318
  // wait for double raf
1318
- context.evaluate(() => new Promise((x) => {
1319
+ progress.race(context.evaluate(() => new Promise((x) => {
1319
1320
  requestAnimationFrame(() => {
1320
1321
  requestAnimationFrame(x);
1321
1322
  });
1322
- })),
1323
- new Promise((fulfill) => setTimeout(fulfill, timeout))
1323
+ }))),
1324
+ progress.wait(timeout)
1324
1325
  ]);
1325
1326
  }
1326
1327
  _onDetached() {
@@ -66,7 +66,7 @@ class Helper {
66
66
  });
67
67
  const dispose = () => import_eventsHelper.eventsHelper.removeEventListeners(listeners);
68
68
  progress.cleanupWhenAborted(dispose);
69
- return { promise, dispose };
69
+ return { promise: progress.race(promise), dispose };
70
70
  }
71
71
  static secondsToRoundishMillis(value) {
72
72
  return (value * 1e6 | 0) / 1e3;