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/client/playwright.js
CHANGED
|
@@ -43,9 +43,9 @@ class Playwright extends import_channelOwner.ChannelOwner {
|
|
|
43
43
|
this._android._playwright = this;
|
|
44
44
|
this._electron = import_electron.Electron.from(initializer.electron);
|
|
45
45
|
this._electron._playwright = this;
|
|
46
|
-
this._bidiChromium = import_browserType.BrowserType.from(initializer.
|
|
46
|
+
this._bidiChromium = import_browserType.BrowserType.from(initializer._bidiChromium);
|
|
47
47
|
this._bidiChromium._playwright = this;
|
|
48
|
-
this._bidiFirefox = import_browserType.BrowserType.from(initializer.
|
|
48
|
+
this._bidiFirefox = import_browserType.BrowserType.from(initializer._bidiFirefox);
|
|
49
49
|
this._bidiFirefox._playwright = this;
|
|
50
50
|
this.devices = this._connection.localUtils()?.devices ?? {};
|
|
51
51
|
this.selectors = new import_selectors.Selectors(this._connection._platform);
|
package/lib/inProcessFactory.js
CHANGED
|
@@ -43,8 +43,8 @@ function createInProcessPlaywright() {
|
|
|
43
43
|
playwrightAPI.firefox._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("firefox");
|
|
44
44
|
playwrightAPI.webkit._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("webkit");
|
|
45
45
|
playwrightAPI._android._serverLauncher = new import_androidServerImpl.AndroidServerLauncherImpl();
|
|
46
|
-
playwrightAPI._bidiChromium._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("
|
|
47
|
-
playwrightAPI._bidiFirefox._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("
|
|
46
|
+
playwrightAPI._bidiChromium._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("_bidiChromium");
|
|
47
|
+
playwrightAPI._bidiFirefox._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("_bidiFirefox");
|
|
48
48
|
dispatcherConnection.onmessage = (message) => setImmediate(() => clientConnection.dispatch(message));
|
|
49
49
|
clientConnection.onmessage = (message) => setImmediate(() => dispatcherConnection.dispatch(message));
|
|
50
50
|
clientConnection.toImpl = (x) => x ? dispatcherConnection._dispatcherByGuid.get(x._guid)._object : dispatcherConnection._dispatcherByGuid.get("");
|
|
@@ -382,8 +382,8 @@ import_validatorPrimitives.scheme.PlaywrightInitializer = (0, import_validatorPr
|
|
|
382
382
|
chromium: (0, import_validatorPrimitives.tChannel)(["BrowserType"]),
|
|
383
383
|
firefox: (0, import_validatorPrimitives.tChannel)(["BrowserType"]),
|
|
384
384
|
webkit: (0, import_validatorPrimitives.tChannel)(["BrowserType"]),
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
_bidiChromium: (0, import_validatorPrimitives.tChannel)(["BrowserType"]),
|
|
386
|
+
_bidiFirefox: (0, import_validatorPrimitives.tChannel)(["BrowserType"]),
|
|
387
387
|
android: (0, import_validatorPrimitives.tChannel)(["Android"]),
|
|
388
388
|
electron: (0, import_validatorPrimitives.tChannel)(["Electron"]),
|
|
389
389
|
utils: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tChannel)(["LocalUtils"])),
|
|
@@ -21,31 +21,22 @@ __export(playwrightConnection_exports, {
|
|
|
21
21
|
PlaywrightConnection: () => PlaywrightConnection
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(playwrightConnection_exports);
|
|
24
|
-
var import_socksProxy = require("../server/utils/socksProxy");
|
|
25
24
|
var import_server = require("../server");
|
|
26
25
|
var import_android = require("../server/android/android");
|
|
27
26
|
var import_browser = require("../server/browser");
|
|
28
27
|
var import_debugControllerDispatcher = require("../server/dispatchers/debugControllerDispatcher");
|
|
29
|
-
var import_instrumentation = require("../server/instrumentation");
|
|
30
|
-
var import_assert = require("../utils/isomorphic/assert");
|
|
31
|
-
var import_debug = require("../server/utils/debug");
|
|
32
28
|
var import_profiler = require("../server/utils/profiler");
|
|
33
29
|
var import_utils = require("../utils");
|
|
34
30
|
var import_debugLogger = require("../server/utils/debugLogger");
|
|
35
31
|
class PlaywrightConnection {
|
|
36
|
-
constructor(
|
|
32
|
+
constructor(semaphore, ws, controller, playwright, initialize, id) {
|
|
37
33
|
this._cleanups = [];
|
|
38
34
|
this._disconnected = false;
|
|
39
35
|
this._ws = ws;
|
|
40
|
-
this.
|
|
41
|
-
this._preLaunched = preLaunched;
|
|
42
|
-
this._options = options;
|
|
43
|
-
options.launchOptions = filterLaunchOptions(options.launchOptions, options.allowFSPaths);
|
|
44
|
-
if (clientType === "pre-launched-browser-or-android")
|
|
45
|
-
(0, import_assert.assert)(preLaunched.browser || preLaunched.androidDevice);
|
|
46
|
-
this._onClose = onClose;
|
|
36
|
+
this._semaphore = semaphore;
|
|
47
37
|
this._id = id;
|
|
48
|
-
this._profileName =
|
|
38
|
+
this._profileName = (/* @__PURE__ */ new Date()).toISOString();
|
|
39
|
+
const lock = this._semaphore.acquire();
|
|
49
40
|
this._dispatcherConnection = new import_server.DispatcherConnection();
|
|
50
41
|
this._dispatcherConnection.onmessage = async (message) => {
|
|
51
42
|
await lock;
|
|
@@ -70,125 +61,33 @@ class PlaywrightConnection {
|
|
|
70
61
|
});
|
|
71
62
|
ws.on("close", () => this._onDisconnect());
|
|
72
63
|
ws.on("error", (error) => this._onDisconnect(error));
|
|
73
|
-
if (
|
|
74
|
-
this.
|
|
64
|
+
if (controller) {
|
|
65
|
+
import_debugLogger.debugLogger.log("server", `[${this._id}] engaged reuse controller mode`);
|
|
66
|
+
this._root = new import_debugControllerDispatcher.DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
|
|
75
67
|
return;
|
|
76
68
|
}
|
|
77
|
-
this._root = new import_server.RootDispatcher(this._dispatcherConnection, async (scope,
|
|
69
|
+
this._root = new import_server.RootDispatcher(this._dispatcherConnection, async (scope, params) => {
|
|
78
70
|
await (0, import_profiler.startProfiling)();
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if ("bidi" === browserName) {
|
|
93
|
-
if (this._options.launchOptions?.channel?.toLocaleLowerCase().includes("firefox"))
|
|
94
|
-
browserName = "bidiFirefox";
|
|
95
|
-
else
|
|
96
|
-
browserName = "bidiChromium";
|
|
97
|
-
}
|
|
98
|
-
const browser = await this._playwright[browserName].launch((0, import_instrumentation.serverSideCallMetadata)(), this._options.launchOptions);
|
|
99
|
-
browser.options.sdkLanguage = options.sdkLanguage;
|
|
100
|
-
this._cleanups.push(() => browser.close({ reason: "Connection terminated" }));
|
|
101
|
-
browser.on(import_browser.Browser.Events.Disconnected, () => {
|
|
102
|
-
this.close({ code: 1001, reason: "Browser closed" });
|
|
103
|
-
});
|
|
104
|
-
return new import_server.PlaywrightDispatcher(scope, this._playwright, { socksProxy: ownedSocksProxy, preLaunchedBrowser: browser, denyLaunch: true });
|
|
105
|
-
}
|
|
106
|
-
async _initPreLaunchedBrowserMode(scope, options) {
|
|
107
|
-
import_debugLogger.debugLogger.log("server", `[${this._id}] engaged pre-launched (browser) mode`);
|
|
108
|
-
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
|
|
109
|
-
const browser = this._preLaunched.browser;
|
|
110
|
-
browser.options.sdkLanguage = options.sdkLanguage;
|
|
111
|
-
browser.on(import_browser.Browser.Events.Disconnected, () => {
|
|
112
|
-
this.close({ code: 1001, reason: "Browser closed" });
|
|
113
|
-
});
|
|
114
|
-
const playwrightDispatcher = new import_server.PlaywrightDispatcher(scope, this._playwright, {
|
|
115
|
-
socksProxy: this._preLaunched.socksProxy,
|
|
116
|
-
preLaunchedBrowser: browser,
|
|
117
|
-
sharedBrowser: this._options.sharedBrowser,
|
|
118
|
-
denyLaunch: true
|
|
119
|
-
});
|
|
120
|
-
for (const b of this._playwright.allBrowsers()) {
|
|
121
|
-
if (b !== browser)
|
|
122
|
-
await b.close({ reason: "Connection terminated" });
|
|
123
|
-
}
|
|
124
|
-
this._cleanups.push(() => playwrightDispatcher.cleanup());
|
|
125
|
-
return playwrightDispatcher;
|
|
126
|
-
}
|
|
127
|
-
async _initPreLaunchedAndroidMode(scope) {
|
|
128
|
-
import_debugLogger.debugLogger.log("server", `[${this._id}] engaged pre-launched (Android) mode`);
|
|
129
|
-
const androidDevice = this._preLaunched.androidDevice;
|
|
130
|
-
androidDevice.on(import_android.AndroidDevice.Events.Close, () => {
|
|
131
|
-
this.close({ code: 1001, reason: "Android device disconnected" });
|
|
132
|
-
});
|
|
133
|
-
const playwrightDispatcher = new import_server.PlaywrightDispatcher(scope, this._playwright, { preLaunchedAndroidDevice: androidDevice, denyLaunch: true });
|
|
134
|
-
this._cleanups.push(() => playwrightDispatcher.cleanup());
|
|
135
|
-
return playwrightDispatcher;
|
|
136
|
-
}
|
|
137
|
-
_initDebugControllerMode() {
|
|
138
|
-
import_debugLogger.debugLogger.log("server", `[${this._id}] engaged reuse controller mode`);
|
|
139
|
-
return new import_debugControllerDispatcher.DebugControllerDispatcher(this._dispatcherConnection, this._playwright.debugController);
|
|
140
|
-
}
|
|
141
|
-
async _initReuseBrowsersMode(scope, options) {
|
|
142
|
-
import_debugLogger.debugLogger.log("server", `[${this._id}] engaged reuse browsers mode for ${this._options.browserName}`);
|
|
143
|
-
const requestedOptions = launchOptionsHash(this._options.launchOptions);
|
|
144
|
-
let browser = this._playwright.allBrowsers().find((b) => {
|
|
145
|
-
if (b.options.name !== this._options.browserName)
|
|
146
|
-
return false;
|
|
147
|
-
const existingOptions = launchOptionsHash(b.options.originalLaunchOptions);
|
|
148
|
-
return existingOptions === requestedOptions;
|
|
149
|
-
});
|
|
150
|
-
for (const b of this._playwright.allBrowsers()) {
|
|
151
|
-
if (b === browser)
|
|
152
|
-
continue;
|
|
153
|
-
if (b.options.name === this._options.browserName && b.options.channel === this._options.launchOptions.channel)
|
|
154
|
-
await b.close({ reason: "Connection terminated" });
|
|
155
|
-
}
|
|
156
|
-
if (!browser) {
|
|
157
|
-
browser = await this._playwright[this._options.browserName || "chromium"].launch((0, import_instrumentation.serverSideCallMetadata)(), {
|
|
158
|
-
...this._options.launchOptions,
|
|
159
|
-
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS
|
|
160
|
-
});
|
|
161
|
-
browser.on(import_browser.Browser.Events.Disconnected, () => {
|
|
162
|
-
this.close({ code: 1001, reason: "Browser closed" });
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
browser.options.sdkLanguage = options.sdkLanguage;
|
|
166
|
-
this._cleanups.push(async () => {
|
|
167
|
-
for (const browser2 of this._playwright.allBrowsers()) {
|
|
168
|
-
for (const context of browser2.contexts()) {
|
|
169
|
-
if (!context.pages().length)
|
|
170
|
-
await context.close({ reason: "Connection terminated" });
|
|
171
|
-
else
|
|
172
|
-
await context.stopPendingOperations("Connection closed");
|
|
173
|
-
}
|
|
174
|
-
if (!browser2.contexts())
|
|
175
|
-
await browser2.close({ reason: "Connection terminated" });
|
|
71
|
+
const options = await initialize();
|
|
72
|
+
if (options.preLaunchedBrowser) {
|
|
73
|
+
const browser = options.preLaunchedBrowser;
|
|
74
|
+
browser.options.sdkLanguage = params.sdkLanguage;
|
|
75
|
+
browser.on(import_browser.Browser.Events.Disconnected, () => {
|
|
76
|
+
this.close({ code: 1001, reason: "Browser closed" });
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (options.preLaunchedAndroidDevice) {
|
|
80
|
+
const androidDevice = options.preLaunchedAndroidDevice;
|
|
81
|
+
androidDevice.on(import_android.AndroidDevice.Events.Close, () => {
|
|
82
|
+
this.close({ code: 1001, reason: "Android device disconnected" });
|
|
83
|
+
});
|
|
176
84
|
}
|
|
85
|
+
if (options.dispose)
|
|
86
|
+
this._cleanups.push(options.dispose);
|
|
87
|
+
const dispatcher = new import_server.PlaywrightDispatcher(scope, playwright, options);
|
|
88
|
+
this._cleanups.push(() => dispatcher.cleanup());
|
|
89
|
+
return dispatcher;
|
|
177
90
|
});
|
|
178
|
-
const playwrightDispatcher = new import_server.PlaywrightDispatcher(scope, this._playwright, { preLaunchedBrowser: browser, denyLaunch: true });
|
|
179
|
-
return playwrightDispatcher;
|
|
180
|
-
}
|
|
181
|
-
async _createOwnedSocksProxy() {
|
|
182
|
-
if (!this._options.socksProxyPattern) {
|
|
183
|
-
this._options.launchOptions.socksProxyPort = void 0;
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const socksProxy = new import_socksProxy.SocksProxy();
|
|
187
|
-
socksProxy.setPattern(this._options.socksProxyPattern);
|
|
188
|
-
this._options.launchOptions.socksProxyPort = await socksProxy.listen(0);
|
|
189
|
-
import_debugLogger.debugLogger.log("server", `[${this._id}] started socks proxy on port ${this._options.launchOptions.socksProxyPort}`);
|
|
190
|
-
this._cleanups.push(() => socksProxy.close());
|
|
191
|
-
return socksProxy;
|
|
192
91
|
}
|
|
193
92
|
async _onDisconnect(error) {
|
|
194
93
|
this._disconnected = true;
|
|
@@ -199,7 +98,7 @@ class PlaywrightConnection {
|
|
|
199
98
|
await cleanup().catch(() => {
|
|
200
99
|
});
|
|
201
100
|
await (0, import_profiler.stopProfiling)(this._profileName);
|
|
202
|
-
this.
|
|
101
|
+
this._semaphore.release();
|
|
203
102
|
import_debugLogger.debugLogger.log("server", `[${this._id}] finished cleanup`);
|
|
204
103
|
}
|
|
205
104
|
logServerMetadata(message, messageString, direction) {
|
|
@@ -222,46 +121,6 @@ class PlaywrightConnection {
|
|
|
222
121
|
}
|
|
223
122
|
}
|
|
224
123
|
}
|
|
225
|
-
function launchOptionsHash(options) {
|
|
226
|
-
const copy = { ...options };
|
|
227
|
-
for (const k of Object.keys(copy)) {
|
|
228
|
-
const key = k;
|
|
229
|
-
if (copy[key] === defaultLaunchOptions[key])
|
|
230
|
-
delete copy[key];
|
|
231
|
-
}
|
|
232
|
-
for (const key of optionsThatAllowBrowserReuse)
|
|
233
|
-
delete copy[key];
|
|
234
|
-
return JSON.stringify(copy);
|
|
235
|
-
}
|
|
236
|
-
function filterLaunchOptions(options, allowFSPaths) {
|
|
237
|
-
return {
|
|
238
|
-
channel: options.channel,
|
|
239
|
-
args: options.args,
|
|
240
|
-
ignoreAllDefaultArgs: options.ignoreAllDefaultArgs,
|
|
241
|
-
ignoreDefaultArgs: options.ignoreDefaultArgs,
|
|
242
|
-
timeout: options.timeout,
|
|
243
|
-
headless: options.headless,
|
|
244
|
-
proxy: options.proxy,
|
|
245
|
-
chromiumSandbox: options.chromiumSandbox,
|
|
246
|
-
firefoxUserPrefs: options.firefoxUserPrefs,
|
|
247
|
-
slowMo: options.slowMo,
|
|
248
|
-
executablePath: (0, import_debug.isUnderTest)() || allowFSPaths ? options.executablePath : void 0,
|
|
249
|
-
downloadsPath: allowFSPaths ? options.downloadsPath : void 0
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
const defaultLaunchOptions = {
|
|
253
|
-
ignoreAllDefaultArgs: false,
|
|
254
|
-
handleSIGINT: false,
|
|
255
|
-
handleSIGTERM: false,
|
|
256
|
-
handleSIGHUP: false,
|
|
257
|
-
headless: true,
|
|
258
|
-
devtools: false
|
|
259
|
-
};
|
|
260
|
-
const optionsThatAllowBrowserReuse = [
|
|
261
|
-
"headless",
|
|
262
|
-
"timeout",
|
|
263
|
-
"tracesDir"
|
|
264
|
-
];
|
|
265
124
|
// Annotate the CommonJS export names for ESM import in node:
|
|
266
125
|
0 && (module.exports = {
|
|
267
126
|
PlaywrightConnection
|
|
@@ -28,6 +28,9 @@ var import_time = require("../utils/isomorphic/time");
|
|
|
28
28
|
var import_wsServer = require("../server/utils/wsServer");
|
|
29
29
|
var import_ascii = require("../server/utils/ascii");
|
|
30
30
|
var import_userAgent = require("../server/utils/userAgent");
|
|
31
|
+
var import_utils = require("../utils");
|
|
32
|
+
var import_server = require("../server");
|
|
33
|
+
var import_socksProxy = require("../server/utils/socksProxy");
|
|
31
34
|
class PlaywrightServer {
|
|
32
35
|
constructor(options) {
|
|
33
36
|
this._options = options;
|
|
@@ -75,41 +78,144 @@ ${uaError}` };
|
|
|
75
78
|
} catch (e) {
|
|
76
79
|
}
|
|
77
80
|
const isExtension = this._options.mode === "extension";
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (isExtension
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
const allowFSPaths = isExtension;
|
|
82
|
+
launchOptions = filterLaunchOptions(launchOptions, allowFSPaths);
|
|
83
|
+
if (isExtension) {
|
|
84
|
+
if (url.searchParams.has("debug-controller")) {
|
|
85
|
+
return new import_playwrightConnection.PlaywrightConnection(
|
|
86
|
+
controllerSemaphore,
|
|
87
|
+
ws,
|
|
88
|
+
true,
|
|
89
|
+
this._playwright,
|
|
90
|
+
async () => {
|
|
91
|
+
throw new Error("shouldnt be used");
|
|
92
|
+
},
|
|
93
|
+
id
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return new import_playwrightConnection.PlaywrightConnection(
|
|
97
|
+
reuseBrowserSemaphore,
|
|
98
|
+
ws,
|
|
99
|
+
false,
|
|
100
|
+
this._playwright,
|
|
101
|
+
() => this._initReuseBrowsersMode(browserName, launchOptions, id),
|
|
102
|
+
id
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (this._options.mode === "launchServer" || this._options.mode === "launchServerShared") {
|
|
106
|
+
if (this._options.preLaunchedBrowser) {
|
|
107
|
+
return new import_playwrightConnection.PlaywrightConnection(
|
|
108
|
+
browserSemaphore,
|
|
109
|
+
ws,
|
|
110
|
+
false,
|
|
111
|
+
this._playwright,
|
|
112
|
+
() => this._initPreLaunchedBrowserMode(id),
|
|
113
|
+
id
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return new import_playwrightConnection.PlaywrightConnection(
|
|
117
|
+
browserSemaphore,
|
|
118
|
+
ws,
|
|
119
|
+
false,
|
|
120
|
+
this._playwright,
|
|
121
|
+
() => this._initPreLaunchedAndroidMode(id),
|
|
122
|
+
id
|
|
123
|
+
);
|
|
89
124
|
}
|
|
90
125
|
return new import_playwrightConnection.PlaywrightConnection(
|
|
91
|
-
|
|
92
|
-
clientType,
|
|
126
|
+
browserSemaphore,
|
|
93
127
|
ws,
|
|
94
|
-
|
|
95
|
-
socksProxyPattern: proxyValue,
|
|
96
|
-
browserName,
|
|
97
|
-
launchOptions,
|
|
98
|
-
allowFSPaths: this._options.mode === "extension",
|
|
99
|
-
sharedBrowser: this._options.mode === "launchServerShared"
|
|
100
|
-
},
|
|
128
|
+
false,
|
|
101
129
|
this._playwright,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
androidDevice: this._options.preLaunchedAndroidDevice,
|
|
105
|
-
socksProxy: this._options.preLaunchedSocksProxy
|
|
106
|
-
},
|
|
107
|
-
id,
|
|
108
|
-
() => semaphore.release()
|
|
130
|
+
() => this._initLaunchBrowserMode(browserName, proxyValue, launchOptions, id),
|
|
131
|
+
id
|
|
109
132
|
);
|
|
110
133
|
}
|
|
111
134
|
});
|
|
112
135
|
}
|
|
136
|
+
async _initReuseBrowsersMode(browserName, launchOptions, id) {
|
|
137
|
+
import_utils.debugLogger.log("server", `[${id}] engaged reuse browsers mode for ${browserName}`);
|
|
138
|
+
const requestedOptions = launchOptionsHash(launchOptions);
|
|
139
|
+
let browser = this._playwright.allBrowsers().find((b) => {
|
|
140
|
+
if (b.options.name !== browserName)
|
|
141
|
+
return false;
|
|
142
|
+
const existingOptions = launchOptionsHash(b.options.originalLaunchOptions);
|
|
143
|
+
return existingOptions === requestedOptions;
|
|
144
|
+
});
|
|
145
|
+
for (const b of this._playwright.allBrowsers()) {
|
|
146
|
+
if (b === browser)
|
|
147
|
+
continue;
|
|
148
|
+
if (b.options.name === browserName && b.options.channel === launchOptions.channel)
|
|
149
|
+
await b.close({ reason: "Connection terminated" });
|
|
150
|
+
}
|
|
151
|
+
if (!browser) {
|
|
152
|
+
browser = await this._playwright[browserName || "chromium"].launch((0, import_server.serverSideCallMetadata)(), {
|
|
153
|
+
...launchOptions,
|
|
154
|
+
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
preLaunchedBrowser: browser,
|
|
159
|
+
denyLaunch: true,
|
|
160
|
+
dispose: async () => {
|
|
161
|
+
for (const browser2 of this._playwright.allBrowsers()) {
|
|
162
|
+
for (const context of browser2.contexts()) {
|
|
163
|
+
if (!context.pages().length)
|
|
164
|
+
await context.close({ reason: "Connection terminated" });
|
|
165
|
+
else
|
|
166
|
+
await context.stopPendingOperations("Connection closed");
|
|
167
|
+
}
|
|
168
|
+
if (!browser2.contexts())
|
|
169
|
+
await browser2.close({ reason: "Connection terminated" });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async _initPreLaunchedBrowserMode(id) {
|
|
175
|
+
import_utils.debugLogger.log("server", `[${id}] engaged pre-launched (browser) mode`);
|
|
176
|
+
const browser = this._options.preLaunchedBrowser;
|
|
177
|
+
for (const b of this._playwright.allBrowsers()) {
|
|
178
|
+
if (b !== browser)
|
|
179
|
+
await b.close({ reason: "Connection terminated" });
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
preLaunchedBrowser: browser,
|
|
183
|
+
socksProxy: this._options.preLaunchedSocksProxy,
|
|
184
|
+
sharedBrowser: this._options.mode === "launchServerShared",
|
|
185
|
+
denyLaunch: true
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async _initPreLaunchedAndroidMode(id) {
|
|
189
|
+
import_utils.debugLogger.log("server", `[${id}] engaged pre-launched (Android) mode`);
|
|
190
|
+
const androidDevice = this._options.preLaunchedAndroidDevice;
|
|
191
|
+
return {
|
|
192
|
+
preLaunchedAndroidDevice: androidDevice,
|
|
193
|
+
denyLaunch: true
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async _initLaunchBrowserMode(browserName, proxyValue, launchOptions, id) {
|
|
197
|
+
import_utils.debugLogger.log("server", `[${id}] engaged launch mode for "${browserName}"`);
|
|
198
|
+
let socksProxy;
|
|
199
|
+
if (proxyValue) {
|
|
200
|
+
socksProxy = new import_socksProxy.SocksProxy();
|
|
201
|
+
socksProxy.setPattern(proxyValue);
|
|
202
|
+
launchOptions.socksProxyPort = await socksProxy.listen(0);
|
|
203
|
+
import_utils.debugLogger.log("server", `[${id}] started socks proxy on port ${launchOptions.socksProxyPort}`);
|
|
204
|
+
} else {
|
|
205
|
+
launchOptions.socksProxyPort = void 0;
|
|
206
|
+
}
|
|
207
|
+
const browser = await this._playwright[browserName].launch((0, import_server.serverSideCallMetadata)(), launchOptions);
|
|
208
|
+
return {
|
|
209
|
+
preLaunchedBrowser: browser,
|
|
210
|
+
socksProxy,
|
|
211
|
+
sharedBrowser: true,
|
|
212
|
+
denyLaunch: true,
|
|
213
|
+
dispose: async () => {
|
|
214
|
+
await browser.close({ reason: "Connection terminated" });
|
|
215
|
+
socksProxy?.close();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
113
219
|
async listen(port = 0, hostname) {
|
|
114
220
|
return this._wsServer.listen(port, hostname, this._options.path);
|
|
115
221
|
}
|
|
@@ -140,6 +246,46 @@ function userAgentVersionMatchesErrorMessage(userAgent) {
|
|
|
140
246
|
].join("\n"), 1);
|
|
141
247
|
}
|
|
142
248
|
}
|
|
249
|
+
function launchOptionsHash(options) {
|
|
250
|
+
const copy = { ...options };
|
|
251
|
+
for (const k of Object.keys(copy)) {
|
|
252
|
+
const key = k;
|
|
253
|
+
if (copy[key] === defaultLaunchOptions[key])
|
|
254
|
+
delete copy[key];
|
|
255
|
+
}
|
|
256
|
+
for (const key of optionsThatAllowBrowserReuse)
|
|
257
|
+
delete copy[key];
|
|
258
|
+
return JSON.stringify(copy);
|
|
259
|
+
}
|
|
260
|
+
function filterLaunchOptions(options, allowFSPaths) {
|
|
261
|
+
return {
|
|
262
|
+
channel: options.channel,
|
|
263
|
+
args: options.args,
|
|
264
|
+
ignoreAllDefaultArgs: options.ignoreAllDefaultArgs,
|
|
265
|
+
ignoreDefaultArgs: options.ignoreDefaultArgs,
|
|
266
|
+
timeout: options.timeout,
|
|
267
|
+
headless: options.headless,
|
|
268
|
+
proxy: options.proxy,
|
|
269
|
+
chromiumSandbox: options.chromiumSandbox,
|
|
270
|
+
firefoxUserPrefs: options.firefoxUserPrefs,
|
|
271
|
+
slowMo: options.slowMo,
|
|
272
|
+
executablePath: (0, import_utils.isUnderTest)() || allowFSPaths ? options.executablePath : void 0,
|
|
273
|
+
downloadsPath: allowFSPaths ? options.downloadsPath : void 0
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const defaultLaunchOptions = {
|
|
277
|
+
ignoreAllDefaultArgs: false,
|
|
278
|
+
handleSIGINT: false,
|
|
279
|
+
handleSIGTERM: false,
|
|
280
|
+
handleSIGHUP: false,
|
|
281
|
+
headless: true,
|
|
282
|
+
devtools: false
|
|
283
|
+
};
|
|
284
|
+
const optionsThatAllowBrowserReuse = [
|
|
285
|
+
"headless",
|
|
286
|
+
"timeout",
|
|
287
|
+
"tracesDir"
|
|
288
|
+
];
|
|
143
289
|
// Annotate the CommonJS export names for ESM import in node:
|
|
144
290
|
0 && (module.exports = {
|
|
145
291
|
PlaywrightServer
|
|
@@ -93,6 +93,7 @@ class AndroidDevice extends import_instrumentation.SdkObject {
|
|
|
93
93
|
this.model = model;
|
|
94
94
|
this.serial = backend.serial;
|
|
95
95
|
this._options = options;
|
|
96
|
+
this.logName = "browser";
|
|
96
97
|
}
|
|
97
98
|
static {
|
|
98
99
|
this.Events = {
|
|
@@ -220,17 +221,20 @@ class AndroidDevice extends import_instrumentation.SdkObject {
|
|
|
220
221
|
this._android._deviceClosed(this);
|
|
221
222
|
this.emit(AndroidDevice.Events.Close);
|
|
222
223
|
}
|
|
223
|
-
async launchBrowser(pkg = "com.android.chrome", options) {
|
|
224
|
-
(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
224
|
+
async launchBrowser(metadata, pkg = "com.android.chrome", options) {
|
|
225
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
226
|
+
return controller.run(async (progress) => {
|
|
227
|
+
(0, import_utilsBundle.debug)("pw:android")("Force-stopping", pkg);
|
|
228
|
+
await this._backend.runCommand(`shell:am force-stop ${pkg}`);
|
|
229
|
+
const socketName = (0, import_debug.isUnderTest)() ? "webview_devtools_remote_playwright_test" : "playwright_" + (0, import_crypto.createGuid)() + "_devtools_remote";
|
|
230
|
+
const commandLine = this._defaultArgs(options, socketName).join(" ");
|
|
231
|
+
(0, import_utilsBundle.debug)("pw:android")("Starting", pkg, commandLine);
|
|
232
|
+
await progress.race(this._backend.runCommand(`shell:echo "${Buffer.from(commandLine).toString("base64")}" | base64 -d > /data/local/tmp/chrome-command-line`));
|
|
233
|
+
await progress.race(this._backend.runCommand(`shell:am start -a android.intent.action.VIEW -d about:blank ${pkg}`));
|
|
234
|
+
const browserContext = await this._connectToBrowser(progress, socketName, options);
|
|
235
|
+
await progress.race(this._backend.runCommand(`shell:rm /data/local/tmp/chrome-command-line`));
|
|
236
|
+
return browserContext;
|
|
237
|
+
});
|
|
234
238
|
}
|
|
235
239
|
_defaultArgs(options, socketName) {
|
|
236
240
|
const chromeArguments = [
|
|
@@ -259,23 +263,28 @@ class AndroidDevice extends import_instrumentation.SdkObject {
|
|
|
259
263
|
chromeArguments.push(...args);
|
|
260
264
|
return chromeArguments;
|
|
261
265
|
}
|
|
262
|
-
async connectToWebView(socketName) {
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
async connectToWebView(metadata, socketName) {
|
|
267
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
268
|
+
return controller.run(async (progress) => {
|
|
269
|
+
const webView = this._webViews.get(socketName);
|
|
270
|
+
if (!webView)
|
|
271
|
+
throw new Error("WebView has been closed");
|
|
272
|
+
return await this._connectToBrowser(progress, socketName);
|
|
273
|
+
});
|
|
267
274
|
}
|
|
268
|
-
async _connectToBrowser(socketName, options = {}) {
|
|
269
|
-
const socket = await this._waitForLocalAbstract(socketName);
|
|
275
|
+
async _connectToBrowser(progress, socketName, options = {}) {
|
|
276
|
+
const socket = await progress.race(this._waitForLocalAbstract(socketName));
|
|
270
277
|
const androidBrowser = new AndroidBrowser(this, socket);
|
|
271
|
-
|
|
278
|
+
progress.cleanupWhenAborted(() => androidBrowser.close());
|
|
279
|
+
await progress.race(androidBrowser._init());
|
|
272
280
|
this._browserConnections.add(androidBrowser);
|
|
273
|
-
const artifactsDir = await import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
|
|
281
|
+
const artifactsDir = await progress.race(import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER));
|
|
274
282
|
const cleanupArtifactsDir = async () => {
|
|
275
283
|
const errors = (await (0, import_fileUtils.removeFolders)([artifactsDir])).filter(Boolean);
|
|
276
284
|
for (let i = 0; i < (errors || []).length; ++i)
|
|
277
285
|
(0, import_utilsBundle.debug)("pw:android")(`exception while removing ${artifactsDir}: ${errors[i]}`);
|
|
278
286
|
};
|
|
287
|
+
progress.cleanupWhenAborted(cleanupArtifactsDir);
|
|
279
288
|
import_processLauncher.gracefullyCloseSet.add(cleanupArtifactsDir);
|
|
280
289
|
socket.on("close", async () => {
|
|
281
290
|
import_processLauncher.gracefullyCloseSet.delete(cleanupArtifactsDir);
|
|
@@ -296,12 +305,9 @@ class AndroidDevice extends import_instrumentation.SdkObject {
|
|
|
296
305
|
originalLaunchOptions: { timeout: 0 }
|
|
297
306
|
};
|
|
298
307
|
(0, import_browserContext.validateBrowserContextOptions)(options, browserOptions);
|
|
299
|
-
const browser = await import_crBrowser.CRBrowser.connect(this.attribution.playwright, androidBrowser, browserOptions);
|
|
300
|
-
const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this);
|
|
308
|
+
const browser = await progress.race(import_crBrowser.CRBrowser.connect(this.attribution.playwright, androidBrowser, browserOptions));
|
|
301
309
|
const defaultContext = browser._defaultContext;
|
|
302
|
-
await
|
|
303
|
-
await defaultContext._loadDefaultContextAsIs(progress);
|
|
304
|
-
});
|
|
310
|
+
await defaultContext._loadDefaultContextAsIs(progress);
|
|
305
311
|
return defaultContext;
|
|
306
312
|
}
|
|
307
313
|
webViews() {
|
|
@@ -40,7 +40,7 @@ var import_chromiumSwitches = require("../chromium/chromiumSwitches");
|
|
|
40
40
|
var import_chromium = require("../chromium/chromium");
|
|
41
41
|
class BidiChromium extends import_browserType.BrowserType {
|
|
42
42
|
constructor(parent) {
|
|
43
|
-
super(parent, "
|
|
43
|
+
super(parent, "_bidiChromium");
|
|
44
44
|
}
|
|
45
45
|
async connectToTransport(transport, options, browserLogsCollector) {
|
|
46
46
|
const bidiTransport = await require("./bidiOverCdp").connectBidiOverCdp(transport);
|
|
@@ -41,7 +41,10 @@ var import_firefoxPrefs = require("./third_party/firefoxPrefs");
|
|
|
41
41
|
var import_manualPromise = require("../../utils/isomorphic/manualPromise");
|
|
42
42
|
class BidiFirefox extends import_browserType.BrowserType {
|
|
43
43
|
constructor(parent) {
|
|
44
|
-
super(parent, "
|
|
44
|
+
super(parent, "_bidiFirefox");
|
|
45
|
+
}
|
|
46
|
+
executablePath() {
|
|
47
|
+
return "";
|
|
45
48
|
}
|
|
46
49
|
async connectToTransport(transport, options) {
|
|
47
50
|
return import_bidiBrowser.BidiBrowser.connect(this.attribution.playwright, transport, options);
|