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.
- package/lib/client/playwright.js +2 -2
- package/lib/inProcessFactory.js +2 -2
- package/lib/protocol/validator.js +2 -2
- package/lib/remote/playwrightConnection.js +27 -168
- package/lib/remote/playwrightServer.js +173 -27
- package/lib/server/android/android.js +31 -25
- package/lib/server/bidi/bidiChromium.js +1 -1
- package/lib/server/bidi/bidiFirefox.js +4 -1
- package/lib/server/browser.js +13 -12
- package/lib/server/browserContext.js +49 -46
- package/lib/server/browserType.js +26 -19
- package/lib/server/chromium/chromium.js +9 -7
- package/lib/server/chromium/videoRecorder.js +2 -1
- package/lib/server/debugController.js +1 -1
- package/lib/server/dispatchers/androidDispatcher.js +4 -4
- package/lib/server/dispatchers/browserContextDispatcher.js +1 -1
- package/lib/server/dispatchers/browserDispatcher.js +2 -2
- package/lib/server/dispatchers/electronDispatcher.js +2 -2
- package/lib/server/dispatchers/frameDispatcher.js +1 -1
- package/lib/server/dispatchers/localUtilsDispatcher.js +1 -2
- package/lib/server/dispatchers/playwrightDispatcher.js +5 -21
- package/lib/server/dom.js +3 -4
- package/lib/server/electron/electron.js +15 -16
- package/lib/server/fetch.js +12 -26
- package/lib/server/firefox/ffPage.js +0 -1
- package/lib/server/frames.js +93 -92
- package/lib/server/helper.js +1 -1
- package/lib/server/page.js +48 -42
- package/lib/server/playwright.js +2 -2
- package/lib/server/recorder/recorderApp.js +1 -1
- package/lib/server/registry/index.js +7 -20
- package/lib/server/screenshotter.js +17 -31
- package/lib/server/socksClientCertificatesInterceptor.js +7 -3
- package/lib/server/trace/viewer/traceViewer.js +1 -1
- package/lib/server/transport.js +3 -7
- package/lib/server/utils/processLauncher.js +1 -1
- package/lib/vite/htmlReport/index.html +9 -9
- package/package.json +1 -1
package/lib/server/browser.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
60
|
+
clientCertificatesProxy = await progress.raceWithCleanup(import_socksClientCertificatesInterceptor.ClientCertificatesProxy.create(options), (proxy) => proxy.close());
|
|
56
61
|
options = { ...options };
|
|
57
|
-
options.proxyOverride =
|
|
62
|
+
options.proxyOverride = clientCertificatesProxy.proxySettings();
|
|
58
63
|
options.internalIgnoreHTTPSErrors = true;
|
|
59
64
|
}
|
|
60
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
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
|
-
|
|
406
|
-
const
|
|
407
|
-
|
|
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
|
-
|
|
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
|
|
444
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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
|
|
501
|
-
|
|
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.
|
|
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 =
|
|
88
|
-
launchOptions.proxyOverride =
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
43
|
-
const
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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
|