chore: align more server-side options with rpc protocol (#3506)

This touches:
- noDefaultViewport;
- ignoreAllDefaultArgs;
- env;
- validateXYZ logic that was copying objects - we do not need that anymore;
- shuffles some converters closer to their usage.
This commit is contained in:
Dmitry Gozman 2020-08-18 09:37:40 -07:00 committed by GitHub
parent 7a77faf194
commit 9790ea5b5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 105 additions and 141 deletions

View file

@ -40,12 +40,10 @@ export type BrowserOptions = {
proxy?: ProxySettings,
};
export type BrowserContextOptions = types.BrowserContextOptions;
export interface Browser extends EventEmitter {
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
newContext(options?: types.BrowserContextOptions): Promise<BrowserContext>;
contexts(): BrowserContext[];
newPage(options?: BrowserContextOptions): Promise<Page>;
newPage(options?: types.BrowserContextOptions): Promise<Page>;
isConnected(): boolean;
close(): Promise<void>;
version(): string;
@ -62,12 +60,12 @@ export abstract class BrowserBase extends EventEmitter implements Browser {
this._options = options;
}
abstract newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
abstract newContext(options?: types.BrowserContextOptions): Promise<BrowserContext>;
abstract contexts(): BrowserContext[];
abstract isConnected(): boolean;
abstract version(): string;
async newPage(options?: BrowserContextOptions): Promise<Page> {
async newPage(options?: types.BrowserContextOptions): Promise<Page> {
const context = await this.newContext(options);
const page = await context.newPage();
page._ownedContext = context;

View file

@ -228,47 +228,23 @@ export function assertBrowserContextIsNotOwned(context: BrowserContextBase) {
}
}
export function validateBrowserContextOptions(options: types.BrowserContextOptions): types.BrowserContextOptions {
// Copy all fields manually to strip any extra junk.
// Especially useful when we share context and launch options for launchPersistent.
const result: types.BrowserContextOptions = {
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
bypassCSP: options.bypassCSP,
locale: options.locale,
timezoneId: options.timezoneId,
offline: options.offline,
colorScheme: options.colorScheme,
acceptDownloads: options.acceptDownloads,
viewport: options.viewport,
javaScriptEnabled: options.javaScriptEnabled,
userAgent: options.userAgent,
geolocation: options.geolocation,
permissions: options.permissions,
extraHTTPHeaders: options.extraHTTPHeaders,
httpCredentials: options.httpCredentials,
deviceScaleFactor: options.deviceScaleFactor,
isMobile: options.isMobile,
hasTouch: options.hasTouch,
};
if (result.viewport === null && result.deviceScaleFactor !== undefined)
export function validateBrowserContextOptions(options: types.BrowserContextOptions) {
if (options.noDefaultViewport && options.deviceScaleFactor !== undefined)
throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
if (result.viewport === null && result.isMobile !== undefined)
if (options.noDefaultViewport && options.isMobile !== undefined)
throw new Error(`"isMobile" option is not supported with null "viewport"`);
if (!result.viewport && result.viewport !== null)
result.viewport = { width: 1280, height: 720 };
if (result.viewport)
result.viewport = { ...result.viewport };
if (result.geolocation)
result.geolocation = verifyGeolocation(result.geolocation);
if (result.extraHTTPHeaders)
result.extraHTTPHeaders = network.verifyHeaders(result.extraHTTPHeaders);
return result;
if (!options.viewport && !options.noDefaultViewport)
options.viewport = { width: 1280, height: 720 };
verifyGeolocation(options.geolocation);
if (options.extraHTTPHeaders)
options.extraHTTPHeaders = network.verifyHeaders(options.extraHTTPHeaders);
}
export function verifyGeolocation(geolocation: types.Geolocation): types.Geolocation {
const result = { ...geolocation };
result.accuracy = result.accuracy || 0;
const { longitude, latitude, accuracy } = result;
export function verifyGeolocation(geolocation?: types.Geolocation) {
if (!geolocation)
return;
geolocation.accuracy = geolocation.accuracy || 0;
const { longitude, latitude, accuracy } = geolocation;
if (!helper.isNumber(longitude))
throw new Error(`geolocation.longitude: expected number, got ${typeof longitude}`);
if (longitude < -180 || longitude > 180)
@ -281,7 +257,6 @@ export function verifyGeolocation(geolocation: types.Geolocation): types.Geoloca
throw new Error(`geolocation.accuracy: expected number, got ${typeof accuracy}`);
if (accuracy < 0)
throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`);
return result;
}
export function verifyProxySettings(proxy: types.ProxySettings): types.ProxySettings {

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
import { BrowserBase, BrowserOptions } from '../browser';
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
import { Events as CommonEvents } from '../events';
import { assert } from '../helper';
@ -99,8 +99,8 @@ export class CRBrowser extends BrowserBase {
this._session.on('Target.detachedFromTarget', this._onDetachedFromTarget.bind(this));
}
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
options = validateBrowserContextOptions(options);
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
validateBrowserContextOptions(options);
const { browserContextId } = await this._session.send('Target.createBrowserContext', { disposeOnDetach: true });
const context = new CRBrowserContext(this, browserContextId, options);
await context._initialize();
@ -381,8 +381,7 @@ export class CRBrowserContext extends BrowserContextBase {
}
async setGeolocation(geolocation?: types.Geolocation): Promise<void> {
if (geolocation)
geolocation = verifyGeolocation(geolocation);
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
for (const page of this.pages())
await (page._delegate as CRPage).updateGeolocation();

View file

@ -74,7 +74,7 @@ export class CRPage implements PageDelegate {
this._mainFrameSession = new FrameSession(this, client, targetId, null);
this._sessions.set(targetId, this._mainFrameSession);
client.once(CRSessionEvents.Disconnected, () => this._page._didDisconnect());
if (opener && browserContext._options.viewport !== null) {
if (opener && !browserContext._options.noDefaultViewport) {
const features = opener._nextWindowOpenPopupFeatures.shift() || [];
const viewportSize = helper.getViewportSizeFromWindowFeatures(features);
if (viewportSize)
@ -371,7 +371,7 @@ class FrameSession {
}
async _initialize(hasUIWindow: boolean) {
if (hasUIWindow && this._crPage._browserContext._options.viewport !== null) {
if (hasUIWindow && !this._crPage._browserContext._options.noDefaultViewport) {
const { windowId } = await this._client.send('Browser.getWindowForTarget');
this._windowId = windowId;
}

View file

@ -105,19 +105,3 @@ export function headersArrayToObject(headers: types.HeadersArray): types.Headers
result[name] = value;
return result;
}
export function envObjectToArray(env: types.Env): types.EnvArray {
const result: types.EnvArray = [];
for (const name in env) {
if (!Object.is(env[name], undefined))
result.push({ name, value: String(env[name]) });
}
return result;
}
export function envArrayToObject(env: types.EnvArray): types.Env {
const result: types.Env = {};
for (const { name, value } of env)
result[name] = value;
return result;
}

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
import { BrowserBase, BrowserOptions } from '../browser';
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
import { Events } from '../events';
import { assert, helper, RegisteredListener } from '../helper';
@ -91,8 +91,8 @@ export class FFBrowser extends BrowserBase {
return !this._connection._closed;
}
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
options = validateBrowserContextOptions(options);
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
validateBrowserContextOptions(options);
if (options.isMobile)
throw new Error('options.isMobile is not supported in Firefox');
const { browserContextId } = await this._connection.send('Browser.createBrowserContext', { removeOnDetach: true });
@ -289,8 +289,7 @@ export class FFBrowserContext extends BrowserContextBase {
}
async setGeolocation(geolocation?: types.Geolocation): Promise<void> {
if (geolocation)
geolocation = verifyGeolocation(geolocation);
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
await this._browser._connection.send('Browser.setGeolocationOverride', { browserContextId: this._browserContextId || undefined, geolocation: geolocation || null });
}

View file

@ -127,7 +127,7 @@ export class Page extends EventEmitter {
this._crashedPromise = new Promise(f => this._crashedCallback = f);
this._browserContext = browserContext;
this._state = {
viewportSize: browserContext._options.viewport ? { ...browserContext._options.viewport } : null,
viewportSize: browserContext._options.viewport || null,
mediaType: null,
colorScheme: null,
extraHTTPHeaders: null,

View file

@ -27,6 +27,7 @@ import { BrowserDispatcher } from './server/browserDispatcher';
import { BrowserContextDispatcher } from './server/browserContextDispatcher';
import { BrowserNewContextParams, BrowserContextChannel } from './channels';
import { BrowserServerLauncher, BrowserServer } from './client/browserType';
import { envObjectToArray } from './client/clientHelper';
export class BrowserServerLauncherImpl implements BrowserServerLauncher {
private _browserType: BrowserTypeBase;
@ -36,7 +37,12 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher {
}
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServerImpl> {
const browser = await this._browserType.launch(options);
const browser = await this._browserType.launch({
...options,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? envObjectToArray(options.env) : undefined,
});
return new BrowserServerImpl(this._browserType, browser as BrowserBase, options.port);
}
}

View file

@ -18,7 +18,7 @@ import { BrowserTypeChannel, BrowserTypeInitializer, BrowserTypeLaunchParams, Br
import { Browser } from './browser';
import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner';
import { headersObjectToArray, envObjectToArray } from '../../converters';
import { headersObjectToArray } from '../../converters';
import { assert, helper } from '../../helper';
import { LaunchOptions, LaunchServerOptions, ConnectOptions, LaunchPersistentContextOptions } from './types';
import * as WebSocket from 'ws';
@ -27,6 +27,7 @@ import { serializeError } from '../serializers';
import { Events } from './events';
import { TimeoutSettings } from '../../timeoutSettings';
import { ChildProcess } from 'child_process';
import { envObjectToArray } from './clientHelper';
export interface BrowserServerLauncher {
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;

View file

@ -16,6 +16,7 @@
*/
import { isUnderTest as commonIsUnderTest } from '../../helper';
import * as types from './types';
const deprecatedHits = new Set();
export function deprecate(methodName: string, message: string) {
@ -28,3 +29,12 @@ export function deprecate(methodName: string, message: string) {
export function isUnderTest() {
return commonIsUnderTest();
}
export function envObjectToArray(env: types.Env): { name: string, value: string }[] {
const result: { name: string, value: string }[] = [];
for (const name in env) {
if (!Object.is(env[name], undefined))
result.push({ name, value: String(env[name]) });
}
return result;
}

View file

@ -22,8 +22,8 @@ import { serializeArgument, FuncOn, parseResult, SmartHandle, JSHandle } from '.
import { TimeoutSettings } from '../../timeoutSettings';
import { Waiter } from './waiter';
import { Events } from './events';
import { envObjectToArray } from '../../converters';
import { WaitForEventOptions, Env, LoggerSink } from './types';
import { envObjectToArray } from './clientHelper';
type ElectronOptions = Omit<ElectronLaunchOptions, 'env'> & {
env?: Env,

View file

@ -39,7 +39,6 @@ export class BrowserDispatcher extends Dispatcher<Browser, BrowserInitializer> i
async newContext(params: BrowserNewContextParams): Promise<{ context: BrowserContextChannel }> {
const options = {
...params,
viewport: params.viewport || (params.noDefaultViewport ? null : undefined),
extraHTTPHeaders: params.extraHTTPHeaders ? headersArrayToObject(params.extraHTTPHeaders) : undefined,
};
return { context: new BrowserContextDispatcher(this._scope, await this._object.newContext(options) as BrowserContextBase) };

View file

@ -21,7 +21,7 @@ import { BrowserChannel, BrowserTypeChannel, BrowserContextChannel, BrowserTypeI
import { Dispatcher, DispatcherScope } from './dispatcher';
import { BrowserContextBase } from '../../browserContext';
import { BrowserContextDispatcher } from './browserContextDispatcher';
import { headersArrayToObject, envArrayToObject } from '../../converters';
import { headersArrayToObject } from '../../converters';
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeInitializer> implements BrowserTypeChannel {
constructor(scope: DispatcherScope, browserType: BrowserTypeBase) {
@ -32,21 +32,13 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, BrowserTypeIn
}
async launch(params: BrowserTypeLaunchParams): Promise<{ browser: BrowserChannel }> {
const options = {
...params,
ignoreDefaultArgs: params.ignoreAllDefaultArgs ? true : params.ignoreDefaultArgs,
env: params.env ? envArrayToObject(params.env) : undefined,
};
const browser = await this._object.launch(options);
const browser = await this._object.launch(params);
return { browser: new BrowserDispatcher(this._scope, browser as BrowserBase) };
}
async launchPersistentContext(params: BrowserTypeLaunchPersistentContextParams): Promise<{ context: BrowserContextChannel }> {
const options = {
...params,
viewport: params.viewport || (params.noDefaultViewport ? null : undefined),
ignoreDefaultArgs: params.ignoreAllDefaultArgs ? true : params.ignoreDefaultArgs,
env: params.env ? envArrayToObject(params.env) : undefined,
extraHTTPHeaders: params.extraHTTPHeaders ? headersArrayToObject(params.extraHTTPHeaders) : undefined,
};
const browserContext = await this._object.launchPersistentContext(params.userDataDir, options);

View file

@ -22,7 +22,6 @@ import { BrowserContextBase } from '../../browserContext';
import { PageDispatcher } from './pageDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { createHandle } from './elementHandlerDispatcher';
import { envArrayToObject } from '../../converters';
export class ElectronDispatcher extends Dispatcher<Electron, ElectronInitializer> implements ElectronChannel {
constructor(scope: DispatcherScope, electron: Electron) {
@ -30,11 +29,7 @@ export class ElectronDispatcher extends Dispatcher<Electron, ElectronInitializer
}
async launch(params: ElectronLaunchParams): Promise<{ electronApplication: ElectronApplicationChannel }> {
const options = {
...params,
env: params.env ? envArrayToObject(params.env) : undefined,
};
const electronApplication = await this._object.launch(params.executablePath, options);
const electronApplication = await this._object.launch(params.executablePath, params);
return { electronApplication: new ElectronApplicationDispatcher(this._scope, electronApplication) };
}
}

View file

@ -23,24 +23,18 @@ import * as browserPaths from '../install/browserPaths';
import { ConnectionTransport, WebSocketTransport } from '../transport';
import { BrowserBase, BrowserOptions, Browser, BrowserProcess } from '../browser';
import { assert, helper } from '../helper';
import { launchProcess, Env, waitForLine } from './processLauncher';
import { launchProcess, Env, waitForLine, envArrayToObject } from './processLauncher';
import { PipeTransport } from './pipeTransport';
import { Progress, runAbortableTask } from '../progress';
import * as types from '../types';
import { TimeoutSettings } from '../timeoutSettings';
import { validateHostRequirements } from './validateDependencies';
type FirefoxPrefsOptions = { firefoxUserPrefs?: { [key: string]: string | number | boolean } };
export type LaunchNonPersistentOptions = types.LaunchOptions & FirefoxPrefsOptions;
type LaunchPersistentOptions = types.LaunchOptions & types.BrowserContextOptions;
type LaunchServerOptions = types.LaunchServerOptions & FirefoxPrefsOptions;
export interface BrowserType {
executablePath(): string;
name(): string;
launch(options?: LaunchNonPersistentOptions): Promise<Browser>;
launchPersistentContext(userDataDir: string, options?: LaunchPersistentOptions): Promise<BrowserContext>;
launch(options?: types.LaunchOptions): Promise<Browser>;
launchPersistentContext(userDataDir: string, options?: types.LaunchPersistentOptions): Promise<BrowserContext>;
}
const mkdirAsync = util.promisify(fs.mkdir);
@ -76,7 +70,7 @@ export abstract class BrowserTypeBase implements BrowserType {
return this._name;
}
async launch(options: LaunchNonPersistentOptions = {}): Promise<Browser> {
async launch(options: types.LaunchOptions = {}): Promise<Browser> {
assert(!(options as any).userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead');
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
options = validateLaunchOptions(options);
@ -84,10 +78,11 @@ export abstract class BrowserTypeBase implements BrowserType {
return browser;
}
async launchPersistentContext(userDataDir: string, options: LaunchPersistentOptions = {}): Promise<BrowserContext> {
async launchPersistentContext(userDataDir: string, options: types.LaunchPersistentOptions = {}): Promise<BrowserContext> {
assert(!(options as any).port, 'Cannot specify a port without launching as a server.');
options = validateLaunchOptions(options);
const persistent = validateBrowserContextOptions(options);
const persistent: types.BrowserContextOptions = options;
validateBrowserContextOptions(persistent);
const browser = await runAbortableTask(progress => this._innerLaunch(progress, options, persistent, userDataDir), TimeoutSettings.timeout(options), 'browser').catch(e => { throw this._rewriteStartupError(e); });
return browser._defaultContext!;
}
@ -109,23 +104,24 @@ export abstract class BrowserTypeBase implements BrowserType {
copyTestHooks(options, browserOptions);
const browser = await this._connectToTransport(transport, browserOptions);
// We assume no control when using custom arguments, and do not prepare the default context in that case.
const hasCustomArguments = !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs);
if (persistent && !hasCustomArguments)
if (persistent && !options.ignoreAllDefaultArgs)
await browser._defaultContext!._loadDefaultContext(progress);
return browser;
}
private async _launchProcess(progress: Progress, options: LaunchServerOptions, isPersistent: boolean, userDataDir?: string): Promise<{ browserProcess: BrowserProcess, downloadsPath: string, transport: ConnectionTransport }> {
private async _launchProcess(progress: Progress, options: types.LaunchOptions, isPersistent: boolean, userDataDir?: string): Promise<{ browserProcess: BrowserProcess, downloadsPath: string, transport: ConnectionTransport }> {
const {
ignoreDefaultArgs = false,
ignoreDefaultArgs,
ignoreAllDefaultArgs,
args = [],
executablePath = null,
env = process.env,
handleSIGINT = true,
handleSIGTERM = true,
handleSIGHUP = true,
} = options;
const env = options.env ? envArrayToObject(options.env) : process.env;
const tempDirectories = [];
let downloadsPath: string;
if (options.downloadsPath) {
@ -142,12 +138,12 @@ export abstract class BrowserTypeBase implements BrowserType {
}
const browserArguments = [];
if (!ignoreDefaultArgs)
browserArguments.push(...this._defaultArgs(options, isPersistent, userDataDir));
else if (Array.isArray(ignoreDefaultArgs))
if (ignoreAllDefaultArgs)
browserArguments.push(...args);
else if (ignoreDefaultArgs)
browserArguments.push(...this._defaultArgs(options, isPersistent, userDataDir).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));
else
browserArguments.push(...args);
browserArguments.push(...this._defaultArgs(options, isPersistent, userDataDir));
const executable = executablePath || this.executablePath();
if (!executable)
@ -211,7 +207,7 @@ export abstract class BrowserTypeBase implements BrowserType {
return { browserProcess, downloadsPath, transport };
}
abstract _defaultArgs(options: types.LaunchOptionsBase, isPersistent: boolean, userDataDir: string): string[];
abstract _defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[];
abstract _connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise<BrowserBase>;
abstract _amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env;
abstract _amendArguments(browserArguments: string[]): string[];
@ -226,7 +222,7 @@ function copyTestHooks(from: object, to: object) {
}
}
function validateLaunchOptions<Options extends types.LaunchOptionsBase>(options: Options): Options {
function validateLaunchOptions<Options extends types.LaunchOptions>(options: Options): Options {
const { devtools = false, headless = !helper.isDebugMode() && !devtools } = options;
return { ...options, devtools, headless };
}

View file

@ -27,7 +27,7 @@ import { ConnectionTransport, ProtocolRequest } from '../transport';
import { BrowserDescriptor } from '../install/browserPaths';
import { CRDevTools } from '../chromium/crDevTools';
import { BrowserOptions } from '../browser';
import { LaunchOptionsBase } from '../types';
import * as types from '../types';
export class Chromium extends BrowserTypeBase {
private _devtools: CRDevTools | undefined;
@ -101,7 +101,7 @@ export class Chromium extends BrowserTypeBase {
transport.send(message);
}
_defaultArgs(options: LaunchOptionsBase, isPersistent: boolean, userDataDir: string): string[] {
_defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] {
const { args = [], proxy } = options;
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
if (userDataDirArg)

View file

@ -24,7 +24,7 @@ import { Page } from '../page';
import { TimeoutSettings } from '../timeoutSettings';
import { WebSocketTransport } from '../transport';
import * as types from '../types';
import { launchProcess, waitForLine } from './processLauncher';
import { launchProcess, waitForLine, envArrayToObject } from './processLauncher';
import { BrowserContext } from '../browserContext';
import type {BrowserWindow} from 'electron';
import { runAbortableTask, ProgressController } from '../progress';
@ -35,7 +35,7 @@ import { BrowserProcess } from '../browser';
export type ElectronLaunchOptionsBase = {
args?: string[],
cwd?: string,
env?: types.Env,
env?: types.EnvArray,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
@ -145,7 +145,6 @@ export class Electron {
async launch(executablePath: string, options: ElectronLaunchOptionsBase = {}): Promise<ElectronApplication> {
const {
args = [],
env = process.env,
handleSIGINT = true,
handleSIGTERM = true,
handleSIGHUP = true,
@ -156,7 +155,7 @@ export class Electron {
const { launchedProcess, gracefullyClose, kill } = await launchProcess({
executablePath,
args: electronArguments,
env,
env: options.env ? envArrayToObject(options.env) : process.env,
handleSIGINT,
handleSIGTERM,
handleSIGHUP,
@ -180,7 +179,7 @@ export class Electron {
close: gracefullyClose,
kill
};
const browser = await CRBrowser.connect(chromeTransport, { name: 'electron', headful: true, persistent: { viewport: null }, browserProcess });
const browser = await CRBrowser.connect(chromeTransport, { name: 'electron', headful: true, persistent: { noDefaultViewport: true }, browserProcess });
app = new ElectronApplication(browser, nodeConnection);
await app._init();
return app;

View file

@ -20,11 +20,12 @@ import * as fs from 'fs';
import * as path from 'path';
import { FFBrowser } from '../firefox/ffBrowser';
import { kBrowserCloseMessageId } from '../firefox/ffConnection';
import { BrowserTypeBase, LaunchNonPersistentOptions } from './browserType';
import { BrowserTypeBase } from './browserType';
import { Env } from './processLauncher';
import { ConnectionTransport } from '../transport';
import { BrowserOptions } from '../browser';
import { BrowserDescriptor } from '../install/browserPaths';
import * as types from '../types';
export class Firefox extends BrowserTypeBase {
constructor(packagePath: string, browser: BrowserDescriptor) {
@ -57,7 +58,7 @@ export class Firefox extends BrowserTypeBase {
transport.send(message);
}
_defaultArgs(options: LaunchNonPersistentOptions, isPersistent: boolean, userDataDir: string): string[] {
_defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] {
const { args = [], devtools, headless } = options;
if (devtools)
console.warn('devtools parameter is not supported as a launch argument in Firefox. You can launch the devtools window manually.');

View file

@ -21,6 +21,7 @@ import * as removeFolder from 'rimraf';
import * as stream from 'stream';
import { helper, isUnderTest } from '../helper';
import { Progress } from '../progress';
import * as types from '../types';
export type Env = {[key: string]: string | number | boolean | undefined};
@ -204,3 +205,10 @@ export function waitForLine(progress: Progress, process: childProcess.ChildProce
}
});
}
export function envArrayToObject(env: types.EnvArray): Env {
const result: Env = {};
for (const { name, value } of env)
result[name] = value;
return result;
}

View file

@ -23,7 +23,7 @@ import { BrowserTypeBase } from './browserType';
import { ConnectionTransport } from '../transport';
import { BrowserOptions } from '../browser';
import { BrowserDescriptor } from '../install/browserPaths';
import { LaunchOptionsBase } from '../types';
import * as types from '../types';
export class WebKit extends BrowserTypeBase {
constructor(packagePath: string, browser: BrowserDescriptor) {
@ -50,7 +50,7 @@ export class WebKit extends BrowserTypeBase {
transport.send({method: 'Playwright.close', params: {}, id: kBrowserCloseMessageId});
}
_defaultArgs(options: LaunchOptionsBase, isPersistent: boolean, userDataDir: string): string[] {
_defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] {
const { args = [], proxy, devtools, headless } = options;
if (devtools)
console.warn('devtools parameter as a launch argument in WebKit is not supported. Also starting Web Inspector manually will terminate the execution in WebKit.');

View file

@ -265,7 +265,8 @@ export type SetNetworkCookieParam = {
};
export type BrowserContextOptions = {
viewport?: Size | null,
viewport?: Size,
noDefaultViewport?: boolean,
ignoreHTTPSErrors?: boolean,
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
@ -284,27 +285,29 @@ export type BrowserContextOptions = {
acceptDownloads?: boolean,
};
export type Env = {[key: string]: string | number | boolean | undefined};
export type EnvArray = { name: string, value: string }[];
export type LaunchOptionsBase = {
type LaunchOptionsBase = {
executablePath?: string,
args?: string[],
ignoreDefaultArgs?: boolean | string[],
ignoreDefaultArgs?: string[],
ignoreAllDefaultArgs?: boolean,
handleSIGINT?: boolean,
handleSIGTERM?: boolean,
handleSIGHUP?: boolean,
timeout?: number,
env?: Env,
env?: EnvArray,
headless?: boolean,
devtools?: boolean,
proxy?: ProxySettings,
downloadsPath?: string,
chromiumSandbox?: boolean,
slowMo?: number,
};
export type LaunchOptions = LaunchOptionsBase & { slowMo?: number };
export type LaunchServerOptions = LaunchOptionsBase & { port?: number };
export type LaunchOptions = LaunchOptionsBase & {
firefoxUserPrefs?: { [key: string]: string | number | boolean },
};
export type LaunchPersistentOptions = LaunchOptionsBase & BrowserContextOptions;
export type SerializedAXNode = {
role: string,

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { BrowserBase, BrowserOptions, BrowserContextOptions } from '../browser';
import { BrowserBase, BrowserOptions } from '../browser';
import { assertBrowserContextIsNotOwned, BrowserContext, BrowserContextBase, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
import { Events } from '../events';
import { helper, RegisteredListener, assert } from '../helper';
@ -72,8 +72,8 @@ export class WKBrowser extends BrowserBase {
this._didClose();
}
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
options = validateBrowserContextOptions(options);
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
validateBrowserContextOptions(options);
const { browserContextId } = await this._browserSession.send('Playwright.createContext');
options.userAgent = options.userAgent || DEFAULT_USER_AGENT;
const context = new WKBrowserContext(this, browserContextId, options);
@ -287,8 +287,7 @@ export class WKBrowserContext extends BrowserContextBase {
}
async setGeolocation(geolocation?: types.Geolocation): Promise<void> {
if (geolocation)
geolocation = verifyGeolocation(geolocation);
verifyGeolocation(geolocation);
this._options.geolocation = geolocation;
const payload: any = geolocation ? { ...geolocation, timestamp: Date.now() } : undefined;
await this._browser._browserSession.send('Playwright.setGeolocationOverride', { browserContextId: this._browserContextId, geolocation: payload });

View file

@ -90,7 +90,7 @@ export class WKPage implements PageDelegate {
this._firstNonInitialNavigationCommittedFulfill = f;
this._firstNonInitialNavigationCommittedReject = r;
});
if (opener && browserContext._options.viewport !== null && opener._nextWindowOpenPopupFeatures) {
if (opener && !browserContext._options.noDefaultViewport && opener._nextWindowOpenPopupFeatures) {
const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures);
opener._nextWindowOpenPopupFeatures = undefined;
if (viewportSize)