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
@@ -27,6 +27,7 @@ var import_download = require("./download");
27
27
  var import_instrumentation = require("./instrumentation");
28
28
  var import_page = require("./page");
29
29
  var import_socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
30
+ var import_progress = require("./progress");
30
31
  class Browser extends import_instrumentation.SdkObject {
31
32
  constructor(parent, options) {
32
33
  super(parent, "browser");
@@ -48,25 +49,25 @@ class Browser extends import_instrumentation.SdkObject {
48
49
  sdkLanguage() {
49
50
  return this.options.sdkLanguage || this.attribution.playwright.options.sdkLanguage;
50
51
  }
51
- async newContext(metadata, options) {
52
+ newContextFromMetadata(metadata, options) {
53
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
54
+ return controller.run((progress) => this.newContext(progress, options));
55
+ }
56
+ async newContext(progress, options) {
52
57
  (0, import_browserContext.validateBrowserContextOptions)(options, this.options);
53
58
  let clientCertificatesProxy;
54
59
  if (options.clientCertificates?.length) {
55
- clientCertificatesProxy = new import_socksClientCertificatesInterceptor.ClientCertificatesProxy(options);
60
+ clientCertificatesProxy = await progress.raceWithCleanup(import_socksClientCertificatesInterceptor.ClientCertificatesProxy.create(options), (proxy) => proxy.close());
56
61
  options = { ...options };
57
- options.proxyOverride = await clientCertificatesProxy.listen();
62
+ options.proxyOverride = clientCertificatesProxy.proxySettings();
58
63
  options.internalIgnoreHTTPSErrors = true;
59
64
  }
60
- let context;
61
- try {
62
- context = await this.doCreateNewContext(options);
63
- } catch (error) {
64
- await clientCertificatesProxy?.close();
65
- throw error;
66
- }
65
+ const context = await progress.raceWithCleanup(this.doCreateNewContext(options), (context2) => context2.close({ reason: "Failed to create context" }));
67
66
  context._clientCertificatesProxy = clientCertificatesProxy;
67
+ if (options.__testHookBeforeSetStorageState)
68
+ await progress.race(options.__testHookBeforeSetStorageState());
68
69
  if (options.storageState)
69
- await context.setStorageState(metadata, options.storageState);
70
+ await context.setStorageState(progress, options.storageState);
70
71
  this.emit(Browser.Events.Context, context);
71
72
  return context;
72
73
  }
@@ -75,7 +76,7 @@ class Browser extends import_instrumentation.SdkObject {
75
76
  if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) {
76
77
  if (this._contextForReuse)
77
78
  await this._contextForReuse.context.close({ reason: "Context reused" });
78
- this._contextForReuse = { context: await this.newContext(metadata, params), hash };
79
+ this._contextForReuse = { context: await this.newContextFromMetadata(metadata, params), hash };
79
80
  return { context: this._contextForReuse.context, needsReset: false };
80
81
  }
81
82
  await this._contextForReuse.context.stopPendingOperations("Context recreated");
@@ -160,11 +160,11 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
160
160
  return JSON.stringify(paramsCopy);
161
161
  }
162
162
  async resetForReuse(metadata, params) {
163
- const controller = new import_progress.ProgressController(metadata, this);
163
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
164
164
  return controller.run((progress) => this.resetForReuseImpl(progress, params));
165
165
  }
166
166
  async resetForReuseImpl(progress, params) {
167
- await this.tracing.resetForReuse();
167
+ await progress.race(this.tracing.resetForReuse());
168
168
  if (params) {
169
169
  for (const key of paramsThatAllowContextReuse)
170
170
  this._options[key] = params[key];
@@ -180,18 +180,21 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
180
180
  page = void 0;
181
181
  }
182
182
  await page?.mainFrame().gotoImpl(progress, "about:blank", {});
183
- await this._resetStorage();
184
- await this.clock.resetForReuse();
185
- if (this._options.permissions)
186
- await this.grantPermissions(this._options.permissions);
187
- else
188
- await this.clearPermissions();
189
- await this.setExtraHTTPHeaders(this._options.extraHTTPHeaders || []);
190
- await this.setGeolocation(this._options.geolocation);
191
- await this.setOffline(!!this._options.offline);
192
- await this.setUserAgent(this._options.userAgent);
193
- await this.clearCache();
194
- await this._resetCookies();
183
+ await this._resetStorage(progress);
184
+ const resetOptions = async () => {
185
+ await this.clock.resetForReuse();
186
+ if (this._options.permissions)
187
+ await this.grantPermissions(this._options.permissions);
188
+ else
189
+ await this.clearPermissions();
190
+ await this.setExtraHTTPHeaders(this._options.extraHTTPHeaders || []);
191
+ await this.setGeolocation(this._options.geolocation);
192
+ await this.setOffline(!!this._options.offline);
193
+ await this.setUserAgent(this._options.userAgent);
194
+ await this.clearCache();
195
+ await this._resetCookies();
196
+ };
197
+ await progress.race(resetOptions());
195
198
  await page?.resetForReuse(progress);
196
199
  }
197
200
  _browserClosed() {
@@ -297,13 +300,12 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
297
300
  async _loadDefaultContextAsIs(progress) {
298
301
  if (!this.possiblyUninitializedPages().length) {
299
302
  const waitForEvent = import_helper.helper.waitForEvent(progress, this, BrowserContext.Events.Page);
300
- progress.cleanupWhenAborted(() => waitForEvent.dispose);
301
303
  await Promise.race([waitForEvent.promise, this._closePromise]);
302
304
  }
303
305
  const page = this.possiblyUninitializedPages()[0];
304
306
  if (!page)
305
307
  return;
306
- const pageOrError = await page.waitForInitializedOrError();
308
+ const pageOrError = await progress.race(page.waitForInitializedOrError());
307
309
  if (pageOrError instanceof Error)
308
310
  throw pageOrError;
309
311
  await page.mainFrame()._waitForLoadState(progress, "load");
@@ -315,7 +317,7 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
315
317
  return;
316
318
  const browserName = this._browser.options.name;
317
319
  if (this._options.isMobile && browserName === "chromium" || this._options.locale && browserName === "webkit") {
318
- await this.newPage(progress.metadata);
320
+ await this.newPage(progress, false);
319
321
  await defaultPage.close();
320
322
  }
321
323
  }
@@ -402,9 +404,13 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
402
404
  }
403
405
  await this._closePromise;
404
406
  }
405
- async newPage(metadata) {
406
- const page = await this.doCreateNewPage(metadata.isServerSide);
407
- const pageOrError = await page.waitForInitializedOrError();
407
+ newPageFromMetadata(metadata) {
408
+ const contoller = new import_progress.ProgressController(metadata, this, "strict");
409
+ return contoller.run((progress) => this.newPage(progress, false));
410
+ }
411
+ async newPage(progress, isServerSide) {
412
+ const page = await progress.raceWithCleanup(this.doCreateNewPage(isServerSide), (page2) => page2.close());
413
+ const pageOrError = await progress.race(page.waitForInitializedOrError());
408
414
  if (pageOrError instanceof import_page2.Page) {
409
415
  if (pageOrError.isClosed())
410
416
  throw new Error("Page has been closed.");
@@ -415,7 +421,11 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
415
421
  addVisitedOrigin(origin) {
416
422
  this._origins.add(origin);
417
423
  }
418
- async storageState(indexedDB = false) {
424
+ storageState(indexedDB = false) {
425
+ const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this, "strict");
426
+ return controller.run((progress) => this.storageStateImpl(progress, indexedDB));
427
+ }
428
+ async storageStateImpl(progress, indexedDB) {
419
429
  const result = {
420
430
  cookies: await this.cookies(),
421
431
  origins: []
@@ -440,16 +450,15 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
440
450
  }
441
451
  }
442
452
  if (originsToSave.size) {
443
- const internalMetadata = (0, import_instrumentation.serverSideCallMetadata)();
444
- const page = await this.newPage(internalMetadata);
445
- page.addRequestInterceptor((route) => {
453
+ const page = await this.newPage(progress, true);
454
+ await progress.race(page.addRequestInterceptor((route) => {
446
455
  route.fulfill({ body: "<html></html>" }).catch(() => {
447
456
  });
448
- }, "prepend");
457
+ }, "prepend"));
449
458
  for (const origin of originsToSave) {
450
459
  const frame = page.mainFrame();
451
- await frame.goto(internalMetadata, origin, { timeout: 0 });
452
- const storage = await frame.evaluateExpression(collectScript, { world: "utility" });
460
+ await frame.gotoImpl(progress, origin, {});
461
+ const storage = await progress.race(frame.evaluateExpression(collectScript, { world: "utility" }));
453
462
  if (storage.localStorage.length || storage.indexedDB?.length)
454
463
  result.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB });
455
464
  }
@@ -457,28 +466,23 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
457
466
  }
458
467
  return result;
459
468
  }
460
- async _resetStorage() {
469
+ async _resetStorage(progress) {
461
470
  const oldOrigins = this._origins;
462
471
  const newOrigins = new Map(this._options.storageState?.origins?.map((p) => [p.origin, p]) || []);
463
472
  if (!oldOrigins.size && !newOrigins.size)
464
473
  return;
465
474
  let page = this.pages()[0];
466
- const internalMetadata = (0, import_instrumentation.serverSideCallMetadata)();
467
- page = page || await this.newPage({
468
- ...internalMetadata,
469
- // Do not mark this page as internal, because we will leave it for later reuse
470
- // as a user-visible page.
471
- isServerSide: false
472
- });
475
+ page = page || await this.newPage(progress, false);
473
476
  const interceptor = (route) => {
474
477
  route.fulfill({ body: "<html></html>" }).catch(() => {
475
478
  });
476
479
  };
477
- await page.addRequestInterceptor(interceptor, "prepend");
480
+ progress.cleanupWhenAborted(() => page.removeRequestInterceptor(interceptor));
481
+ await progress.race(page.addRequestInterceptor(interceptor, "prepend"));
478
482
  for (const origin of /* @__PURE__ */ new Set([...oldOrigins, ...newOrigins.keys()])) {
479
483
  const frame = page.mainFrame();
480
- await frame.goto(internalMetadata, origin, { timeout: 0 });
481
- await frame.resetStorageForCurrentOriginBestEffort(newOrigins.get(origin));
484
+ await frame.gotoImpl(progress, origin, {});
485
+ await progress.race(frame.resetStorageForCurrentOriginBestEffort(newOrigins.get(origin)));
482
486
  }
483
487
  await page.removeRequestInterceptor(interceptor);
484
488
  this._origins = /* @__PURE__ */ new Set([...newOrigins.keys()]);
@@ -491,28 +495,27 @@ if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { co
491
495
  isSettingStorageState() {
492
496
  return this._settingStorageState;
493
497
  }
494
- async setStorageState(metadata, state) {
498
+ async setStorageState(progress, state) {
495
499
  this._settingStorageState = true;
496
500
  try {
497
501
  if (state.cookies)
498
- await this.addCookies(state.cookies);
502
+ await progress.race(this.addCookies(state.cookies));
499
503
  if (state.origins && state.origins.length) {
500
- const internalMetadata = (0, import_instrumentation.serverSideCallMetadata)();
501
- const page = await this.newPage(internalMetadata);
502
- await page.addRequestInterceptor((route) => {
504
+ const page = await this.newPage(progress, true);
505
+ await progress.race(page.addRequestInterceptor((route) => {
503
506
  route.fulfill({ body: "<html></html>" }).catch(() => {
504
507
  });
505
- }, "prepend");
508
+ }, "prepend"));
506
509
  for (const originState of state.origins) {
507
510
  const frame = page.mainFrame();
508
- await frame.goto(metadata, originState.origin, { timeout: 0 });
511
+ await frame.gotoImpl(progress, originState.origin, {});
509
512
  const restoreScript = `(() => {
510
513
  const module = {};
511
514
  ${rawStorageSource.source}
512
515
  const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"});
513
516
  return script.restore(${JSON.stringify(originState)});
514
517
  })()`;
515
- await frame.evaluateExpression(restoreScript, { world: "utility" });
518
+ await progress.race(frame.evaluateExpression(restoreScript, { world: "utility" }));
516
519
  }
517
520
  await page.close();
518
521
  }
@@ -67,7 +67,7 @@ class BrowserType extends import_instrumentation.SdkObject {
67
67
  }
68
68
  async launch(metadata, options, protocolLogger) {
69
69
  options = this._validateLaunchOptions(options);
70
- const controller = new import_progress.ProgressController(metadata, this);
70
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
71
71
  const browser = await controller.run((progress) => {
72
72
  const seleniumHubUrl = options.__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL;
73
73
  if (seleniumHubUrl)
@@ -80,16 +80,15 @@ class BrowserType extends import_instrumentation.SdkObject {
80
80
  }
81
81
  async launchPersistentContext(metadata, userDataDir, options) {
82
82
  const launchOptions = this._validateLaunchOptions(options);
83
- const controller = new import_progress.ProgressController(metadata, this);
83
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
84
84
  const browser = await controller.run(async (progress) => {
85
85
  let clientCertificatesProxy;
86
86
  if (options.clientCertificates?.length) {
87
- clientCertificatesProxy = new import_socksClientCertificatesInterceptor.ClientCertificatesProxy(options);
88
- launchOptions.proxyOverride = await clientCertificatesProxy?.listen();
87
+ clientCertificatesProxy = await progress.raceWithCleanup(import_socksClientCertificatesInterceptor.ClientCertificatesProxy.create(options), (proxy) => proxy.close());
88
+ launchOptions.proxyOverride = clientCertificatesProxy.proxySettings();
89
89
  options = { ...options };
90
90
  options.internalIgnoreHTTPSErrors = true;
91
91
  }
92
- progress.cleanupWhenAborted(() => clientCertificatesProxy?.close());
93
92
  const browser2 = await this._innerLaunchWithRetries(progress, launchOptions, options, import_helper.helper.debugProtocolLogger(), userDataDir).catch((e) => {
94
93
  throw this._rewriteStartupLog(e);
95
94
  });
@@ -115,7 +114,7 @@ class BrowserType extends import_instrumentation.SdkObject {
115
114
  const browserLogsCollector = new import_debugLogger.RecentLogsCollector();
116
115
  const { browserProcess, userDataDir, artifactsDir, transport } = await this._launchProcess(progress, options, !!persistent, browserLogsCollector, maybeUserDataDir);
117
116
  if (options.__testHookBeforeCreateBrowser)
118
- await options.__testHookBeforeCreateBrowser();
117
+ await progress.race(options.__testHookBeforeCreateBrowser());
119
118
  const browserOptions = {
120
119
  name: this._name,
121
120
  isChromium: this._name === "chromium",
@@ -137,23 +136,19 @@ class BrowserType extends import_instrumentation.SdkObject {
137
136
  if (persistent)
138
137
  (0, import_browserContext.validateBrowserContextOptions)(persistent, browserOptions);
139
138
  copyTestHooks(options, browserOptions);
140
- const browser = await this.connectToTransport(transport, browserOptions, browserLogsCollector);
139
+ const browser = await progress.race(this.connectToTransport(transport, browserOptions, browserLogsCollector));
141
140
  browser._userDataDirForTest = userDataDir;
142
141
  if (persistent && !options.ignoreAllDefaultArgs)
143
142
  await browser._defaultContext._loadDefaultContext(progress);
144
143
  return browser;
145
144
  }
146
- async _launchProcess(progress, options, isPersistent, browserLogsCollector, userDataDir) {
145
+ async _prepareToLaunch(options, isPersistent, userDataDir) {
147
146
  const {
148
147
  ignoreDefaultArgs,
149
148
  ignoreAllDefaultArgs,
150
149
  args = [],
151
- executablePath = null,
152
- handleSIGINT = true,
153
- handleSIGTERM = true,
154
- handleSIGHUP = true
150
+ executablePath = null
155
151
  } = options;
156
- const env = options.env ? (0, import_processLauncher.envArrayToObject)(options.env) : process.env;
157
152
  await this._createArtifactDirs(options);
158
153
  const tempDirectories = [];
159
154
  const artifactsDir = await import_fs.default.promises.mkdtemp(import_path.default.join(import_os.default.tmpdir(), "playwright-artifacts-"));
@@ -186,13 +181,24 @@ class BrowserType extends import_instrumentation.SdkObject {
186
181
  executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage);
187
182
  await import_registry.registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage);
188
183
  }
184
+ return { executable, browserArguments, userDataDir, artifactsDir, tempDirectories };
185
+ }
186
+ async _launchProcess(progress, options, isPersistent, browserLogsCollector, userDataDir) {
187
+ const {
188
+ handleSIGINT = true,
189
+ handleSIGTERM = true,
190
+ handleSIGHUP = true
191
+ } = options;
192
+ const env = options.env ? (0, import_processLauncher.envArrayToObject)(options.env) : process.env;
193
+ const prepared = await progress.race(this._prepareToLaunch(options, isPersistent, userDataDir));
194
+ progress.cleanupWhenAborted(() => (0, import_fileUtils.removeFolders)(prepared.tempDirectories));
189
195
  let transport = void 0;
190
196
  let browserProcess = void 0;
191
197
  const exitPromise = new import_manualPromise.ManualPromise();
192
198
  const { launchedProcess, gracefullyClose, kill } = await (0, import_processLauncher.launchProcess)({
193
- command: executable,
194
- args: browserArguments,
195
- env: this.amendEnvironment(env, userDataDir, executable, browserArguments),
199
+ command: prepared.executable,
200
+ args: prepared.browserArguments,
201
+ env: this.amendEnvironment(env, prepared.userDataDir, prepared.executable, prepared.browserArguments),
196
202
  handleSIGINT,
197
203
  handleSIGTERM,
198
204
  handleSIGHUP,
@@ -201,7 +207,7 @@ class BrowserType extends import_instrumentation.SdkObject {
201
207
  browserLogsCollector.log(message);
202
208
  },
203
209
  stdio: "pipe",
204
- tempDirectories,
210
+ tempDirectories: prepared.tempDirectories,
205
211
  attemptToGracefullyClose: async () => {
206
212
  if (options.__testHookGracefullyClose)
207
213
  await options.__testHookGracefullyClose();
@@ -234,7 +240,7 @@ class BrowserType extends import_instrumentation.SdkObject {
234
240
  kill
235
241
  };
236
242
  progress.cleanupWhenAborted(() => closeOrKill(progress.timeUntilDeadline()));
237
- const { wsEndpoint } = await Promise.race([
243
+ const { wsEndpoint } = await progress.race([
238
244
  this.waitForReadyState(options, browserLogsCollector),
239
245
  exitPromise.then(() => ({ wsEndpoint: void 0 }))
240
246
  ]);
@@ -244,7 +250,8 @@ class BrowserType extends import_instrumentation.SdkObject {
244
250
  const stdio = launchedProcess.stdio;
245
251
  transport = new import_pipeTransport.PipeTransport(stdio[3], stdio[4]);
246
252
  }
247
- return { browserProcess, artifactsDir, userDataDir, transport };
253
+ progress.cleanupWhenAborted(() => transport.close());
254
+ return { browserProcess, artifactsDir: prepared.artifactsDir, userDataDir: prepared.userDataDir, transport };
248
255
  }
249
256
  async _createArtifactDirs(options) {
250
257
  if (options.downloadsPath)
@@ -62,7 +62,7 @@ class Chromium extends import_browserType.BrowserType {
62
62
  this._devtools = this._createDevTools();
63
63
  }
64
64
  async connectOverCDP(metadata, endpointURL, options) {
65
- const controller = new import_progress.ProgressController(metadata, this);
65
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
66
66
  return controller.run(async (progress) => {
67
67
  return await this._connectOverCDPInternal(progress, endpointURL, options);
68
68
  }, options.timeout);
@@ -75,10 +75,10 @@ class Chromium extends import_browserType.BrowserType {
75
75
  headersMap = { "User-Agent": (0, import_userAgent.getUserAgent)() };
76
76
  else if (headersMap && !Object.keys(headersMap).some((key) => key.toLowerCase() === "user-agent"))
77
77
  headersMap["User-Agent"] = (0, import_userAgent.getUserAgent)();
78
- const artifactsDir = await import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
78
+ const artifactsDir = await progress.race(import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER));
79
79
  const wsEndpoint = await urlToWSEndpoint(progress, endpointURL, headersMap);
80
- progress.throwIfAborted();
81
80
  const chromeTransport = await import_transport.WebSocketTransport.connect(progress, wsEndpoint, { headers: headersMap });
81
+ progress.cleanupWhenAborted(() => chromeTransport.close());
82
82
  const cleanedUp = new import_manualPromise.ManualPromise();
83
83
  const doCleanup = async () => {
84
84
  await (0, import_fileUtils.removeFolders)([artifactsDir]);
@@ -105,8 +105,7 @@ class Chromium extends import_browserType.BrowserType {
105
105
  originalLaunchOptions: { timeout: options.timeout }
106
106
  };
107
107
  (0, import_browserContext.validateBrowserContextOptions)(persistent, browserOptions);
108
- progress.throwIfAborted();
109
- const browser = await import_crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
108
+ const browser = await progress.race(import_crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions));
110
109
  browser._isCollocatedWithServer = false;
111
110
  browser.on(import_browser.Browser.Events.Disconnected, doCleanup);
112
111
  return browser;
@@ -158,7 +157,7 @@ class Chromium extends import_browserType.BrowserType {
158
157
  transport.send(message);
159
158
  }
160
159
  async _launchWithSeleniumHub(progress, hubUrl, options) {
161
- await this._createArtifactDirs(options);
160
+ await progress.race(this._createArtifactDirs(options));
162
161
  if (!hubUrl.endsWith("/"))
163
162
  hubUrl = hubUrl + "/";
164
163
  const args = this._innerDefaultArgs(options);
@@ -340,12 +339,15 @@ async function urlToWSEndpoint(progress, endpointURL, headers) {
340
339
  return endpointURL;
341
340
  progress.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
342
341
  const url = new URL(endpointURL);
342
+ if (!url.pathname.endsWith("/"))
343
+ url.pathname = url.pathname + "/";
343
344
  url.pathname += "json/version/";
344
345
  const httpURL = url.toString();
345
346
  const json = await (0, import_network.fetchData)(
346
347
  {
347
348
  url: httpURL,
348
- headers
349
+ headers,
350
+ timeout: progress.timeUntilDeadline()
349
351
  },
350
352
  async (_, resp) => new Error(`Unexpected status ${resp.statusCode} when connecting to ${httpURL}.
351
353
  This does not look like a DevTools server, try connecting via ws://.`)
@@ -44,10 +44,11 @@ class VideoRecorder {
44
44
  static async launch(page, ffmpegPath, options) {
45
45
  if (!options.outputFile.endsWith(".webm"))
46
46
  throw new Error("File must have .webm extension");
47
- const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), page);
47
+ const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), page, "strict");
48
48
  controller.setLogName("browser");
49
49
  return await controller.run(async (progress) => {
50
50
  const recorder = new VideoRecorder(page, ffmpegPath, progress);
51
+ progress.cleanupWhenAborted(() => recorder.stop());
51
52
  await recorder._launch(options);
52
53
  return recorder;
53
54
  });
@@ -92,7 +92,7 @@ class DebugController extends import_instrumentation.SdkObject {
92
92
  if (!pages.length) {
93
93
  const [browser] = this._playwright.allBrowsers();
94
94
  const { context } = await browser.newContextForReuse({}, internalMetadata);
95
- await context.newPage(internalMetadata);
95
+ await context.newPageFromMetadata(internalMetadata);
96
96
  }
97
97
  if (params.testIdAttributeName) {
98
98
  for (const page of this._playwright.allPages())
@@ -136,19 +136,19 @@ class AndroidDeviceDispatcher extends import_dispatcher.Dispatcher {
136
136
  async push(params) {
137
137
  await this._object.push(params.file, params.path, params.mode);
138
138
  }
139
- async launchBrowser(params) {
139
+ async launchBrowser(params, metadata) {
140
140
  if (this.parentScope()._denyLaunch)
141
141
  throw new Error(`Launching more browsers is not allowed.`);
142
- const context = await this._object.launchBrowser(params.pkg, params);
142
+ const context = await this._object.launchBrowser(metadata, params.pkg, params);
143
143
  return { context: import_browserContextDispatcher.BrowserContextDispatcher.from(this, context) };
144
144
  }
145
145
  async close(params) {
146
146
  await this._object.close();
147
147
  }
148
- async connectToWebView(params) {
148
+ async connectToWebView(params, metadata) {
149
149
  if (this.parentScope()._denyLaunch)
150
150
  throw new Error(`Launching more browsers is not allowed.`);
151
- return { context: import_browserContextDispatcher.BrowserContextDispatcher.from(this, await this._object.connectToWebView(params.socketName)) };
151
+ return { context: import_browserContextDispatcher.BrowserContextDispatcher.from(this, await this._object.connectToWebView(metadata, params.socketName)) };
152
152
  }
153
153
  }
154
154
  class SocketSdkObject extends import_instrumentation.SdkObject {
@@ -221,7 +221,7 @@ class BrowserContextDispatcher extends import_dispatcher.Dispatcher {
221
221
  this._bindings.push(binding);
222
222
  }
223
223
  async newPage(params, metadata) {
224
- return { page: import_pageDispatcher.PageDispatcher.from(this, await this._context.newPage(metadata)) };
224
+ return { page: import_pageDispatcher.PageDispatcher.from(this, await this._context.newPageFromMetadata(metadata)) };
225
225
  }
226
226
  async cookies(params) {
227
227
  return { cookies: await this._context.cookies(params.urls) };
@@ -48,13 +48,13 @@ class BrowserDispatcher extends import_dispatcher.Dispatcher {
48
48
  }
49
49
  async newContext(params, metadata) {
50
50
  if (!this._options.isolateContexts) {
51
- const context2 = await this._object.newContext(metadata, params);
51
+ const context2 = await this._object.newContextFromMetadata(metadata, params);
52
52
  const contextDispatcher2 = import_browserContextDispatcher.BrowserContextDispatcher.from(this, context2);
53
53
  return { context: contextDispatcher2 };
54
54
  }
55
55
  if (params.recordVideo)
56
56
  params.recordVideo.dir = this._object.options.artifactsDir;
57
- const context = await this._object.newContext(metadata, params);
57
+ const context = await this._object.newContextFromMetadata(metadata, params);
58
58
  this._isolatedContexts.add(context);
59
59
  context.on(import_browserContext.BrowserContext.Events.Close, () => this._isolatedContexts.delete(context));
60
60
  const contextDispatcher = import_browserContextDispatcher.BrowserContextDispatcher.from(this, context);
@@ -32,10 +32,10 @@ class ElectronDispatcher extends import_dispatcher.Dispatcher {
32
32
  this._type_Electron = true;
33
33
  this._denyLaunch = denyLaunch;
34
34
  }
35
- async launch(params) {
35
+ async launch(params, metadata) {
36
36
  if (this._denyLaunch)
37
37
  throw new Error(`Launching more browsers is not allowed.`);
38
- const electronApplication = await this._object.launch(params);
38
+ const electronApplication = await this._object.launch(metadata, params);
39
39
  return { electronApplication: new ElectronApplicationDispatcher(this, electronApplication) };
40
40
  }
41
41
  }
@@ -196,7 +196,7 @@ class FrameDispatcher extends import_dispatcher.Dispatcher {
196
196
  return await this._frame.waitForTimeout(metadata, params.timeout);
197
197
  }
198
198
  async waitForFunction(params, metadata) {
199
- return { handle: import_elementHandlerDispatcher.ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, (0, import_jsHandleDispatcher.parseArgument)(params.arg), params)) };
199
+ return { handle: import_elementHandlerDispatcher.ElementHandleDispatcher.fromJSOrElementHandle(this, await this._frame.waitForFunctionExpression(metadata, params.expression, params.isFunction, (0, import_jsHandleDispatcher.parseArgument)(params.arg), params)) };
200
200
  }
201
201
  async title(params, metadata) {
202
202
  return { value: await this._frame.title() };
@@ -79,7 +79,7 @@ class LocalUtilsDispatcher extends import_dispatcher.Dispatcher {
79
79
  return await localUtils.addStackToTracingNoReply(this._stackSessions, params);
80
80
  }
81
81
  async connect(params, metadata) {
82
- const controller = new import_progress.ProgressController(metadata, this._object);
82
+ const controller = new import_progress.ProgressController(metadata, this._object, "strict");
83
83
  return await controller.run(async (progress) => {
84
84
  const wsHeaders = {
85
85
  "User-Agent": (0, import_userAgent.getUserAgent)(),
@@ -138,7 +138,6 @@ async function urlToWSEndpoint(progress, endpointURL) {
138
138
  return new Error(`Unexpected status ${response.statusCode} when connecting to ${fetchUrl.toString()}.
139
139
  This does not look like a Playwright server, try connecting via ws://.`);
140
140
  });
141
- progress.throwIfAborted();
142
141
  const wsUrl = new URL(endpointURL);
143
142
  let wsEndpointPath = JSON.parse(json).wsEndpointPath;
144
143
  if (wsEndpointPath.startsWith("/"))
@@ -39,15 +39,15 @@ class PlaywrightDispatcher extends import_dispatcher.Dispatcher {
39
39
  const chromium = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.chromium, denyLaunch);
40
40
  const firefox = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.firefox, denyLaunch);
41
41
  const webkit = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.webkit, denyLaunch);
42
- const bidiChromium = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidiChromium, denyLaunch);
43
- const bidiFirefox = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidiFirefox, denyLaunch);
42
+ const _bidiChromium = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright._bidiChromium, denyLaunch);
43
+ const _bidiFirefox = new import_browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright._bidiFirefox, denyLaunch);
44
44
  const android = new import_androidDispatcher.AndroidDispatcher(scope, playwright.android, denyLaunch);
45
45
  const initializer = {
46
46
  chromium,
47
47
  firefox,
48
48
  webkit,
49
- bidiChromium,
50
- bidiFirefox,
49
+ _bidiChromium,
50
+ _bidiFirefox,
51
51
  android,
52
52
  electron: new import_electronDispatcher.ElectronDispatcher(scope, playwright.electron, denyLaunch),
53
53
  utils: playwright.options.isServer ? void 0 : new import_localUtilsDispatcher.LocalUtilsDispatcher(scope, playwright),
@@ -55,23 +55,7 @@ class PlaywrightDispatcher extends import_dispatcher.Dispatcher {
55
55
  };
56
56
  let browserDispatcher;
57
57
  if (options.preLaunchedBrowser) {
58
- let browserTypeDispatcher;
59
- switch (options.preLaunchedBrowser.options.name) {
60
- case "chromium":
61
- browserTypeDispatcher = chromium;
62
- break;
63
- case "firefox":
64
- browserTypeDispatcher = firefox;
65
- break;
66
- case "webkit":
67
- browserTypeDispatcher = webkit;
68
- break;
69
- case "bidi":
70
- browserTypeDispatcher = options.preLaunchedBrowser.options.channel?.includes("firefox") ? bidiFirefox : bidiChromium;
71
- break;
72
- default:
73
- throw new Error(`Unknown browser name: ${options.preLaunchedBrowser.options.name}`);
74
- }
58
+ const browserTypeDispatcher = initializer[options.preLaunchedBrowser.options.name];
75
59
  browserDispatcher = new import_browserDispatcher.BrowserDispatcher(browserTypeDispatcher, options.preLaunchedBrowser, {
76
60
  ignoreStopAndKill: true,
77
61
  isolateContexts: !options.sharedBrowser
package/lib/server/dom.js CHANGED
@@ -43,7 +43,6 @@ var js = __toESM(require("./javascript"));
43
43
  var import_progress = require("./progress");
44
44
  var import_utils = require("../utils");
45
45
  var import_fileUploadUtils = require("./fileUploadUtils");
46
- var import_protocolError = require("./protocolError");
47
46
  var rawInjectedScriptSource = __toESM(require("../generated/injectedScriptSource"));
48
47
  class NonRecoverableDOMError extends Error {
49
48
  }
@@ -124,7 +123,7 @@ class ElementHandle extends js.JSHandle {
124
123
  const utility = await this._frame._utilityContext();
125
124
  return await utility.evaluate(pageFunction, [await utility.injectedScript(), this, arg]);
126
125
  } catch (e) {
127
- if ((0, import_progress.isAbortError)(e) || js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
126
+ if (this._frame.isNonRetriableError(e))
128
127
  throw e;
129
128
  return "error:notconnected";
130
129
  }
@@ -134,7 +133,7 @@ class ElementHandle extends js.JSHandle {
134
133
  const utility = await this._frame._utilityContext();
135
134
  return await utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]);
136
135
  } catch (e) {
137
- if ((0, import_progress.isAbortError)(e) || js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
136
+ if (this._frame.isNonRetriableError(e))
138
137
  throw e;
139
138
  return "error:notconnected";
140
139
  }
@@ -732,7 +731,7 @@ class ElementHandle extends js.JSHandle {
732
731
  return await this.evaluateInUtility(([injected, element, options2]) => injected.ariaSnapshot(element, options2), options);
733
732
  }
734
733
  async screenshot(metadata, options) {
735
- const controller = new import_progress.ProgressController(metadata, this);
734
+ const controller = new import_progress.ProgressController(metadata, this, "strict");
736
735
  return controller.run(
737
736
  (progress) => this._page.screenshotter.screenshotElement(progress, this, options),
738
737
  options.timeout