mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: move some playwright-wide options to be per browser (#36342)
This commit is contained in:
parent
a7ff65cb8b
commit
8fcf838c37
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SocksProxy } from './server/utils/socksProxy';
|
||||
import { PlaywrightServer } from './remote/playwrightServer';
|
||||
import { helper } from './server/helper';
|
||||
import { serverSideCallMetadata } from './server/instrumentation';
|
||||
@ -41,10 +40,6 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
||||
|
||||
async launchServer(options: LaunchServerOptions & { _sharedBrowser?: boolean, _userDataDir?: string } = {}): Promise<BrowserServer> {
|
||||
const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true });
|
||||
// TODO: enable socks proxy once ipv6 is supported.
|
||||
const socksProxy = false ? new SocksProxy() : undefined;
|
||||
playwright.options.socksProxyPort = await socksProxy?.listen(0);
|
||||
|
||||
// 1. Pre-launch the browser
|
||||
const metadata = serverSideCallMetadata();
|
||||
const validatorContext = {
|
||||
@ -83,7 +78,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
||||
const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`;
|
||||
|
||||
// 2. Start the server
|
||||
const server = new PlaywrightServer({ mode: options._sharedBrowser ? 'launchServerShared' : 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
|
||||
const server = new PlaywrightServer({ mode: options._sharedBrowser ? 'launchServerShared' : 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser });
|
||||
const wsEndpoint = await server.listen(options.port, options.host);
|
||||
|
||||
// 3. Return the BrowserServer interface
|
||||
@ -96,7 +91,6 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
||||
(browserServer as any)._disconnectForTest = () => server.close();
|
||||
(browserServer as any)._userDataDirForTest = (browser as any)._userDataDirForTest;
|
||||
browser.options.browserProcess.onclose = (exitCode, signal) => {
|
||||
socksProxy?.close().catch(() => {});
|
||||
server.close();
|
||||
browserServer.emit('close', exitCode, signal);
|
||||
};
|
||||
|
@ -107,9 +107,9 @@ export class PlaywrightConnection {
|
||||
this._root = new RootDispatcher(this._dispatcherConnection, async (scope, options) => {
|
||||
await startProfiling();
|
||||
if (clientType === 'reuse-browser')
|
||||
return await this._initReuseBrowsersMode(scope);
|
||||
return await this._initReuseBrowsersMode(scope, options);
|
||||
if (clientType === 'pre-launched-browser-or-android')
|
||||
return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope);
|
||||
return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope, options) : await this._initPreLaunchedAndroidMode(scope);
|
||||
if (clientType === 'launch-browser')
|
||||
return await this._initLaunchBrowserMode(scope, options);
|
||||
throw new Error('Unsupported client type: ' + clientType);
|
||||
@ -120,7 +120,7 @@ export class PlaywrightConnection {
|
||||
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
|
||||
const playwright = createPlaywright({ sdkLanguage: options.sdkLanguage, isServer: true });
|
||||
|
||||
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
|
||||
const ownedSocksProxy = await this._createOwnedSocksProxy();
|
||||
let browserName = this._options.browserName;
|
||||
if ('bidi' === browserName) {
|
||||
if (this._options.launchOptions?.channel?.toLocaleLowerCase().includes('firefox'))
|
||||
@ -129,6 +129,7 @@ export class PlaywrightConnection {
|
||||
browserName = 'bidiChromium';
|
||||
}
|
||||
const browser = await playwright[browserName as 'chromium'].launch(serverSideCallMetadata(), this._options.launchOptions);
|
||||
browser.options.sdkLanguage = options.sdkLanguage;
|
||||
|
||||
this._cleanups.push(async () => {
|
||||
for (const browser of playwright.allBrowsers())
|
||||
@ -142,7 +143,7 @@ export class PlaywrightConnection {
|
||||
return new PlaywrightDispatcher(scope, playwright, { socksProxy: ownedSocksProxy, preLaunchedBrowser: browser });
|
||||
}
|
||||
|
||||
private async _initPreLaunchedBrowserMode(scope: RootDispatcher) {
|
||||
private async _initPreLaunchedBrowserMode(scope: RootDispatcher, options: channels.RootInitializeParams) {
|
||||
debugLogger.log('server', `[${this._id}] engaged pre-launched (browser) mode`);
|
||||
const playwright = this._preLaunched.playwright!;
|
||||
|
||||
@ -150,6 +151,7 @@ export class PlaywrightConnection {
|
||||
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
|
||||
|
||||
const browser = this._preLaunched.browser!;
|
||||
browser.options.sdkLanguage = options.sdkLanguage;
|
||||
browser.on(Browser.Events.Disconnected, () => {
|
||||
// Underlying browser did close for some reason - force disconnect the client.
|
||||
this.close({ code: 1001, reason: 'Browser closed' });
|
||||
@ -189,7 +191,7 @@ export class PlaywrightConnection {
|
||||
return new DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
|
||||
}
|
||||
|
||||
private async _initReuseBrowsersMode(scope: RootDispatcher) {
|
||||
private async _initReuseBrowsersMode(scope: RootDispatcher, options: channels.RootInitializeParams) {
|
||||
// Note: reuse browser mode does not support socks proxy, because
|
||||
// clients come and go, while the browser stays the same.
|
||||
|
||||
@ -222,6 +224,7 @@ export class PlaywrightConnection {
|
||||
this.close({ code: 1001, reason: 'Browser closed' });
|
||||
});
|
||||
}
|
||||
browser.options.sdkLanguage = options.sdkLanguage;
|
||||
|
||||
this._cleanups.push(async () => {
|
||||
// Don't close the pages so that user could debug them,
|
||||
@ -242,13 +245,15 @@ export class PlaywrightConnection {
|
||||
return playwrightDispatcher;
|
||||
}
|
||||
|
||||
private async _createOwnedSocksProxy(playwright: Playwright): Promise<SocksProxy | undefined> {
|
||||
if (!this._options.socksProxyPattern)
|
||||
private async _createOwnedSocksProxy(): Promise<SocksProxy | undefined> {
|
||||
if (!this._options.socksProxyPattern) {
|
||||
this._options.launchOptions.socksProxyPort = undefined;
|
||||
return;
|
||||
}
|
||||
const socksProxy = new SocksProxy();
|
||||
socksProxy.setPattern(this._options.socksProxyPattern);
|
||||
playwright.options.socksProxyPort = await socksProxy.listen(0);
|
||||
debugLogger.log('server', `[${this._id}] started socks proxy on port ${playwright.options.socksProxyPort}`);
|
||||
this._options.launchOptions.socksProxyPort = await socksProxy.listen(0);
|
||||
debugLogger.log('server', `[${this._id}] started socks proxy on port ${this._options.launchOptions.socksProxyPort}`);
|
||||
this._cleanups.push(() => socksProxy.close());
|
||||
return socksProxy;
|
||||
}
|
||||
|
@ -144,14 +144,14 @@ export class BidiChromium extends BrowserType {
|
||||
const proxyURL = new URL(proxy.server);
|
||||
const isSocks = proxyURL.protocol === 'socks5:';
|
||||
// https://www.chromium.org/developers/design-documents/network-settings
|
||||
if (isSocks && !this.attribution.playwright.options.socksProxyPort) {
|
||||
if (isSocks && !options.socksProxyPort) {
|
||||
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
|
||||
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
|
||||
}
|
||||
chromeArguments.push(`--proxy-server=${proxy.server}`);
|
||||
const proxyBypassRules = [];
|
||||
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
|
||||
if (this.attribution.playwright.options.socksProxyPort)
|
||||
if (options.socksProxyPort)
|
||||
proxyBypassRules.push('<-loopback>');
|
||||
if (proxy.bypass)
|
||||
proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
|
||||
|
@ -27,6 +27,7 @@ import type { ProxySettings } from './types';
|
||||
import type { RecentLogsCollector } from './utils/debugLogger';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import type { ChildProcess } from 'child_process';
|
||||
import type { Language } from '../utils';
|
||||
|
||||
|
||||
export interface BrowserProcess {
|
||||
@ -52,6 +53,7 @@ export type BrowserOptions = {
|
||||
browserLogsCollector: RecentLogsCollector,
|
||||
slowMo?: number;
|
||||
wsEndpoint?: string; // Only there when connected over web socket.
|
||||
sdkLanguage?: Language;
|
||||
originalLaunchOptions: types.LaunchOptions;
|
||||
};
|
||||
|
||||
@ -84,6 +86,10 @@ export abstract class Browser extends SdkObject {
|
||||
abstract version(): string;
|
||||
abstract userAgent(): string;
|
||||
|
||||
sdkLanguage() {
|
||||
return this.options.sdkLanguage || this.attribution.playwright.options.sdkLanguage;
|
||||
}
|
||||
|
||||
async newContext(metadata: CallMetadata, options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
let clientCertificatesProxy: ClientCertificatesProxy | undefined;
|
||||
|
@ -79,7 +79,7 @@ export abstract class BrowserType extends SdkObject {
|
||||
return browser;
|
||||
}
|
||||
|
||||
async launchPersistentContext(metadata: CallMetadata, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { timeout: number, cdpPort?: number, internalIgnoreHTTPSErrors?: boolean }): Promise<BrowserContext> {
|
||||
async launchPersistentContext(metadata: CallMetadata, userDataDir: string, options: channels.BrowserTypeLaunchPersistentContextOptions & { timeout: number, cdpPort?: number, internalIgnoreHTTPSErrors?: boolean, socksProxyPort?: number }): Promise<BrowserContext> {
|
||||
const launchOptions = this._validateLaunchOptions(options);
|
||||
const controller = new ProgressController(metadata, this);
|
||||
const browser = await controller.run(async progress => {
|
||||
@ -288,8 +288,8 @@ export abstract class BrowserType extends SdkObject {
|
||||
headless = false;
|
||||
if (downloadsPath && !path.isAbsolute(downloadsPath))
|
||||
downloadsPath = path.join(process.cwd(), downloadsPath);
|
||||
if (this.attribution.playwright.options.socksProxyPort)
|
||||
proxy = { server: `socks5://127.0.0.1:${this.attribution.playwright.options.socksProxyPort}` };
|
||||
if (options.socksProxyPort)
|
||||
proxy = { server: `socks5://127.0.0.1:${options.socksProxyPort}` };
|
||||
return { ...options, devtools, headless, downloadsPath, proxy };
|
||||
}
|
||||
|
||||
|
@ -331,14 +331,14 @@ export class Chromium extends BrowserType {
|
||||
const proxyURL = new URL(proxy.server);
|
||||
const isSocks = proxyURL.protocol === 'socks5:';
|
||||
// https://www.chromium.org/developers/design-documents/network-settings
|
||||
if (isSocks && !this.attribution.playwright.options.socksProxyPort) {
|
||||
if (isSocks && !options.socksProxyPort) {
|
||||
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
|
||||
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
|
||||
}
|
||||
chromeArguments.push(`--proxy-server=${proxy.server}`);
|
||||
const proxyBypassRules = [];
|
||||
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
|
||||
if (this.attribution.playwright.options.socksProxyPort)
|
||||
if (options.socksProxyPort)
|
||||
proxyBypassRules.push('<-loopback>');
|
||||
if (proxy.bypass)
|
||||
proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
|
||||
|
@ -894,7 +894,7 @@ class FrameSession {
|
||||
|
||||
async _createVideoRecorder(screencastId: string, options: types.PageScreencastOptions): Promise<void> {
|
||||
assert(!this._screencastId);
|
||||
const ffmpegPath = registry.findExecutable('ffmpeg')!.executablePathOrDie(this._page.attribution.playwright.options.sdkLanguage);
|
||||
const ffmpegPath = registry.findExecutable('ffmpeg')!.executablePathOrDie(this._page.browserContext._browser.sdkLanguage());
|
||||
this._videoRecorder = await VideoRecorder.launch(this._crPage._page, ffmpegPath, options);
|
||||
this._screencastId = screencastId;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
const selectorsRegistry = this.frame._page.browserContext.selectors();
|
||||
for (const [name, { source }] of selectorsRegistry._engines)
|
||||
customEngines.push({ name, source: `(${source})` });
|
||||
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
||||
const sdkLanguage = this.frame._page.browserContext._browser.sdkLanguage();
|
||||
const options: InjectedScriptOptions = {
|
||||
isUnderTest: isUnderTest(),
|
||||
sdkLanguage,
|
||||
|
@ -134,7 +134,7 @@ export class FrameSelectors {
|
||||
for (const chunk of frameChunks) {
|
||||
visitAllSelectorParts(chunk, (part, nested) => {
|
||||
if (nested && part.name === 'internal:control' && part.body === 'enter-frame') {
|
||||
const locator = asLocator(this.frame._page.attribution.playwright.options.sdkLanguage, selector);
|
||||
const locator = asLocator(this.frame._page.browserContext._browser.sdkLanguage(), selector);
|
||||
throw new InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator}"`);
|
||||
}
|
||||
});
|
||||
|
@ -1711,7 +1711,7 @@ export class Frame extends SdkObject {
|
||||
}
|
||||
|
||||
private _asLocator(selector: string) {
|
||||
return asLocator(this._page.attribution.playwright.options.sdkLanguage, selector);
|
||||
return asLocator(this._page.browserContext._browser.sdkLanguage(), selector);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,11 +484,11 @@ export class Page extends SdkObject {
|
||||
}
|
||||
if (handler.resolved) {
|
||||
++this._locatorHandlerRunningCounter;
|
||||
progress.log(` found ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)}, intercepting action to run the handler`);
|
||||
progress.log(` found ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)}, intercepting action to run the handler`);
|
||||
const promise = handler.resolved.then(async () => {
|
||||
progress.throwIfAborted();
|
||||
if (!handler.noWaitAfter) {
|
||||
progress.log(` locator handler has finished, waiting for ${asLocator(this.attribution.playwright.options.sdkLanguage, handler.selector)} to be hidden`);
|
||||
progress.log(` locator handler has finished, waiting for ${asLocator(this.browserContext._browser.sdkLanguage(), handler.selector)} to be hidden`);
|
||||
await this.mainFrame().waitForSelectorInternal(progress, handler.selector, false, { state: 'hidden' });
|
||||
} else {
|
||||
progress.log(` locator handler has finished`);
|
||||
|
@ -33,7 +33,6 @@ import type { CallMetadata } from './instrumentation';
|
||||
import type { Page } from './page';
|
||||
|
||||
type PlaywrightOptions = {
|
||||
socksProxyPort?: number;
|
||||
sdkLanguage: Language;
|
||||
isInternalPlaywright?: boolean;
|
||||
isServer?: boolean;
|
||||
|
@ -63,7 +63,7 @@ export class ContextRecorder extends EventEmitter {
|
||||
this._params = params;
|
||||
this._delegate = delegate;
|
||||
this._recorderSources = [];
|
||||
const language = params.language || context.attribution.playwright.options.sdkLanguage;
|
||||
const language = params.language || context._browser.sdkLanguage();
|
||||
this.setOutput(language, params.outputFile);
|
||||
|
||||
// Make a copy of options to modify them later.
|
||||
|
@ -103,7 +103,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||
}
|
||||
|
||||
private static async _open(recorder: IRecorder, inspectedContext: BrowserContext): Promise<IRecorderApp> {
|
||||
const sdkLanguage = inspectedContext.attribution.playwright.options.sdkLanguage;
|
||||
const sdkLanguage = inspectedContext._browser.sdkLanguage();
|
||||
const headed = !!inspectedContext._browser.options.headful;
|
||||
const recorderPlaywright = (require('../playwright').createPlaywright as typeof import('../playwright').createPlaywright)({ sdkLanguage: 'javascript', isInternalPlaywright: true });
|
||||
const { context, page } = await launchApp(recorderPlaywright.chromium, {
|
||||
|
@ -109,7 +109,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
||||
platform: process.platform,
|
||||
wallTime: 0,
|
||||
monotonicTime: 0,
|
||||
sdkLanguage: context.attribution.playwright.options.sdkLanguage,
|
||||
sdkLanguage: this._sdkLanguage(),
|
||||
testIdAttributeName,
|
||||
contextId: context.guid,
|
||||
};
|
||||
@ -122,6 +122,10 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
||||
}
|
||||
}
|
||||
|
||||
private _sdkLanguage() {
|
||||
return this._context instanceof BrowserContext ? this._context._browser.sdkLanguage() : this._context.attribution.playwright.options.sdkLanguage;
|
||||
}
|
||||
|
||||
async resetForReuse() {
|
||||
// Discard previous chunk if any and ignore any errors there.
|
||||
await this.stopChunk({ mode: 'discard' }).catch(() => {});
|
||||
@ -136,7 +140,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
||||
throw new Error('Tracing has been already started');
|
||||
|
||||
// Re-write for testing.
|
||||
this._contextCreatedEvent.sdkLanguage = this._context.attribution.playwright.options.sdkLanguage;
|
||||
this._contextCreatedEvent.sdkLanguage = this._sdkLanguage();
|
||||
|
||||
// TODO: passing the same name for two contexts makes them write into a single file
|
||||
// and conflict.
|
||||
|
@ -169,7 +169,6 @@ export async function openTraceViewerApp(url: string, browserName: string, optio
|
||||
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;
|
||||
|
||||
const { context, page } = await launchApp(traceViewerPlaywright[traceViewerBrowser as 'chromium'], {
|
||||
// TODO: store language in the trace.
|
||||
sdkLanguage: traceViewerPlaywright.options.sdkLanguage,
|
||||
windowSize: { width: 1280, height: 800 },
|
||||
persistentContextOptions: {
|
||||
|
@ -158,6 +158,7 @@ export type LaunchOptions = channels.BrowserTypeLaunchParams & {
|
||||
cdpPort?: number,
|
||||
proxyOverride?: ProxySettings,
|
||||
assistantMode?: boolean,
|
||||
socksProxyPort?: number,
|
||||
};
|
||||
|
||||
export type BrowserContextOptions = channels.BrowserNewContextOptions & {
|
||||
|
Loading…
x
Reference in New Issue
Block a user