Socket
Socket
Sign inDemoInstall

playwright-core

Package Overview
Dependencies
42
Maintainers
1
Versions
4382
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.11.0 to 0.11.1-next.1582929239274

26

index.js

@@ -16,24 +16,8 @@ /**

*/
const {Playwright} = require('./lib/server/playwright.js');
const { helper } = require('./lib/helper');
const api = require('./lib/api');
const packageJson = require('./package.json');
const { DeviceDescriptors } = require('./lib/deviceDescriptors');
const { TimeoutError } = require('./lib/errors');
const { Chromium } = require('./lib/server/chromium');
const { Firefox } = require('./lib/server/firefox');
const { WebKit } = require('./lib/server/webkit');
module.exports = new Playwright({
downloadPath: __dirname,
browsers: ['webkit', 'chromium', 'firefox'],
});
for (const className in api) {
if (typeof api[className] === 'function')
helper.installApiHooks(className, api[className]);
}
module.exports = {
devices: DeviceDescriptors,
errors: { TimeoutError },
selectors: api.Selectors._instance(),
chromium: new Chromium(__dirname, packageJson.playwright.chromium_revision),
firefox: new Firefox(__dirname, packageJson.playwright.firefox_revision),
webkit: new WebKit(__dirname, packageJson.playwright.webkit_revision),
}

1

lib/api.d.ts

@@ -30,2 +30,3 @@ /**

export { CRBrowser as ChromiumBrowser } from './chromium/crBrowser';
export { CRBrowserContext as ChromiumBrowserContext } from './chromium/crBrowser';
export { CRCoverage as ChromiumCoverage } from './chromium/crCoverage';

@@ -32,0 +33,0 @@ export { CRSession as ChromiumSession } from './chromium/crConnection';

@@ -20,4 +20,2 @@ "use strict";

exports.Accessibility = accessibility_1.Accessibility;
var browserContext_1 = require("./browserContext");
exports.BrowserContext = browserContext_1.BrowserContext;
var console_1 = require("./console");

@@ -48,2 +46,4 @@ exports.ConsoleMessage = console_1.ConsoleMessage;

exports.ChromiumBrowser = crBrowser_1.CRBrowser;
var crBrowser_2 = require("./chromium/crBrowser");
exports.ChromiumBrowserContext = crBrowser_2.CRBrowserContext;
var crCoverage_1 = require("./chromium/crCoverage");

@@ -50,0 +50,0 @@ exports.ChromiumCoverage = crCoverage_1.CRCoverage;

@@ -20,16 +20,3 @@ /**

import * as types from './types';
import * as platform from './platform';
import { TimeoutSettings } from './timeoutSettings';
export interface BrowserContextDelegate {
pages(): Promise<Page[]>;
existingPages(): Page[];
newPage(): Promise<Page>;
close(): Promise<void>;
cookies(): Promise<network.NetworkCookie[]>;
setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
clearCookies(): Promise<void>;
setPermissions(origin: string, permissions: string[]): Promise<void>;
clearPermissions(): Promise<void>;
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
}
export declare type BrowserContextOptions = {

@@ -47,11 +34,5 @@ viewport?: types.Viewport | null;

};
extraHTTPHeaders?: network.Headers;
};
export declare class BrowserContext extends platform.EventEmitter {
private readonly _delegate;
readonly _options: BrowserContextOptions;
readonly _timeoutSettings: TimeoutSettings;
private _closed;
constructor(delegate: BrowserContextDelegate, options: BrowserContextOptions);
_initialize(): Promise<void>;
_existingPages(): Page[];
export interface BrowserContext {
setDefaultNavigationTimeout(timeout: number): void;

@@ -67,5 +48,14 @@ setDefaultTimeout(timeout: number): void;

setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
addInitScript(script: Function | string | {
path?: string;
content?: string;
}, ...args: any[]): Promise<void>;
close(): Promise<void>;
static validateOptions(options: BrowserContextOptions): void;
_browserClosed(): void;
_existingPages(): Page[];
readonly _timeoutSettings: TimeoutSettings;
readonly _options: BrowserContextOptions;
}
export declare function assertBrowserContextIsNotOwned(context: BrowserContext): void;
export declare function validateBrowserContextOptions(options: BrowserContextOptions): BrowserContextOptions;
export declare function verifyGeolocation(geolocation: types.Geolocation): types.Geolocation;

@@ -21,85 +21,23 @@ "use strict";

const helper_1 = require("./helper");
const platform = require("./platform");
const events_1 = require("./events");
const timeoutSettings_1 = require("./timeoutSettings");
class BrowserContext extends platform.EventEmitter {
constructor(delegate, options) {
super();
this._closed = false;
this._delegate = delegate;
this._timeoutSettings = new timeoutSettings_1.TimeoutSettings();
this._options = { ...options };
if (!this._options.viewport && this._options.viewport !== null)
this._options.viewport = { width: 800, height: 600 };
if (this._options.viewport)
this._options.viewport = { ...this._options.viewport };
if (this._options.geolocation)
this._options.geolocation = verifyGeolocation(this._options.geolocation);
function assertBrowserContextIsNotOwned(context) {
const pages = context._existingPages();
for (const page of pages) {
if (page._ownedContext)
throw new Error('Please use browser.newContext() for multi-page scripts that share the context.');
}
async _initialize() {
const entries = Object.entries(this._options.permissions || {});
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
if (this._options.geolocation)
await this.setGeolocation(this._options.geolocation);
}
_existingPages() {
return this._delegate.existingPages();
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async pages() {
return this._delegate.pages();
}
async newPage() {
const pages = this._delegate.existingPages();
for (const page of pages) {
if (page._ownedContext)
throw new Error('Please use browser.newContext() for multi-page scripts that share the context.');
}
return this._delegate.newPage();
}
async cookies(...urls) {
return network.filterCookies(await this._delegate.cookies(), urls);
}
async setCookies(cookies) {
await this._delegate.setCookies(network.rewriteCookies(cookies));
}
async clearCookies() {
await this._delegate.clearCookies();
}
async setPermissions(origin, permissions) {
await this._delegate.setPermissions(origin, permissions);
}
async clearPermissions() {
await this._delegate.clearPermissions();
}
async setGeolocation(geolocation) {
if (geolocation)
geolocation = verifyGeolocation(geolocation);
this._options.geolocation = geolocation || undefined;
await this._delegate.setGeolocation(geolocation);
}
async close() {
if (this._closed)
return;
await this._delegate.close();
this._closed = true;
this.emit(events_1.Events.BrowserContext.Close);
}
static validateOptions(options) {
if (options.geolocation)
verifyGeolocation(options.geolocation);
}
_browserClosed() {
this._closed = true;
for (const page of this._delegate.existingPages())
page._didClose();
this.emit(events_1.Events.BrowserContext.Close);
}
}
exports.BrowserContext = BrowserContext;
exports.assertBrowserContextIsNotOwned = assertBrowserContextIsNotOwned;
function validateBrowserContextOptions(options) {
const result = { ...options };
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;
}
exports.validateBrowserContextOptions = validateBrowserContextOptions;
function verifyGeolocation(geolocation) {

@@ -117,2 +55,3 @@ const result = { ...geolocation };

}
exports.verifyGeolocation = verifyGeolocation;
//# sourceMappingURL=browserContext.js.map

@@ -19,13 +19,16 @@ /**

import { CRConnection, CRSession } from './crConnection';
import { Page, Worker } from '../page';
import { Page } from '../page';
import { CRTarget } from './crTarget';
import { Protocol } from './protocol';
import { Browser } from '../browser';
import * as network from '../network';
import * as types from '../types';
import * as platform from '../platform';
import { ConnectionTransport } from '../transport';
import { TimeoutSettings } from '../timeoutSettings';
export declare class CRBrowser extends platform.EventEmitter implements Browser {
_connection: CRConnection;
_client: CRSession;
readonly _defaultContext: BrowserContext;
private _contexts;
readonly _defaultContext: CRBrowserContext;
readonly _contexts: Map<string, CRBrowserContext>;
_targets: Map<string, CRTarget>;

@@ -37,3 +40,2 @@ private _tracingRecording;

constructor(connection: CRConnection);
_createBrowserContext(contextId: string | null, options: BrowserContextOptions): BrowserContext;
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;

@@ -49,8 +51,4 @@ contexts(): BrowserContext[];

_allTargets(): CRTarget[];
waitForTarget(predicate: (arg0: CRTarget) => boolean, options?: {
timeout?: number;
} | undefined): Promise<CRTarget>;
close(): Promise<void>;
browserTarget(): CRTarget;
serviceWorker(target: CRTarget): Promise<Worker | null>;
startTracing(page: Page | undefined, options?: {

@@ -62,6 +60,37 @@ path?: string;

stopTracing(): Promise<platform.BufferType>;
targets(context?: BrowserContext): CRTarget[];
pageTarget(page: Page): CRTarget;
isConnected(): boolean;
_setDebugFunction(debugFunction: (message: string) => void): void;
}
export declare class CRBrowserContext extends platform.EventEmitter implements BrowserContext {
readonly _browser: CRBrowser;
readonly _browserContextId: string | null;
readonly _options: BrowserContextOptions;
readonly _timeoutSettings: TimeoutSettings;
readonly _evaluateOnNewDocumentSources: string[];
private _closed;
constructor(browser: CRBrowser, browserContextId: string | null, options: BrowserContextOptions);
_initialize(): Promise<void>;
_existingPages(): Page[];
setDefaultNavigationTimeout(timeout: number): void;
setDefaultTimeout(timeout: number): void;
pages(): Promise<Page[]>;
newPage(): Promise<Page>;
cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
clearCookies(): Promise<void>;
setPermissions(origin: string, permissions: string[]): Promise<void>;
clearPermissions(): Promise<void>;
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
addInitScript(script: Function | string | {
path?: string;
content?: string;
}, ...args: any[]): Promise<void>;
close(): Promise<void>;
pageTarget(page: Page): CRTarget;
targets(): CRTarget[];
waitForTarget(predicate: (arg0: CRTarget) => boolean, options?: {
timeout?: number;
}): Promise<CRTarget>;
_browserClosed(): void;
}

@@ -26,5 +26,7 @@ "use strict";

const browser_1 = require("../browser");
const network = require("../network");
const platform = require("../platform");
const crProtocolHelper_1 = require("./crProtocolHelper");
const transport_1 = require("../transport");
const timeoutSettings_1 = require("../timeoutSettings");
class CRBrowser extends platform.EventEmitter {

@@ -39,5 +41,5 @@ constructor(connection) {

this._client = connection.rootSession;
this._defaultContext = this._createBrowserContext(null, {});
this._defaultContext = new CRBrowserContext(this, null, browserContext_1.validateBrowserContextOptions({}));
this._connection.on(crConnection_1.ConnectionEvents.Disconnected, () => {
for (const context of this.contexts())
for (const context of this._contexts.values())
context._browserClosed();

@@ -56,85 +58,6 @@ this.emit(events_2.Events.Browser.Disconnected);

}
_createBrowserContext(contextId, options) {
const context = new browserContext_1.BrowserContext({
pages: async () => {
const targets = this._allTargets().filter(target => target.context() === context && target.type() === 'page');
const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page);
},
existingPages: () => {
const pages = [];
for (const target of this._allTargets()) {
if (target.context() === context && target._crPage)
pages.push(target._crPage.page());
}
return pages;
},
newPage: async () => {
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
const target = this._targets.get(targetId);
helper_1.assert(await target._initializedPromise, 'Failed to create target for page');
const page = await target.page();
return page;
},
close: async () => {
helper_1.assert(contextId, 'Non-incognito profiles cannot be closed!');
await this._client.send('Target.disposeBrowserContext', { browserContextId: contextId });
this._contexts.delete(contextId);
},
cookies: async () => {
const { cookies } = await this._client.send('Storage.getCookies', { browserContextId: contextId || undefined });
return cookies.map(c => {
const copy = { sameSite: 'None', ...c };
delete copy.size;
delete copy.priority;
return copy;
});
},
clearCookies: async () => {
await this._client.send('Storage.clearCookies', { browserContextId: contextId || undefined });
},
setCookies: async (cookies) => {
await this._client.send('Storage.setCookies', { cookies, browserContextId: contextId || undefined });
},
setPermissions: async (origin, permissions) => {
const webPermissionToProtocol = new Map([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardSanitizedWrite'],
['payment-handler', 'paymentHandler'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._client.send('Browser.grantPermissions', { origin, browserContextId: contextId || undefined, permissions: filtered });
},
clearPermissions: async () => {
await this._client.send('Browser.resetPermissions', { browserContextId: contextId || undefined });
},
setGeolocation: async (geolocation) => {
for (const page of await context.pages())
await page._delegate._client.send('Emulation.setGeolocationOverride', geolocation || {});
}
}, options);
return context;
}
async newContext(options = {}) {
browserContext_1.BrowserContext.validateOptions(options);
const { browserContextId } = await this._client.send('Target.createBrowserContext');
const context = this._createBrowserContext(browserContextId, options);
options = browserContext_1.validateBrowserContextOptions(options);
const { browserContextId } = await this._client.send('Target.createBrowserContext', { disposeOnDetach: true });
const context = new CRBrowserContext(this, browserContextId, options);
await context._initialize();

@@ -158,3 +81,3 @@ this._contexts.set(browserContextId, context);

if (target._isInitialized || await target._initializedPromise)
this.emit(events_1.Events.CRBrowser.TargetCreated, target);
context.emit(events_1.Events.CRBrowserContext.TargetCreated, target);
}

@@ -167,3 +90,3 @@ async _targetDestroyed(event) {

if (await target._initializedPromise)
this.emit(events_1.Events.CRBrowser.TargetDestroyed, target);
target.context().emit(events_1.Events.CRBrowserContext.TargetDestroyed, target);
}

@@ -177,3 +100,3 @@ _targetInfoChanged(event) {

if (wasInitialized && previousURL !== target.url())
this.emit(events_1.Events.CRBrowser.TargetChanged, target);
target.context().emit(events_1.Events.CRBrowserContext.TargetChanged, target);
}

@@ -186,25 +109,2 @@ async _closePage(page) {

}
async waitForTarget(predicate, options = {}) {
const { timeout = 30000 } = options;
const existingTarget = this._allTargets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve;
const targetPromise = new Promise(x => resolve = x);
this.on(events_1.Events.CRBrowser.TargetCreated, check);
this.on(events_1.Events.CRBrowser.TargetChanged, check);
try {
if (!timeout)
return await targetPromise;
return await helper_1.helper.waitWithTimeout(targetPromise, 'target', timeout);
}
finally {
this.removeListener(events_1.Events.CRBrowser.TargetCreated, check);
this.removeListener(events_1.Events.CRBrowser.TargetChanged, check);
}
function check(target) {
if (predicate(target))
resolve(target);
}
}
async close() {

@@ -219,5 +119,2 @@ const disconnected = new Promise(f => this._connection.once(crConnection_1.ConnectionEvents.Disconnected, f));

}
serviceWorker(target) {
return target._worker();
}
async startTracing(page, options = {}) {

@@ -253,9 +150,2 @@ helper_1.assert(!this._tracingRecording, 'Cannot start recording trace while already recording trace.');

}
targets(context) {
const targets = this._allTargets();
return context ? targets.filter(t => t.context() === context) : targets;
}
pageTarget(page) {
return crTarget_1.CRTarget.fromPage(page);
}
isConnected() {

@@ -269,2 +159,154 @@ return !this._connection._closed;

exports.CRBrowser = CRBrowser;
class CRBrowserContext extends platform.EventEmitter {
constructor(browser, browserContextId, options) {
super();
this._closed = false;
this._browser = browser;
this._browserContextId = browserContextId;
this._timeoutSettings = new timeoutSettings_1.TimeoutSettings();
this._options = options;
this._evaluateOnNewDocumentSources = [];
}
async _initialize() {
const entries = Object.entries(this._options.permissions || {});
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
if (this._options.geolocation)
await this.setGeolocation(this._options.geolocation);
}
_existingPages() {
const pages = [];
for (const target of this._browser._allTargets()) {
if (target.context() === this && target._crPage)
pages.push(target._crPage.page());
}
return pages;
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async pages() {
const targets = this._browser._allTargets().filter(target => target.context() === this && target.type() === 'page');
const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page);
}
async newPage() {
browserContext_1.assertBrowserContextIsNotOwned(this);
const { targetId } = await this._browser._client.send('Target.createTarget', { url: 'about:blank', browserContextId: this._browserContextId || undefined });
const target = this._browser._targets.get(targetId);
helper_1.assert(await target._initializedPromise, 'Failed to create target for page');
const page = await target.page();
return page;
}
async cookies(...urls) {
const { cookies } = await this._browser._client.send('Storage.getCookies', { browserContextId: this._browserContextId || undefined });
return network.filterCookies(cookies.map(c => {
const copy = { sameSite: 'None', ...c };
delete copy.size;
delete copy.priority;
return copy;
}), urls);
}
async setCookies(cookies) {
await this._browser._client.send('Storage.setCookies', { cookies: network.rewriteCookies(cookies), browserContextId: this._browserContextId || undefined });
}
async clearCookies() {
await this._browser._client.send('Storage.clearCookies', { browserContextId: this._browserContextId || undefined });
}
async setPermissions(origin, permissions) {
const webPermissionToProtocol = new Map([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardSanitizedWrite'],
['payment-handler', 'paymentHandler'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._browser._client.send('Browser.grantPermissions', { origin, browserContextId: this._browserContextId || undefined, permissions: filtered });
}
async clearPermissions() {
await this._browser._client.send('Browser.resetPermissions', { browserContextId: this._browserContextId || undefined });
}
async setGeolocation(geolocation) {
if (geolocation)
geolocation = browserContext_1.verifyGeolocation(geolocation);
this._options.geolocation = geolocation || undefined;
for (const page of this._existingPages())
await page._delegate._client.send('Emulation.setGeolocationOverride', geolocation || {});
}
async setExtraHTTPHeaders(headers) {
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
for (const page of this._existingPages())
await page._delegate.updateExtraHTTPHeaders();
}
async addInitScript(script, ...args) {
const source = await helper_1.helper.evaluationScript(script, ...args);
this._evaluateOnNewDocumentSources.push(source);
for (const page of this._existingPages())
await page._delegate.evaluateOnNewDocument(source);
}
async close() {
if (this._closed)
return;
helper_1.assert(this._browserContextId, 'Non-incognito profiles cannot be closed!');
await this._browser._client.send('Target.disposeBrowserContext', { browserContextId: this._browserContextId });
this._browser._contexts.delete(this._browserContextId);
this._closed = true;
this.emit(events_2.Events.BrowserContext.Close);
}
pageTarget(page) {
return crTarget_1.CRTarget.fromPage(page);
}
targets() {
return this._browser._allTargets().filter(t => t.context() === this);
}
async waitForTarget(predicate, options = {}) {
const { timeout = 30000 } = options;
const existingTarget = this._browser._allTargets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve;
const targetPromise = new Promise(x => resolve = x);
this.on(events_1.Events.CRBrowserContext.TargetCreated, check);
this.on(events_1.Events.CRBrowserContext.TargetChanged, check);
try {
if (!timeout)
return await targetPromise;
return await helper_1.helper.waitWithTimeout(targetPromise, 'target', timeout);
}
finally {
this.removeListener(events_1.Events.CRBrowserContext.TargetCreated, check);
this.removeListener(events_1.Events.CRBrowserContext.TargetChanged, check);
}
function check(target) {
if (predicate(target))
resolve(target);
}
}
_browserClosed() {
this._closed = true;
for (const page of this._existingPages())
page._didClose();
this.emit(events_2.Events.BrowserContext.Close);
}
}
exports.CRBrowserContext = CRBrowserContext;
//# sourceMappingURL=crBrowser.js.map

@@ -52,3 +52,3 @@ "use strict";

const data = JSON.stringify(message);
this._debugProtocol('SEND ► ' + data);
this._debugProtocol('SEND ► ' + (rewriteInjectedScriptEvaluationLog(message) || data));
this._transport.send(data);

@@ -163,2 +163,8 @@ return id;

}
function rewriteInjectedScriptEvaluationLog(message) {
// Injected script is very long and clutters protocol logs.
// To increase development velocity, we skip replace it with short description in the log.
if (message.method === 'Runtime.evaluate' && message.params && message.params.expression && message.params.expression.includes('src/injected/injected.ts'))
return `{"id":${message.id} [evaluate injected script]}`;
}
//# sourceMappingURL=crConnection.js.map

@@ -19,3 +19,2 @@ /**

import * as frames from '../frames';
import * as network from '../network';
import { CRSession } from './crConnection';

@@ -28,4 +27,3 @@ import { CRNetworkManager } from './crNetworkManager';

import { CRCoverage } from './crCoverage';
import { CRBrowser } from './crBrowser';
import { BrowserContext } from '../browserContext';
import { CRBrowser, CRBrowserContext } from './crBrowser';
import * as types from '../types';

@@ -45,3 +43,4 @@ import * as platform from '../platform';

private _coverage;
constructor(client: CRSession, browser: CRBrowser, browserContext: BrowserContext);
private readonly _browserContext;
constructor(client: CRSession, browser: CRBrowser, browserContext: CRBrowserContext);
initialize(): Promise<void>;

@@ -72,3 +71,3 @@ didClose(): void;

_onFileChooserOpened(event: Protocol.Page.fileChooserOpenedPayload): Promise<void>;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
updateExtraHTTPHeaders(): Promise<void>;
setViewportSize(viewportSize: types.Size): Promise<void>;

@@ -75,0 +74,0 @@ _updateViewport(updateTouch: boolean): Promise<void>;

@@ -21,2 +21,3 @@ "use strict";

const helper_1 = require("../helper");
const network = require("../network");
const crConnection_1 = require("./crConnection");

@@ -47,2 +48,3 @@ const crExecutionContext_1 = require("./crExecutionContext");

this._coverage = new crCoverage_1.CRCoverage(client);
this._browserContext = browserContext;
this._page = new page_1.Page(this, browserContext);

@@ -96,2 +98,4 @@ this._networkManager = new crNetworkManager_1.CRNetworkManager(client, this._page);

promises.push(this._client.send('Emulation.setUserAgentOverride', { userAgent: options.userAgent || '', acceptLanguage: options.locale }));
if (options.locale)
promises.push(this._client.send('Emulation.setLocaleOverride', { locale: options.locale }));
if (options.timezoneId)

@@ -101,2 +105,5 @@ promises.push(emulateTimezone(this._client, options.timezoneId));

promises.push(this._client.send('Emulation.setGeolocationOverride', options.geolocation));
promises.push(this.updateExtraHTTPHeaders());
for (const source of this._browserContext._evaluateOnNewDocumentSources)
promises.push(this.evaluateOnNewDocument(source));
await Promise.all(promises);

@@ -270,3 +277,7 @@ }

}
async setExtraHTTPHeaders(headers) {
async updateExtraHTTPHeaders() {
const headers = network.mergeHeaders([
this._page.context()._options.extraHTTPHeaders,
this._page._state.extraHTTPHeaders
]);
await this._client.send('Network.setExtraHTTPHeaders', { headers });

@@ -273,0 +284,0 @@ }

@@ -17,4 +17,3 @@ /**

*/
import { CRBrowser } from './crBrowser';
import { BrowserContext } from '../browserContext';
import { CRBrowser, CRBrowserContext } from './crBrowser';
import { CRSession } from './crConnection';

@@ -37,9 +36,9 @@ import { Page, Worker } from '../page';

static fromPage(page: Page): CRTarget;
constructor(browser: CRBrowser, targetInfo: Protocol.Target.TargetInfo, browserContext: BrowserContext, sessionFactory: () => Promise<CRSession>);
constructor(browser: CRBrowser, targetInfo: Protocol.Target.TargetInfo, browserContext: CRBrowserContext, sessionFactory: () => Promise<CRSession>);
_didClose(): void;
page(): Promise<Page | null>;
_worker(): Promise<Worker | null>;
serviceWorker(): Promise<Worker | null>;
url(): string;
type(): 'page' | 'background_page' | 'service_worker' | 'shared_worker' | 'other' | 'browser';
context(): BrowserContext;
context(): CRBrowserContext;
opener(): CRTarget | null;

@@ -46,0 +45,0 @@ createCDPSession(): Promise<CRSession>;

@@ -74,4 +74,4 @@ "use strict";

}
async _worker() {
if (this._targetInfo.type !== 'service_worker' && this._targetInfo.type !== 'shared_worker')
async serviceWorker() {
if (this._targetInfo.type !== 'service_worker')
return null;

@@ -78,0 +78,0 @@ if (!this._workerPromise) {

@@ -18,3 +18,3 @@ /**

export declare const Events: {
CRBrowser: {
CRBrowserContext: {
TargetCreated: string;

@@ -21,0 +21,0 @@ TargetDestroyed: string;

@@ -20,3 +20,3 @@ "use strict";

exports.Events = {
CRBrowser: {
CRBrowserContext: {
TargetCreated: 'targetcreated',

@@ -23,0 +23,0 @@ TargetDestroyed: 'targetdestroyed',

@@ -22,2 +22,8 @@ /**

import * as platform from './platform';
export declare type PointerActionOptions = {
modifiers?: input.Modifier[];
offset?: types.Point;
};
export declare type ClickOptions = PointerActionOptions & input.MouseClickOptions;
export declare type MultiClickOptions = PointerActionOptions & input.MouseMultiClickOptions;
export declare class FrameExecutionContext extends js.ExecutionContext {

@@ -40,3 +46,3 @@ readonly frame: frames.Frame;

asElement(): ElementHandle<T> | null;
_evaluateInUtility: types.EvaluateOn<T>;
_evaluateInUtility: types.EvaluateWithInjected<T>;
ownerFrame(): Promise<frames.Frame | null>;

@@ -47,8 +53,8 @@ contentFrame(): Promise<frames.Frame | null>;

private _clickablePoint;
private _relativePoint;
_performPointerAction(action: (point: types.Point) => Promise<void>, options?: input.PointerActionOptions): Promise<void>;
hover(options?: input.PointerActionOptions): Promise<void>;
click(options?: input.ClickOptions): Promise<void>;
dblclick(options?: input.MultiClickOptions): Promise<void>;
tripleclick(options?: input.MultiClickOptions): Promise<void>;
private _offsetPoint;
_performPointerAction(action: (point: types.Point) => Promise<void>, options?: PointerActionOptions & types.WaitForOptions): Promise<void>;
hover(options?: PointerActionOptions & types.WaitForOptions): Promise<void>;
click(options?: ClickOptions & types.WaitForOptions): Promise<void>;
dblclick(options?: MultiClickOptions & types.WaitForOptions): Promise<void>;
tripleclick(options?: MultiClickOptions & types.WaitForOptions): Promise<void>;
select(...values: (string | ElementHandle | types.SelectOption)[]): Promise<string[]>;

@@ -61,8 +67,8 @@ fill(value: string): Promise<void>;

}): Promise<void>;
press(key: string, options: {
press(key: string, options?: {
delay?: number;
text?: string;
} | undefined): Promise<void>;
check(): Promise<void>;
uncheck(): Promise<void>;
}): Promise<void>;
check(options?: types.WaitForOptions): Promise<void>;
uncheck(options?: types.WaitForOptions): Promise<void>;
private _setChecked;

@@ -75,7 +81,8 @@ boundingBox(): Promise<types.Rect | null>;

$$eval: types.$$Eval;
visibleRatio(): Promise<number>;
_waitForStablePosition(options?: types.TimeoutOptions): Promise<void>;
_waitForHitTargetAt(point: types.Point, options?: types.TimeoutOptions): Promise<void>;
}
export declare type Task = (context: FrameExecutionContext) => Promise<js.JSHandle>;
export declare function waitForFunctionTask(selector: string | undefined, pageFunction: Function | string, options: types.WaitForFunctionOptions, ...args: any[]): (context: FrameExecutionContext) => Promise<ElementHandle<any> | js.JSHandle<any>>;
export declare function waitForFunctionTask(selector: string | undefined, pageFunction: Function | string, options: types.WaitForFunctionOptions, ...args: any[]): Task;
export declare function waitForSelectorTask(selector: string, visibility: types.Visibility, timeout: number): Task;
export declare const setFileInputFunction: (element: HTMLInputElement, payloads: types.FilePayload[]) => Promise<void>;

@@ -107,3 +107,3 @@ "use strict";

const utility = await this._context.frame._utilityContext();
return utility.evaluate(pageFunction, this, ...args);
return utility.evaluate(pageFunction, await utility._injected(), this, ...args);
};

@@ -143,3 +143,3 @@ this.$eval = async (selector, pageFunction, ...args) => {

async contentFrame() {
const isFrameElement = await this._evaluateInUtility(node => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'));
const isFrameElement = await this._evaluateInUtility((injected, node) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'));
if (!isFrameElement)

@@ -190,13 +190,8 @@ return null;

}
async _relativePoint(relativePoint) {
async _offsetPoint(offset) {
const [box, border] = await Promise.all([
this.boundingBox(),
this._evaluateInUtility((node) => {
if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)
return { x: 0, y: 0 };
const style = node.ownerDocument.defaultView.getComputedStyle(node);
return { x: parseInt(style.borderLeftWidth || '', 10), y: parseInt(style.borderTopWidth || '', 10) };
}).catch(helper_1.debugError),
this._evaluateInUtility((injected, node) => injected.getElementBorderWidth(node)).catch(helper_1.debugError),
]);
const point = { x: relativePoint.x, y: relativePoint.y };
const point = { x: offset.x, y: offset.y };
if (box) {

@@ -208,4 +203,4 @@ point.x += box.x;

// Make point relative to the padding box to align with offsetX/offsetY.
point.x += border.x;
point.y += border.y;
point.x += border.left;
point.y += border.top;
}

@@ -215,5 +210,12 @@ return point;

async _performPointerAction(action, options) {
const relativePoint = options ? options.relativePoint : undefined;
await this._scrollRectIntoViewIfNeeded(relativePoint ? { x: relativePoint.x, y: relativePoint.y, width: 0, height: 0 } : undefined);
const point = relativePoint ? await this._relativePoint(relativePoint) : await this._clickablePoint();
const { waitFor = true } = (options || {});
if (!helper_1.helper.isBoolean(waitFor))
throw new Error('waitFor option should be a boolean, got "' + (typeof waitFor) + '"');
if (waitFor)
await this._waitForStablePosition(options);
const offset = options ? options.offset : undefined;
await this._scrollRectIntoViewIfNeeded(offset ? { x: offset.x, y: offset.y, width: 0, height: 0 } : undefined);
const point = offset ? await this._offsetPoint(offset) : await this._clickablePoint();
if (waitFor)
await this._waitForHitTargetAt(point, options);
let restoreModifiers;

@@ -250,88 +252,7 @@ if (options && options.modifiers)

}
return this._evaluateInUtility((node, ...optionsToSelect) => {
if (node.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a <select> element.');
const element = node;
const options = Array.from(element.options);
element.value = undefined;
for (let index = 0; index < options.length; index++) {
const option = options[index];
option.selected = optionsToSelect.some(optionToSelect => {
if (optionToSelect instanceof Node)
return option === optionToSelect;
let matches = true;
if (optionToSelect.value !== undefined)
matches = matches && optionToSelect.value === option.value;
if (optionToSelect.label !== undefined)
matches = matches && optionToSelect.label === option.label;
if (optionToSelect.index !== undefined)
matches = matches && optionToSelect.index === index;
return matches;
});
if (option.selected && !element.multiple)
break;
}
element.dispatchEvent(new Event('input', { 'bubbles': true }));
element.dispatchEvent(new Event('change', { 'bubbles': true }));
return options.filter(option => option.selected).map(option => option.value);
}, ...options);
return this._evaluateInUtility((injected, node, ...optionsToSelect) => injected.selectOptions(node, optionsToSelect), ...options);
}
async fill(value) {
helper_1.assert(helper_1.helper.isString(value), 'Value must be string. Found value "' + value + '" of type "' + (typeof value) + '"');
const error = await this._evaluateInUtility((node, value) => {
if (node.nodeType !== Node.ELEMENT_NODE)
return 'Node is not of type HTMLElement';
const element = node;
if (!element.isConnected)
return 'Element is not attached to the DOM';
if (!element.ownerDocument || !element.ownerDocument.defaultView)
return 'Element does not belong to a window';
const style = element.ownerDocument.defaultView.getComputedStyle(element);
if (!style || style.visibility === 'hidden')
return 'Element is hidden';
if (!element.offsetParent && element.tagName !== 'BODY')
return 'Element is not visible';
if (element.nodeName.toLowerCase() === 'input') {
const input = element;
const type = input.getAttribute('type') || '';
const kTextInputTypes = new Set(['', 'email', 'number', 'password', 'search', 'tel', 'text', 'url']);
if (!kTextInputTypes.has(type.toLowerCase()))
return 'Cannot fill input of type "' + type + '".';
if (type.toLowerCase() === 'number') {
value = value.trim();
if (!value || isNaN(Number(value)))
return 'Cannot type text into input[type=number].';
}
if (input.disabled)
return 'Cannot fill a disabled input.';
if (input.readOnly)
return 'Cannot fill a readonly input.';
input.select();
input.focus();
}
else if (element.nodeName.toLowerCase() === 'textarea') {
const textarea = element;
if (textarea.disabled)
return 'Cannot fill a disabled textarea.';
if (textarea.readOnly)
return 'Cannot fill a readonly textarea.';
textarea.selectionStart = 0;
textarea.selectionEnd = textarea.value.length;
textarea.focus();
}
else if (element.isContentEditable) {
const range = element.ownerDocument.createRange();
range.selectNodeContents(element);
const selection = element.ownerDocument.defaultView.getSelection();
if (!selection)
return 'Element belongs to invisible iframe.';
selection.removeAllRanges();
selection.addRange(range);
element.focus();
}
else {
return 'Element is not an <input>, <textarea> or [contenteditable] element.';
}
return false;
}, value);
const error = await this._evaluateInUtility((injected, node, value) => injected.fill(node, value), value);
if (error)

@@ -345,3 +266,3 @@ throw new Error(error);

async setInputFiles(...files) {
const multiple = await this._evaluateInUtility((node) => {
const multiple = await this._evaluateInUtility((injected, node) => {
if (node.nodeType !== Node.ELEMENT_NODE || node.tagName !== 'INPUT')

@@ -367,3 +288,3 @@ throw new Error('Node is not an HTMLInputElement');

async focus() {
const errorMessage = await this._evaluateInUtility((element) => {
const errorMessage = await this._evaluateInUtility((injected, element) => {
if (!element['focus'])

@@ -385,35 +306,13 @@ return 'Node is not an HTML or SVG element.';

}
async check() {
await this._setChecked(true);
async check(options) {
await this._setChecked(true, options);
}
async uncheck() {
await this._setChecked(false);
async uncheck(options) {
await this._setChecked(false, options);
}
async _setChecked(state) {
const isCheckboxChecked = async () => {
return this._evaluateInUtility((node) => {
if (node.nodeType !== Node.ELEMENT_NODE)
throw new Error('Not a checkbox or radio button');
let element = node;
if (element.getAttribute('role') === 'checkbox')
return element.getAttribute('aria-checked') === 'true';
if (element.nodeName === 'LABEL') {
const forId = element.getAttribute('for');
if (forId && element.ownerDocument)
element = element.ownerDocument.querySelector(`input[id="${forId}"]`) || undefined;
else
element = element.querySelector('input[type=checkbox],input[type=radio]') || undefined;
}
if (element && element.nodeName === 'INPUT') {
const type = element.getAttribute('type');
if (type && (type.toLowerCase() === 'checkbox' || type.toLowerCase() === 'radio'))
return element.checked;
}
throw new Error('Not a checkbox');
});
};
if (await isCheckboxChecked() === state)
async _setChecked(state, options) {
if (await this._evaluateInUtility((injected, node) => injected.isCheckboxChecked(node)) === state)
return;
await this.click();
if (await isCheckboxChecked() !== state)
await this.click(options);
if (await this._evaluateInUtility((injected, node) => injected.isCheckboxChecked(node)) !== state)
throw new Error('Unable to click checkbox');

@@ -433,20 +332,23 @@ }

}
visibleRatio() {
return this._evaluateInUtility(async (node) => {
if (node.nodeType !== Node.ELEMENT_NODE)
throw new Error('Node is not of type HTMLElement');
const element = node;
const visibleRatio = await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
// Firefox doesn't call IntersectionObserver callback unless
// there are rafs.
requestAnimationFrame(() => { });
});
return visibleRatio;
});
async _waitForStablePosition(options = {}) {
const stablePromise = this._evaluateInUtility((injected, node, timeout) => {
return injected.waitForStablePosition(node, timeout);
}, options.timeout || 0);
await helper_1.helper.waitWithTimeout(stablePromise, 'element to stop moving', options.timeout || 0);
}
async _waitForHitTargetAt(point, options = {}) {
const frame = await this.ownerFrame();
if (frame && frame.parentFrame()) {
const element = await frame.frameElement();
const box = await element.boundingBox();
if (!box)
throw new Error('Element is not attached to the DOM');
// Translate from viewport coordinates to frame coordinates.
point = { x: point.x - box.x, y: point.y - box.y };
}
const hitTargetPromise = this._evaluateInUtility((injected, node, timeout, point) => {
return injected.waitForHitTargetAt(node, timeout, point);
}, options.timeout || 0, point);
await helper_1.helper.waitWithTimeout(hitTargetPromise, 'element to receive mouse events', options.timeout || 0);
}
}

@@ -466,4 +368,3 @@ exports.ElementHandle = ElementHandle;

}
function waitForFunctionTask(selector, pageFunction, options, ...args) {
const { polling = 'raf' } = options;
function assertPolling(polling) {
if (helper_1.helper.isString(polling))

@@ -475,2 +376,6 @@ helper_1.assert(polling === 'raf' || polling === 'mutation', 'Unknown polling option: ' + polling);

throw new Error('Unknown polling options: ' + polling);
}
function waitForFunctionTask(selector, pageFunction, options, ...args) {
const { polling = 'raf' } = options;
assertPolling(polling);
const predicateBody = helper_1.helper.isString(pageFunction) ? 'return (' + pageFunction + ')' : 'return (' + pageFunction + ')(...args)';

@@ -481,12 +386,7 @@ if (selector !== undefined)

const innerPredicate = new Function('...args', predicateBody);
if (polling === 'raf')
return injected.pollRaf(selector, predicate, timeout);
if (polling === 'mutation')
return injected.pollMutation(selector, predicate, timeout);
return injected.pollInterval(selector, polling, predicate, timeout);
function predicate(element) {
return injected.poll(polling, selector, timeout, (element) => {
if (selector === undefined)
return innerPredicate(...args);
return innerPredicate(element, ...args);
}
});
}, await context._injected(), selector, predicateBody, polling, options.timeout || 0, ...args);

@@ -496,17 +396,13 @@ }

function waitForSelectorTask(selector, visibility, timeout) {
return async (context) => {
selector = normalizeSelector(selector);
return context.evaluateHandle((injected, selector, visibility, timeout) => {
if (visibility !== 'any')
return injected.pollRaf(selector, predicate, timeout);
return injected.pollMutation(selector, predicate, timeout);
function predicate(element) {
if (!element)
return visibility === 'hidden';
if (visibility === 'any')
return element;
return injected.isVisible(element) === (visibility === 'visible') ? element : false;
}
}, await context._injected(), selector, visibility, timeout);
};
selector = normalizeSelector(selector);
return async (context) => context.evaluateHandle((injected, selector, visibility, timeout) => {
const polling = visibility === 'any' ? 'mutation' : 'raf';
return injected.poll(polling, selector, timeout, (element) => {
if (!element)
return visibility === 'hidden';
if (visibility === 'any')
return element;
return injected.isVisible(element) === (visibility === 'visible') ? element : false;
});
}, await context._injected(), selector, visibility, timeout);
}

@@ -524,3 +420,4 @@ exports.waitForSelectorTask = waitForSelectorTask;

element.dispatchEvent(new Event('input', { 'bubbles': true }));
element.dispatchEvent(new Event('change', { 'bubbles': true }));
};
//# sourceMappingURL=dom.js.map

@@ -43,5 +43,7 @@ /**

Popup: string;
WorkerCreated: string;
WorkerDestroyed: string;
Worker: string;
};
Worker: {
Close: string;
};
};

@@ -47,6 +47,8 @@ "use strict";

Popup: 'popup',
WorkerCreated: 'workercreated',
WorkerDestroyed: 'workerdestroyed',
Worker: 'worker',
},
Worker: {
Close: 'close',
},
};
//# sourceMappingURL=events.js.map

@@ -19,2 +19,4 @@ /**

import { BrowserContext, BrowserContextOptions } from '../browserContext';
import * as network from '../network';
import * as types from '../types';
import { Page } from '../page';

@@ -26,7 +28,8 @@ import { FFConnection, FFSession } from './ffConnection';

import { ConnectionTransport } from '../transport';
import { TimeoutSettings } from '../timeoutSettings';
export declare class FFBrowser extends platform.EventEmitter implements Browser {
_connection: FFConnection;
_targets: Map<string, Target>;
readonly _defaultContext: BrowserContext;
private _contexts;
readonly _defaultContext: FFBrowserContext;
readonly _contexts: Map<string, FFBrowserContext>;
private _eventListeners;

@@ -48,3 +51,2 @@ static connect(transport: ConnectionTransport, slowMo?: number): Promise<FFBrowser>;

close(): Promise<void>;
_createBrowserContext(browserContextId: string | null, options: BrowserContextOptions): BrowserContext;
_setDebugFunction(debugFunction: (message: string) => void): void;

@@ -72,2 +74,30 @@ }

}
export declare class FFBrowserContext extends platform.EventEmitter implements BrowserContext {
readonly _browser: FFBrowser;
readonly _browserContextId: string | null;
readonly _options: BrowserContextOptions;
readonly _timeoutSettings: TimeoutSettings;
private _closed;
private readonly _evaluateOnNewDocumentSources;
constructor(browser: FFBrowser, browserContextId: string | null, options: BrowserContextOptions);
_initialize(): Promise<void>;
_existingPages(): Page[];
setDefaultNavigationTimeout(timeout: number): void;
setDefaultTimeout(timeout: number): void;
pages(): Promise<Page[]>;
newPage(): Promise<Page>;
cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
clearCookies(): Promise<void>;
setPermissions(origin: string, permissions: string[]): Promise<void>;
clearPermissions(): Promise<void>;
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
addInitScript(script: Function | string | {
path?: string;
content?: string;
}, ...args: any[]): Promise<void>;
close(): Promise<void>;
_browserClosed(): void;
}
export {};

@@ -23,2 +23,3 @@ "use strict";

const helper_1 = require("../helper");
const network = require("../network");
const ffConnection_1 = require("./ffConnection");

@@ -28,2 +29,4 @@ const ffPage_1 = require("./ffPage");

const transport_1 = require("../transport");
const timeoutSettings_1 = require("../timeoutSettings");
const ffNetworkManager_1 = require("./ffNetworkManager");
class FFBrowser extends platform.EventEmitter {

@@ -34,6 +37,6 @@ constructor(connection) {

this._targets = new Map();
this._defaultContext = this._createBrowserContext(null, {});
this._defaultContext = new FFBrowserContext(this, null, browserContext_1.validateBrowserContextOptions({}));
this._contexts = new Map();
this._connection.on(ffConnection_1.ConnectionEvents.Disconnected, () => {
for (const context of this.contexts())
for (const context of this._contexts.values())
context._browserClosed();

@@ -59,8 +62,20 @@ this.emit(events_1.Events.Browser.Disconnected);

async newContext(options = {}) {
const viewport = options.viewport ? {
viewportSize: { width: options.viewport.width, height: options.viewport.height },
isMobile: !!options.viewport.isMobile,
deviceScaleFactor: options.viewport.deviceScaleFactor || 1,
hasTouch: !!options.viewport.isMobile,
} : undefined;
options = browserContext_1.validateBrowserContextOptions(options);
let viewport;
if (options.viewport) {
viewport = {
viewportSize: { width: options.viewport.width, height: options.viewport.height },
isMobile: !!options.viewport.isMobile,
deviceScaleFactor: options.viewport.deviceScaleFactor || 1,
hasTouch: !!options.viewport.isMobile,
};
}
else if (options.viewport !== null) {
viewport = {
viewportSize: { width: 1280, height: 720 },
isMobile: false,
deviceScaleFactor: 1,
hasTouch: false,
};
}
const { browserContextId } = await this._connection.send('Target.createBrowserContext', {

@@ -71,2 +86,3 @@ userAgent: options.userAgent,

viewport,
removeOnDetach: true
});

@@ -76,3 +92,3 @@ // TODO: move ignoreHTTPSErrors to browser context level.

await this._connection.send('Browser.setIgnoreHTTPSErrors', { enabled: true });
const context = this._createBrowserContext(browserContextId, options);
const context = new FFBrowserContext(this, browserContextId, options);
await context._initialize();

@@ -149,68 +165,2 @@ this._contexts.set(browserContextId, context);

}
_createBrowserContext(browserContextId, options) {
browserContext_1.BrowserContext.validateOptions(options);
const context = new browserContext_1.BrowserContext({
pages: async () => {
const targets = this._allTargets().filter(target => target.context() === context && target.type() === 'page');
const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page);
},
existingPages: () => {
const pages = [];
for (const target of this._allTargets()) {
if (target.context() === context && target._ffPage)
pages.push(target._ffPage._page);
}
return pages;
},
newPage: async () => {
const { targetId } = await this._connection.send('Target.newPage', {
browserContextId: browserContextId || undefined
});
const target = this._targets.get(targetId);
return target.page();
},
close: async () => {
helper_1.assert(browserContextId, 'Non-incognito profiles cannot be closed!');
await this._connection.send('Target.removeBrowserContext', { browserContextId });
this._contexts.delete(browserContextId);
},
cookies: async () => {
const { cookies } = await this._connection.send('Browser.getCookies', { browserContextId: browserContextId || undefined });
return cookies.map(c => {
const copy = { ...c };
delete copy.size;
return copy;
});
},
clearCookies: async () => {
await this._connection.send('Browser.clearCookies', { browserContextId: browserContextId || undefined });
},
setCookies: async (cookies) => {
await this._connection.send('Browser.setCookies', { browserContextId: browserContextId || undefined, cookies });
},
setPermissions: async (origin, permissions) => {
const webPermissionToProtocol = new Map([
['geolocation', 'geo'],
['microphone', 'microphone'],
['camera', 'camera'],
['notifications', 'desktop-notifications'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._connection.send('Browser.grantPermissions', { origin, browserContextId: browserContextId || undefined, permissions: filtered });
},
clearPermissions: async () => {
await this._connection.send('Browser.resetPermissions', { browserContextId: browserContextId || undefined });
},
setGeolocation: async (geolocation) => {
throw new Error('Geolocation emulation is not supported in Firefox');
}
}, options);
return context;
}
_setDebugFunction(debugFunction) {

@@ -273,2 +223,108 @@ this._connection._debugProtocol = debugFunction;

}
class FFBrowserContext extends platform.EventEmitter {
constructor(browser, browserContextId, options) {
super();
this._closed = false;
this._browser = browser;
this._browserContextId = browserContextId;
this._timeoutSettings = new timeoutSettings_1.TimeoutSettings();
this._options = options;
this._evaluateOnNewDocumentSources = [];
}
async _initialize() {
const entries = Object.entries(this._options.permissions || {});
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
if (this._options.geolocation)
await this.setGeolocation(this._options.geolocation);
if (this._options.extraHTTPHeaders)
await this.setExtraHTTPHeaders(this._options.extraHTTPHeaders);
}
_existingPages() {
const pages = [];
for (const target of this._browser._allTargets()) {
if (target.context() === this && target._ffPage)
pages.push(target._ffPage._page);
}
return pages;
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async pages() {
const targets = this._browser._allTargets().filter(target => target.context() === this && target.type() === 'page');
const pages = await Promise.all(targets.map(target => target.page()));
return pages.filter(page => !!page);
}
async newPage() {
browserContext_1.assertBrowserContextIsNotOwned(this);
const { targetId } = await this._browser._connection.send('Target.newPage', {
browserContextId: this._browserContextId || undefined
});
const target = this._browser._targets.get(targetId);
return target.page();
}
async cookies(...urls) {
const { cookies } = await this._browser._connection.send('Browser.getCookies', { browserContextId: this._browserContextId || undefined });
return network.filterCookies(cookies.map(c => {
const copy = { ...c };
delete copy.size;
return copy;
}), urls);
}
async setCookies(cookies) {
await this._browser._connection.send('Browser.setCookies', { browserContextId: this._browserContextId || undefined, cookies: network.rewriteCookies(cookies) });
}
async clearCookies() {
await this._browser._connection.send('Browser.clearCookies', { browserContextId: this._browserContextId || undefined });
}
async setPermissions(origin, permissions) {
const webPermissionToProtocol = new Map([
['geolocation', 'geo'],
['microphone', 'microphone'],
['camera', 'camera'],
['notifications', 'desktop-notifications'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._browser._connection.send('Browser.grantPermissions', { origin, browserContextId: this._browserContextId || undefined, permissions: filtered });
}
async clearPermissions() {
await this._browser._connection.send('Browser.resetPermissions', { browserContextId: this._browserContextId || undefined });
}
async setGeolocation(geolocation) {
throw new Error('Geolocation emulation is not supported in Firefox');
}
async setExtraHTTPHeaders(headers) {
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
await this._browser._connection.send('Browser.setExtraHTTPHeaders', { browserContextId: this._browserContextId || undefined, headers: ffNetworkManager_1.headersArray(this._options.extraHTTPHeaders) });
}
async addInitScript(script, ...args) {
const source = await helper_1.helper.evaluationScript(script, ...args);
this._evaluateOnNewDocumentSources.push(source);
await this._browser._connection.send('Browser.addScriptToEvaluateOnNewDocument', { browserContextId: this._browserContextId || undefined, script: source });
}
async close() {
if (this._closed)
return;
helper_1.assert(this._browserContextId, 'Non-incognito profiles cannot be closed!');
await this._browser._connection.send('Target.removeBrowserContext', { browserContextId: this._browserContextId });
this._browser._contexts.delete(this._browserContextId);
this._closed = true;
this.emit(events_1.Events.BrowserContext.Close);
}
_browserClosed() {
this._closed = true;
for (const page of this._existingPages())
page._didClose();
this.emit(events_1.Events.BrowserContext.Close);
}
}
exports.FFBrowserContext = FFBrowserContext;
//# sourceMappingURL=ffBrowser.js.map

@@ -61,5 +61,5 @@ "use strict";

_rawSend(message) {
message = JSON.stringify(message);
this._debugProtocol('SEND ► ' + message);
this._transport.send(message);
const data = JSON.stringify(message);
this._debugProtocol('SEND ► ' + (rewriteInjectedScriptEvaluationLog(message) || data));
this._transport.send(data);
}

@@ -184,2 +184,8 @@ async _onMessage(message) {

}
function rewriteInjectedScriptEvaluationLog(message) {
// Injected script is very long and clutters protocol logs.
// To increase development velocity, we skip replace it with short description in the log.
if (message.method === 'Runtime.evaluate' && message.params && message.params.expression && message.params.expression.includes('src/injected/injected.ts'))
return `{"id":${message.id} [evaluate injected script]}`;
}
//# sourceMappingURL=ffConnection.js.map

@@ -59,2 +59,5 @@ "use strict";

code = 'OSRight';
// Firefox will figure out Enter by itself
if (text === '\r')
text = '';
await this._client.send('Page.dispatchKeyEvent', {

@@ -61,0 +64,0 @@ type: 'keydown',

@@ -19,2 +19,3 @@ /**

import { Page } from '../page';
import * as network from '../network';
import { Protocol } from './protocol';

@@ -34,1 +35,2 @@ export declare class FFNetworkManager {

}
export declare function headersArray(headers: network.Headers): Protocol.Network.HTTPHeader[];

@@ -185,2 +185,3 @@ "use strict";

}
exports.headersArray = headersArray;
//# sourceMappingURL=ffNetworkManager.js.map

@@ -25,3 +25,2 @@ /**

import { BrowserContext } from '../browserContext';
import * as network from '../network';
import * as types from '../types';

@@ -63,3 +62,3 @@ import * as platform from '../platform';

navigateFrame(frame: frames.Frame, url: string, referer: string | undefined): Promise<frames.GotoResult>;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
updateExtraHTTPHeaders(): Promise<void>;
setViewportSize(viewportSize: types.Size): Promise<void>;

@@ -66,0 +65,0 @@ setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;

@@ -211,7 +211,4 @@ "use strict";

}
async setExtraHTTPHeaders(headers) {
const array = [];
for (const [name, value] of Object.entries(headers))
array.push({ name, value });
await this._session.send('Network.setExtraHTTPHeaders', { headers: array });
async updateExtraHTTPHeaders() {
await this._session.send('Network.setExtraHTTPHeaders', { headers: ffNetworkManager_1.headersArray(this._page._state.extraHTTPHeaders || {}) });
}

@@ -218,0 +215,0 @@ async setViewportSize(viewportSize) {

@@ -37,2 +37,15 @@ export declare module Protocol {

type setIgnoreHTTPSErrorsReturnValue = void;
type setExtraHTTPHeadersParameters = {
browserContextId?: string;
headers: {
name: string;
value: string;
}[];
};
type setExtraHTTPHeadersReturnValue = void;
type addScriptToEvaluateOnNewDocumentParameters = {
browserContextId?: string;
script: string;
};
type addScriptToEvaluateOnNewDocumentReturnValue = void;
type grantPermissionsParameters = {

@@ -837,2 +850,4 @@ origin: string;

"Browser.setIgnoreHTTPSErrors": Browser.setIgnoreHTTPSErrorsParameters;
"Browser.setExtraHTTPHeaders": Browser.setExtraHTTPHeadersParameters;
"Browser.addScriptToEvaluateOnNewDocument": Browser.addScriptToEvaluateOnNewDocumentParameters;
"Browser.grantPermissions": Browser.grantPermissionsParameters;

@@ -892,2 +907,4 @@ "Browser.resetPermissions": Browser.resetPermissionsParameters;

"Browser.setIgnoreHTTPSErrors": Browser.setIgnoreHTTPSErrorsReturnValue;
"Browser.setExtraHTTPHeaders": Browser.setExtraHTTPHeadersReturnValue;
"Browser.addScriptToEvaluateOnNewDocument": Browser.addScriptToEvaluateOnNewDocumentReturnValue;
"Browser.grantPermissions": Browser.grantPermissionsReturnValue;

@@ -894,0 +911,0 @@ "Browser.resetPermissions": Browser.resetPermissionsReturnValue;

@@ -22,3 +22,2 @@ /**

import * as network from './network';
import { ClickOptions, MultiClickOptions, PointerActionOptions } from './input';
import { Page } from './page';

@@ -41,5 +40,2 @@ import { ConsoleMessage } from './console';

export declare type LifecycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
export declare type WaitForOptions = types.TimeoutOptions & {
waitFor?: types.Visibility | 'nowait';
};
declare type ConsoleTagHandler = () => void;

@@ -142,14 +138,14 @@ export declare class FrameManager {

private _raceWithCSPError;
click(selector: string, options?: WaitForOptions & ClickOptions): Promise<void>;
dblclick(selector: string, options?: WaitForOptions & MultiClickOptions): Promise<void>;
tripleclick(selector: string, options?: WaitForOptions & MultiClickOptions): Promise<void>;
fill(selector: string, value: string, options?: WaitForOptions): Promise<void>;
focus(selector: string, options?: WaitForOptions): Promise<void>;
hover(selector: string, options?: WaitForOptions & PointerActionOptions): Promise<void>;
select(selector: string, value: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | undefined, options?: WaitForOptions): Promise<string[]>;
type(selector: string, text: string, options?: WaitForOptions & {
click(selector: string, options?: dom.ClickOptions & types.WaitForOptions): Promise<void>;
dblclick(selector: string, options?: dom.MultiClickOptions & types.WaitForOptions): Promise<void>;
tripleclick(selector: string, options?: dom.MultiClickOptions & types.WaitForOptions): Promise<void>;
fill(selector: string, value: string, options?: types.WaitForOptions): Promise<void>;
focus(selector: string, options?: types.WaitForOptions): Promise<void>;
hover(selector: string, options?: dom.PointerActionOptions & types.WaitForOptions): Promise<void>;
select(selector: string, value: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | undefined, options?: types.WaitForOptions): Promise<string[]>;
type(selector: string, text: string, options?: {
delay?: number;
}): Promise<void>;
check(selector: string, options?: WaitForOptions): Promise<void>;
uncheck(selector: string, options?: WaitForOptions): Promise<void>;
} & types.WaitForOptions): Promise<void>;
check(selector: string, options?: types.WaitForOptions): Promise<void>;
uncheck(selector: string, options?: types.WaitForOptions): Promise<void>;
waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & {

@@ -156,0 +152,0 @@ visibility?: types.Visibility;

@@ -379,5 +379,6 @@ "use strict";

const watch = (documentId, error) => {
if (documentId !== expectedDocumentId)
return resolve(new Error('Navigation interrupted by another one'));
resolve(error);
if (documentId === expectedDocumentId)
resolve(error);
else if (!error)
resolve(new Error('Navigation interrupted by another one'));
};

@@ -701,3 +702,3 @@ const dispose = () => this._documentWatchers.delete(watch);

const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
await handle.check();
await handle.check(options);
await handle.dispose();

@@ -707,3 +708,3 @@ }

const handle = await this._optionallyWaitForSelectorInUtilityContext(selector, options);
await handle.uncheck();
await handle.uncheck(options);
await handle.dispose();

@@ -721,8 +722,10 @@ }

async _optionallyWaitForSelectorInUtilityContext(selector, options) {
const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'visible' } = (options || {});
const { timeout = this._page._timeoutSettings.timeout(), waitFor = true } = (options || {});
if (!helper_1.helper.isBoolean(waitFor))
throw new Error('waitFor option should be a boolean, got "' + (typeof waitFor) + '"');
let handle;
if (waitFor !== 'nowait') {
const maybeHandle = await this._waitForSelectorInUtilityContext(selector, waitFor, timeout);
if (waitFor) {
const maybeHandle = await this._waitForSelectorInUtilityContext(selector, 'any', timeout);
if (!maybeHandle)
throw new Error('No node found for selector: ' + selectorToString(selector, waitFor));
throw new Error('No node found for selector: ' + selectorToString(selector, 'any'));
handle = maybeHandle;

@@ -743,3 +746,3 @@ }

else
throw new Error(`Unsupported waitFor option "${waitFor}"`);
throw new Error(`Unsupported visibility option "${waitFor}"`);
const task = dom.waitForSelectorTask(selector, visibility, timeout);

@@ -746,0 +749,0 @@ const result = await this._scheduleRerunnableTask(task, 'utility', timeout, `selector "${selectorToString(selector, visibility)}"`);

@@ -1,1 +0,1 @@

export declare const source = "(/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./src/injected/injected.ts\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./src/injected/cssSelectorEngine.ts\":\n/*!*******************************************!*\\\n !*** ./src/injected/cssSelectorEngine.ts ***!\n \\*******************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.CSSEngine = {\n name: 'css',\n create(root, targetElement) {\n const tokens = [];\n function uniqueCSSSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n const selector = path.join(' > ');\n const nodes = Array.from(root.querySelectorAll(selector));\n return nodes[0] === targetElement ? selector : undefined;\n }\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n // Element ID is the strongest signal, use it.\n let bestTokenForLevel = '';\n if (element.id) {\n const token = /^[a-zA-Z][a-zA-Z0-9\\-\\_]+$/.test(element.id) ? '#' + element.id : `[id=\"${element.id}\"]`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n bestTokenForLevel = token;\n }\n const parent = element.parentElement;\n // Combine class names until unique.\n const classes = Array.from(element.classList);\n for (let i = 0; i < classes.length; ++i) {\n const token = '.' + classes.slice(0, i + 1).join('.');\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n // Even if not unique, does this subset of classes uniquely identify node as a child?\n if (!bestTokenForLevel && parent) {\n const sameClassSiblings = parent.querySelectorAll(token);\n if (sameClassSiblings.length === 1)\n bestTokenForLevel = token;\n }\n }\n // Ordinal is the weakest signal.\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n const token = sameTagSiblings.length === 1 ? nodeName : `${nodeName}:nth-child(${1 + siblings.indexOf(element)})`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n if (!bestTokenForLevel)\n bestTokenForLevel = token;\n }\n else if (!bestTokenForLevel) {\n bestTokenForLevel = nodeName;\n }\n tokens.unshift(bestTokenForLevel);\n }\n return uniqueCSSSelector();\n },\n query(root, selector) {\n return root.querySelector(selector) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(selector));\n }\n};\n\n\n/***/ }),\n\n/***/ \"./src/injected/injected.ts\":\n/*!**********************************!*\\\n !*** ./src/injected/injected.ts ***!\n \\**********************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst utils_1 = __webpack_require__(/*! ./utils */ \"./src/injected/utils.ts\");\nconst cssSelectorEngine_1 = __webpack_require__(/*! ./cssSelectorEngine */ \"./src/injected/cssSelectorEngine.ts\");\nconst xpathSelectorEngine_1 = __webpack_require__(/*! ./xpathSelectorEngine */ \"./src/injected/xpathSelectorEngine.ts\");\nconst textSelectorEngine_1 = __webpack_require__(/*! ./textSelectorEngine */ \"./src/injected/textSelectorEngine.ts\");\nfunction createAttributeEngine(attribute) {\n const engine = {\n name: attribute,\n create(root, target) {\n const value = target.getAttribute(attribute);\n if (!value)\n return;\n if (root.querySelector(`[${attribute}=${value}]`) === target)\n return value;\n },\n query(root, selector) {\n return root.querySelector(`[${attribute}=${selector}]`) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(`[${attribute}=${selector}]`));\n }\n };\n return engine;\n}\nclass Injected {\n constructor(customEngines) {\n const defaultEngines = [\n cssSelectorEngine_1.CSSEngine,\n xpathSelectorEngine_1.XPathEngine,\n textSelectorEngine_1.TextEngine,\n createAttributeEngine('id'),\n createAttributeEngine('data-testid'),\n createAttributeEngine('data-test-id'),\n createAttributeEngine('data-test'),\n ];\n this.utils = new utils_1.Utils();\n this.engines = new Map();\n for (const engine of [...defaultEngines, ...customEngines])\n this.engines.set(engine.name, engine);\n }\n querySelector(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelector'])\n throw new Error('Node is not queryable.');\n let element = root;\n for (const { engine, selector } of parsed) {\n const next = engine.query(element.shadowRoot || element, selector);\n if (!next)\n return;\n element = next;\n }\n return element;\n }\n querySelectorAll(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelectorAll'])\n throw new Error('Node is not queryable.');\n let set = new Set([root]);\n for (const { engine, selector } of parsed) {\n const newSet = new Set();\n for (const prev of set) {\n for (const next of engine.queryAll(prev.shadowRoot || prev, selector)) {\n if (newSet.has(next))\n continue;\n newSet.add(next);\n }\n }\n set = newSet;\n }\n return Array.from(set);\n }\n _parseSelector(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = [];\n const append = () => {\n const part = selector.substring(start, index);\n const eqIndex = part.indexOf('=');\n if (eqIndex === -1)\n throw new Error(`Cannot parse selector ${selector}`);\n const name = part.substring(0, eqIndex).trim();\n const body = part.substring(eqIndex + 1);\n const engine = this.engines.get(name.toLowerCase());\n if (!engine)\n throw new Error(`Unknown engine ${name} while parsing selector ${selector}`);\n result.push({ engine, selector: body });\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === '\\\\' && index + 1 < selector.length) {\n index += 2;\n }\n else if (c === quote) {\n quote = undefined;\n index++;\n }\n else if (!quote && c === '>' && selector[index + 1] === '>') {\n append();\n index += 2;\n start = index;\n }\n else {\n index++;\n }\n }\n append();\n return result;\n }\n isVisible(element) {\n if (!element.ownerDocument || !element.ownerDocument.defaultView)\n return true;\n const style = element.ownerDocument.defaultView.getComputedStyle(element);\n if (!style || style.visibility === 'hidden')\n return false;\n const rect = element.getBoundingClientRect();\n return !!(rect.top || rect.bottom || rect.width || rect.height);\n }\n pollMutation(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n return Promise.resolve(success);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const observer = new MutationObserver(() => {\n if (timedOut) {\n observer.disconnect();\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success) {\n observer.disconnect();\n fulfill(success);\n }\n });\n observer.observe(document, {\n childList: true,\n subtree: true,\n attributes: true\n });\n return result;\n }\n pollRaf(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onRaf = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n requestAnimationFrame(onRaf);\n };\n onRaf();\n return result;\n }\n pollInterval(selector, pollInterval, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onTimeout = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n setTimeout(onTimeout, pollInterval);\n };\n onTimeout();\n return result;\n }\n}\nexports.default = Injected;\n\n\n/***/ }),\n\n/***/ \"./src/injected/textSelectorEngine.ts\":\n/*!********************************************!*\\\n !*** ./src/injected/textSelectorEngine.ts ***!\n \\********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TextEngine = {\n name: 'text',\n create(root, targetElement, type) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n for (let child = targetElement.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 3 /* Node.TEXT_NODE */) {\n const text = child.nodeValue;\n if (!text)\n continue;\n if (text.match(/^\\s*[a-zA-Z0-9]+\\s*$/) && exports.TextEngine.query(root, text.trim()) === targetElement)\n return text.trim();\n if (exports.TextEngine.query(root, JSON.stringify(text)) === targetElement)\n return JSON.stringify(text);\n }\n }\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n return element;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n result.push(element);\n }\n return result;\n }\n};\nfunction createMatcher(selector) {\n if (selector[0] === '\"' && selector[selector.length - 1] === '\"') {\n const parsed = JSON.parse(selector);\n return text => text === parsed;\n }\n if (selector[0] === '/' && selector.lastIndexOf('/') > 0) {\n const lastSlash = selector.lastIndexOf('/');\n const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));\n return text => re.test(text);\n }\n selector = selector.trim();\n return text => text.trim() === selector;\n}\n\n\n/***/ }),\n\n/***/ \"./src/injected/utils.ts\":\n/*!*******************************!*\\\n !*** ./src/injected/utils.ts ***!\n \\*******************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass Utils {\n parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE && element.parentNode.host)\n return element.parentNode.host;\n }\n deepElementFromPoint(document, x, y) {\n let container = document;\n let element;\n while (container) {\n const innerElement = container.elementFromPoint(x, y);\n if (!innerElement || element === innerElement)\n break;\n element = innerElement;\n container = element.shadowRoot;\n }\n return element;\n }\n}\nexports.Utils = Utils;\n\n\n/***/ }),\n\n/***/ \"./src/injected/xpathSelectorEngine.ts\":\n/*!*********************************************!*\\\n !*** ./src/injected/xpathSelectorEngine.ts ***!\n \\*********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst maxTextLength = 80;\nconst minMeaningfulSelectorLegth = 100;\nexports.XPathEngine = {\n name: 'xpath',\n create(root, targetElement, type) {\n const maybeDocument = root instanceof Document ? root : root.ownerDocument;\n if (!maybeDocument)\n return;\n const document = maybeDocument;\n const xpathCache = new Map();\n if (type === 'notext')\n return createNoText(root, targetElement);\n const tokens = [];\n function evaluateXPath(expression) {\n let nodes = xpathCache.get(expression);\n if (!nodes) {\n nodes = [];\n try {\n const result = document.evaluate(expression, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = result.iterateNext(); node; node = result.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n nodes.push(node);\n }\n }\n catch (e) {\n }\n xpathCache.set(expression, nodes);\n }\n return nodes;\n }\n function uniqueXPathSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n let selector = '//' + path.join('/');\n while (selector.includes('///'))\n selector = selector.replace('///', '//');\n if (selector.endsWith('/'))\n selector = selector.substring(0, selector.length - 1);\n const nodes = evaluateXPath(selector);\n if (nodes[nodes.length - 1] === targetElement)\n return selector;\n // If we are looking at a small set of elements with long selector, fall back to ordinal.\n if (nodes.length < 5 && selector.length > minMeaningfulSelectorLegth) {\n const index = nodes.indexOf(targetElement);\n if (index !== -1)\n return `(${selector})[${index + 1}]`;\n }\n return undefined;\n }\n function escapeAndCap(text) {\n text = text.substring(0, maxTextLength);\n // XPath 1.0 does not support quote escaping.\n // 1. If there are no single quotes - use them.\n if (text.indexOf(`'`) === -1)\n return `'${text}'`;\n // 2. If there are no double quotes - use them to enclose text.\n if (text.indexOf(`\"`) === -1)\n return `\"${text}\"`;\n // 3. Otherwise, use popular |concat| trick.\n const Q = `'`;\n return `concat(${text.split(Q).map(token => Q + token + Q).join(`, \"'\", `)})`;\n }\n const defaultAttributes = new Set(['title', 'aria-label', 'disabled', 'role']);\n const importantAttributes = new Map([\n ['form', ['action']],\n ['img', ['alt']],\n ['input', ['placeholder', 'type', 'name', 'value']],\n ]);\n let usedTextConditions = false;\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n const tag = nodeName === 'svg' ? '*' : nodeName;\n const tagConditions = [];\n if (nodeName === 'svg')\n tagConditions.push('local-name()=\"svg\"');\n const attrConditions = [];\n const importantAttrs = [...defaultAttributes, ...(importantAttributes.get(tag) || [])];\n for (const attr of importantAttrs) {\n const value = element.getAttribute(attr);\n if (value && value.length < maxTextLength)\n attrConditions.push(`normalize-space(@${attr})=${escapeAndCap(value)}`);\n else if (value)\n attrConditions.push(`starts-with(normalize-space(@${attr}), ${escapeAndCap(value)})`);\n }\n const text = document.evaluate('normalize-space(.)', element).stringValue;\n const textConditions = [];\n if (tag !== 'select' && text.length && !usedTextConditions) {\n if (text.length < maxTextLength)\n textConditions.push(`normalize-space(.)=${escapeAndCap(text)}`);\n else\n textConditions.push(`starts-with(normalize-space(.), ${escapeAndCap(text)})`);\n usedTextConditions = true;\n }\n // Always retain the last tag.\n const conditions = [...tagConditions, ...textConditions, ...attrConditions];\n const token = conditions.length ? `${tag}[${conditions.join(' and ')}]` : (tokens.length ? '' : tag);\n const selector = uniqueXPathSelector(token);\n if (selector)\n return selector;\n // Ordinal is the weakest signal.\n const parent = element.parentElement;\n let tagWithOrdinal = tag;\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n if (sameTagSiblings.length > 1)\n tagWithOrdinal += `[${1 + siblings.indexOf(element)}]`;\n }\n // Do not include text into this token, only tag / attributes.\n // Topmost node will get all the text.\n const nonTextConditions = [...tagConditions, ...attrConditions];\n const levelToken = nonTextConditions.length ? `${tagWithOrdinal}[${nonTextConditions.join(' and ')}]` : tokens.length ? '' : tagWithOrdinal;\n tokens.unshift(levelToken);\n }\n return uniqueXPathSelector();\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n return node;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n result.push(node);\n }\n return result;\n }\n};\nfunction createNoText(root, targetElement) {\n const steps = [];\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n if (element.getAttribute('id')) {\n steps.unshift(`//*[@id=\"${element.getAttribute('id')}\"]`);\n return steps.join('/');\n }\n const siblings = element.parentElement ? Array.from(element.parentElement.children) : [];\n const similarElements = siblings.filter(sibling => element.nodeName === sibling.nodeName);\n const index = similarElements.length === 1 ? 0 : similarElements.indexOf(element) + 1;\n steps.unshift(index ? `${element.nodeName}[${index}]` : element.nodeName);\n }\n return '/' + steps.join('/');\n}\n\n\n/***/ })\n\n/******/ })).default";
export declare const source = "(/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./src/injected/injected.ts\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./src/injected/cssSelectorEngine.ts\":\n/*!*******************************************!*\\\n !*** ./src/injected/cssSelectorEngine.ts ***!\n \\*******************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.CSSEngine = {\n name: 'css',\n create(root, targetElement) {\n const tokens = [];\n function uniqueCSSSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n const selector = path.join(' > ');\n const nodes = Array.from(root.querySelectorAll(selector));\n return nodes[0] === targetElement ? selector : undefined;\n }\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n // Element ID is the strongest signal, use it.\n let bestTokenForLevel = '';\n if (element.id) {\n const token = /^[a-zA-Z][a-zA-Z0-9\\-\\_]+$/.test(element.id) ? '#' + element.id : `[id=\"${element.id}\"]`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n bestTokenForLevel = token;\n }\n const parent = element.parentElement;\n // Combine class names until unique.\n const classes = Array.from(element.classList);\n for (let i = 0; i < classes.length; ++i) {\n const token = '.' + classes.slice(0, i + 1).join('.');\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n // Even if not unique, does this subset of classes uniquely identify node as a child?\n if (!bestTokenForLevel && parent) {\n const sameClassSiblings = parent.querySelectorAll(token);\n if (sameClassSiblings.length === 1)\n bestTokenForLevel = token;\n }\n }\n // Ordinal is the weakest signal.\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n const token = sameTagSiblings.length === 1 ? nodeName : `${nodeName}:nth-child(${1 + siblings.indexOf(element)})`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n if (!bestTokenForLevel)\n bestTokenForLevel = token;\n }\n else if (!bestTokenForLevel) {\n bestTokenForLevel = nodeName;\n }\n tokens.unshift(bestTokenForLevel);\n }\n return uniqueCSSSelector();\n },\n query(root, selector) {\n return root.querySelector(selector) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(selector));\n }\n};\n\n\n/***/ }),\n\n/***/ \"./src/injected/injected.ts\":\n/*!**********************************!*\\\n !*** ./src/injected/injected.ts ***!\n \\**********************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst utils_1 = __webpack_require__(/*! ./utils */ \"./src/injected/utils.ts\");\nconst cssSelectorEngine_1 = __webpack_require__(/*! ./cssSelectorEngine */ \"./src/injected/cssSelectorEngine.ts\");\nconst xpathSelectorEngine_1 = __webpack_require__(/*! ./xpathSelectorEngine */ \"./src/injected/xpathSelectorEngine.ts\");\nconst textSelectorEngine_1 = __webpack_require__(/*! ./textSelectorEngine */ \"./src/injected/textSelectorEngine.ts\");\nfunction createAttributeEngine(attribute) {\n const engine = {\n name: attribute,\n create(root, target) {\n const value = target.getAttribute(attribute);\n if (!value)\n return;\n if (root.querySelector(`[${attribute}=${value}]`) === target)\n return value;\n },\n query(root, selector) {\n return root.querySelector(`[${attribute}=${selector}]`) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(`[${attribute}=${selector}]`));\n }\n };\n return engine;\n}\nclass Injected {\n constructor(customEngines) {\n const defaultEngines = [\n cssSelectorEngine_1.CSSEngine,\n xpathSelectorEngine_1.XPathEngine,\n textSelectorEngine_1.TextEngine,\n createAttributeEngine('id'),\n createAttributeEngine('data-testid'),\n createAttributeEngine('data-test-id'),\n createAttributeEngine('data-test'),\n ];\n this.utils = new utils_1.Utils();\n this.engines = new Map();\n for (const engine of [...defaultEngines, ...customEngines])\n this.engines.set(engine.name, engine);\n }\n querySelector(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelector'])\n throw new Error('Node is not queryable.');\n let element = root;\n for (const { engine, selector } of parsed) {\n const next = engine.query(element.shadowRoot || element, selector);\n if (!next)\n return;\n element = next;\n }\n return element;\n }\n querySelectorAll(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelectorAll'])\n throw new Error('Node is not queryable.');\n let set = new Set([root]);\n for (const { engine, selector } of parsed) {\n const newSet = new Set();\n for (const prev of set) {\n for (const next of engine.queryAll(prev.shadowRoot || prev, selector)) {\n if (newSet.has(next))\n continue;\n newSet.add(next);\n }\n }\n set = newSet;\n }\n return Array.from(set);\n }\n _parseSelector(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = [];\n const append = () => {\n const part = selector.substring(start, index);\n const eqIndex = part.indexOf('=');\n if (eqIndex === -1)\n throw new Error(`Cannot parse selector ${selector}`);\n const name = part.substring(0, eqIndex).trim();\n const body = part.substring(eqIndex + 1);\n const engine = this.engines.get(name.toLowerCase());\n if (!engine)\n throw new Error(`Unknown engine ${name} while parsing selector ${selector}`);\n result.push({ engine, selector: body });\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === '\\\\' && index + 1 < selector.length) {\n index += 2;\n }\n else if (c === quote) {\n quote = undefined;\n index++;\n }\n else if (!quote && c === '>' && selector[index + 1] === '>') {\n append();\n index += 2;\n start = index;\n }\n else {\n index++;\n }\n }\n append();\n return result;\n }\n isVisible(element) {\n if (!element.ownerDocument || !element.ownerDocument.defaultView)\n return true;\n const style = element.ownerDocument.defaultView.getComputedStyle(element);\n if (!style || style.visibility === 'hidden')\n return false;\n const rect = element.getBoundingClientRect();\n return !!(rect.top || rect.bottom || rect.width || rect.height);\n }\n _pollMutation(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n return Promise.resolve(success);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const observer = new MutationObserver(() => {\n if (timedOut) {\n observer.disconnect();\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success) {\n observer.disconnect();\n fulfill(success);\n }\n });\n observer.observe(document, {\n childList: true,\n subtree: true,\n attributes: true\n });\n return result;\n }\n _pollRaf(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onRaf = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n requestAnimationFrame(onRaf);\n };\n onRaf();\n return result;\n }\n _pollInterval(selector, pollInterval, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onTimeout = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n setTimeout(onTimeout, pollInterval);\n };\n onTimeout();\n return result;\n }\n poll(polling, selector, timeout, predicate) {\n if (polling === 'raf')\n return this._pollRaf(selector, predicate, timeout);\n if (polling === 'mutation')\n return this._pollMutation(selector, predicate, timeout);\n return this._pollInterval(selector, polling, predicate, timeout);\n }\n getElementBorderWidth(node) {\n if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)\n return { left: 0, top: 0 };\n const style = node.ownerDocument.defaultView.getComputedStyle(node);\n return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };\n }\n selectOptions(node, optionsToSelect) {\n if (node.nodeName.toLowerCase() !== 'select')\n throw new Error('Element is not a <select> element.');\n const element = node;\n const options = Array.from(element.options);\n element.value = undefined;\n for (let index = 0; index < options.length; index++) {\n const option = options[index];\n option.selected = optionsToSelect.some(optionToSelect => {\n if (optionToSelect instanceof Node)\n return option === optionToSelect;\n let matches = true;\n if (optionToSelect.value !== undefined)\n matches = matches && optionToSelect.value === option.value;\n if (optionToSelect.label !== undefined)\n matches = matches && optionToSelect.label === option.label;\n if (optionToSelect.index !== undefined)\n matches = matches && optionToSelect.index === index;\n return matches;\n });\n if (option.selected && !element.multiple)\n break;\n }\n element.dispatchEvent(new Event('input', { 'bubbles': true }));\n element.dispatchEvent(new Event('change', { 'bubbles': true }));\n return options.filter(option => option.selected).map(option => option.value);\n }\n fill(node, value) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n return 'Node is not of type HTMLElement';\n const element = node;\n if (!element.isConnected)\n return 'Element is not attached to the DOM';\n if (!element.ownerDocument || !element.ownerDocument.defaultView)\n return 'Element does not belong to a window';\n const style = element.ownerDocument.defaultView.getComputedStyle(element);\n if (!style || style.visibility === 'hidden')\n return 'Element is hidden';\n if (!element.offsetParent && element.tagName !== 'BODY')\n return 'Element is not visible';\n if (element.nodeName.toLowerCase() === 'input') {\n const input = element;\n const type = input.getAttribute('type') || '';\n const kTextInputTypes = new Set(['', 'email', 'number', 'password', 'search', 'tel', 'text', 'url']);\n if (!kTextInputTypes.has(type.toLowerCase()))\n return 'Cannot fill input of type \"' + type + '\".';\n if (type.toLowerCase() === 'number') {\n value = value.trim();\n if (!value || isNaN(Number(value)))\n return 'Cannot type text into input[type=number].';\n }\n if (input.disabled)\n return 'Cannot fill a disabled input.';\n if (input.readOnly)\n return 'Cannot fill a readonly input.';\n input.select();\n input.focus();\n }\n else if (element.nodeName.toLowerCase() === 'textarea') {\n const textarea = element;\n if (textarea.disabled)\n return 'Cannot fill a disabled textarea.';\n if (textarea.readOnly)\n return 'Cannot fill a readonly textarea.';\n textarea.selectionStart = 0;\n textarea.selectionEnd = textarea.value.length;\n textarea.focus();\n }\n else if (element.isContentEditable) {\n const range = element.ownerDocument.createRange();\n range.selectNodeContents(element);\n const selection = element.ownerDocument.defaultView.getSelection();\n if (!selection)\n return 'Element belongs to invisible iframe.';\n selection.removeAllRanges();\n selection.addRange(range);\n element.focus();\n }\n else {\n return 'Element is not an <input>, <textarea> or [contenteditable] element.';\n }\n return false;\n }\n isCheckboxChecked(node) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw new Error('Not a checkbox or radio button');\n let element = node;\n if (element.getAttribute('role') === 'checkbox')\n return element.getAttribute('aria-checked') === 'true';\n if (element.nodeName === 'LABEL') {\n const forId = element.getAttribute('for');\n if (forId && element.ownerDocument)\n element = element.ownerDocument.querySelector(`input[id=\"${forId}\"]`) || undefined;\n else\n element = element.querySelector('input[type=checkbox],input[type=radio]') || undefined;\n }\n if (element && element.nodeName === 'INPUT') {\n const type = element.getAttribute('type');\n if (type && (type.toLowerCase() === 'checkbox' || type.toLowerCase() === 'radio'))\n return element.checked;\n }\n throw new Error('Not a checkbox');\n }\n waitForStablePosition(node, timeout) {\n if (!node.isConnected)\n throw new Error('Element is not attached to the DOM');\n const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n if (!element)\n throw new Error('Element is not attached to the DOM');\n let lastRect;\n let counter = 0;\n return this.poll('raf', undefined, timeout, () => {\n // First raf happens in the same animation frame as evaluation, so it does not produce\n // any client rect difference compared to synchronous call. We skip the synchronous call\n // and only force layout during actual rafs as a small optimisation.\n if (++counter === 1)\n return false;\n const clientRect = element.getBoundingClientRect();\n const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };\n const isStable = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;\n lastRect = rect;\n return isStable;\n });\n }\n waitForHitTargetAt(node, timeout, point) {\n const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n if (!element)\n throw new Error('Element is not attached to the DOM');\n return this.poll('raf', undefined, timeout, () => {\n let hitElement = this.utils.deepElementFromPoint(document, point.x, point.y);\n while (hitElement && hitElement !== element)\n hitElement = this.utils.parentElementOrShadowHost(hitElement);\n return hitElement === element;\n });\n }\n}\nexports.default = Injected;\n\n\n/***/ }),\n\n/***/ \"./src/injected/textSelectorEngine.ts\":\n/*!********************************************!*\\\n !*** ./src/injected/textSelectorEngine.ts ***!\n \\********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TextEngine = {\n name: 'text',\n create(root, targetElement, type) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n for (let child = targetElement.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 3 /* Node.TEXT_NODE */) {\n const text = child.nodeValue;\n if (!text)\n continue;\n if (text.match(/^\\s*[a-zA-Z0-9]+\\s*$/) && exports.TextEngine.query(root, text.trim()) === targetElement)\n return text.trim();\n if (exports.TextEngine.query(root, JSON.stringify(text)) === targetElement)\n return JSON.stringify(text);\n }\n }\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n return element;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n result.push(element);\n }\n return result;\n }\n};\nfunction createMatcher(selector) {\n if (selector[0] === '\"' && selector[selector.length - 1] === '\"') {\n const parsed = JSON.parse(selector);\n return text => text === parsed;\n }\n if (selector[0] === '/' && selector.lastIndexOf('/') > 0) {\n const lastSlash = selector.lastIndexOf('/');\n const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));\n return text => re.test(text);\n }\n selector = selector.trim().toLowerCase();\n return text => text.trim().toLowerCase() === selector;\n}\n\n\n/***/ }),\n\n/***/ \"./src/injected/utils.ts\":\n/*!*******************************!*\\\n !*** ./src/injected/utils.ts ***!\n \\*******************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass Utils {\n parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE && element.parentNode.host)\n return element.parentNode.host;\n }\n deepElementFromPoint(document, x, y) {\n let container = document;\n let element;\n while (container) {\n const innerElement = container.elementFromPoint(x, y);\n if (!innerElement || element === innerElement)\n break;\n element = innerElement;\n container = element.shadowRoot;\n }\n return element;\n }\n}\nexports.Utils = Utils;\n\n\n/***/ }),\n\n/***/ \"./src/injected/xpathSelectorEngine.ts\":\n/*!*********************************************!*\\\n !*** ./src/injected/xpathSelectorEngine.ts ***!\n \\*********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst maxTextLength = 80;\nconst minMeaningfulSelectorLegth = 100;\nexports.XPathEngine = {\n name: 'xpath',\n create(root, targetElement, type) {\n const maybeDocument = root instanceof Document ? root : root.ownerDocument;\n if (!maybeDocument)\n return;\n const document = maybeDocument;\n const xpathCache = new Map();\n if (type === 'notext')\n return createNoText(root, targetElement);\n const tokens = [];\n function evaluateXPath(expression) {\n let nodes = xpathCache.get(expression);\n if (!nodes) {\n nodes = [];\n try {\n const result = document.evaluate(expression, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = result.iterateNext(); node; node = result.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n nodes.push(node);\n }\n }\n catch (e) {\n }\n xpathCache.set(expression, nodes);\n }\n return nodes;\n }\n function uniqueXPathSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n let selector = '//' + path.join('/');\n while (selector.includes('///'))\n selector = selector.replace('///', '//');\n if (selector.endsWith('/'))\n selector = selector.substring(0, selector.length - 1);\n const nodes = evaluateXPath(selector);\n if (nodes[nodes.length - 1] === targetElement)\n return selector;\n // If we are looking at a small set of elements with long selector, fall back to ordinal.\n if (nodes.length < 5 && selector.length > minMeaningfulSelectorLegth) {\n const index = nodes.indexOf(targetElement);\n if (index !== -1)\n return `(${selector})[${index + 1}]`;\n }\n return undefined;\n }\n function escapeAndCap(text) {\n text = text.substring(0, maxTextLength);\n // XPath 1.0 does not support quote escaping.\n // 1. If there are no single quotes - use them.\n if (text.indexOf(`'`) === -1)\n return `'${text}'`;\n // 2. If there are no double quotes - use them to enclose text.\n if (text.indexOf(`\"`) === -1)\n return `\"${text}\"`;\n // 3. Otherwise, use popular |concat| trick.\n const Q = `'`;\n return `concat(${text.split(Q).map(token => Q + token + Q).join(`, \"'\", `)})`;\n }\n const defaultAttributes = new Set(['title', 'aria-label', 'disabled', 'role']);\n const importantAttributes = new Map([\n ['form', ['action']],\n ['img', ['alt']],\n ['input', ['placeholder', 'type', 'name', 'value']],\n ]);\n let usedTextConditions = false;\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n const tag = nodeName === 'svg' ? '*' : nodeName;\n const tagConditions = [];\n if (nodeName === 'svg')\n tagConditions.push('local-name()=\"svg\"');\n const attrConditions = [];\n const importantAttrs = [...defaultAttributes, ...(importantAttributes.get(tag) || [])];\n for (const attr of importantAttrs) {\n const value = element.getAttribute(attr);\n if (value && value.length < maxTextLength)\n attrConditions.push(`normalize-space(@${attr})=${escapeAndCap(value)}`);\n else if (value)\n attrConditions.push(`starts-with(normalize-space(@${attr}), ${escapeAndCap(value)})`);\n }\n const text = document.evaluate('normalize-space(.)', element).stringValue;\n const textConditions = [];\n if (tag !== 'select' && text.length && !usedTextConditions) {\n if (text.length < maxTextLength)\n textConditions.push(`normalize-space(.)=${escapeAndCap(text)}`);\n else\n textConditions.push(`starts-with(normalize-space(.), ${escapeAndCap(text)})`);\n usedTextConditions = true;\n }\n // Always retain the last tag.\n const conditions = [...tagConditions, ...textConditions, ...attrConditions];\n const token = conditions.length ? `${tag}[${conditions.join(' and ')}]` : (tokens.length ? '' : tag);\n const selector = uniqueXPathSelector(token);\n if (selector)\n return selector;\n // Ordinal is the weakest signal.\n const parent = element.parentElement;\n let tagWithOrdinal = tag;\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n if (sameTagSiblings.length > 1)\n tagWithOrdinal += `[${1 + siblings.indexOf(element)}]`;\n }\n // Do not include text into this token, only tag / attributes.\n // Topmost node will get all the text.\n const nonTextConditions = [...tagConditions, ...attrConditions];\n const levelToken = nonTextConditions.length ? `${tagWithOrdinal}[${nonTextConditions.join(' and ')}]` : tokens.length ? '' : tagWithOrdinal;\n tokens.unshift(levelToken);\n }\n return uniqueXPathSelector();\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n return node;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n result.push(node);\n }\n return result;\n }\n};\nfunction createNoText(root, targetElement) {\n const steps = [];\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n if (element.getAttribute('id')) {\n steps.unshift(`//*[@id=\"${element.getAttribute('id')}\"]`);\n return steps.join('/');\n }\n const siblings = element.parentElement ? Array.from(element.parentElement.children) : [];\n const similarElements = siblings.filter(sibling => element.nodeName === sibling.nodeName);\n const index = similarElements.length === 1 ? 0 : similarElements.indexOf(element) + 1;\n steps.unshift(index ? `${element.nodeName}[${index}]` : element.nodeName);\n }\n return '/' + steps.join('/');\n}\n\n\n/***/ })\n\n/******/ })).default";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.source = "(/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./src/injected/injected.ts\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./src/injected/cssSelectorEngine.ts\":\n/*!*******************************************!*\\\n !*** ./src/injected/cssSelectorEngine.ts ***!\n \\*******************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.CSSEngine = {\n name: 'css',\n create(root, targetElement) {\n const tokens = [];\n function uniqueCSSSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n const selector = path.join(' > ');\n const nodes = Array.from(root.querySelectorAll(selector));\n return nodes[0] === targetElement ? selector : undefined;\n }\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n // Element ID is the strongest signal, use it.\n let bestTokenForLevel = '';\n if (element.id) {\n const token = /^[a-zA-Z][a-zA-Z0-9\\-\\_]+$/.test(element.id) ? '#' + element.id : `[id=\"${element.id}\"]`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n bestTokenForLevel = token;\n }\n const parent = element.parentElement;\n // Combine class names until unique.\n const classes = Array.from(element.classList);\n for (let i = 0; i < classes.length; ++i) {\n const token = '.' + classes.slice(0, i + 1).join('.');\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n // Even if not unique, does this subset of classes uniquely identify node as a child?\n if (!bestTokenForLevel && parent) {\n const sameClassSiblings = parent.querySelectorAll(token);\n if (sameClassSiblings.length === 1)\n bestTokenForLevel = token;\n }\n }\n // Ordinal is the weakest signal.\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n const token = sameTagSiblings.length === 1 ? nodeName : `${nodeName}:nth-child(${1 + siblings.indexOf(element)})`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n if (!bestTokenForLevel)\n bestTokenForLevel = token;\n }\n else if (!bestTokenForLevel) {\n bestTokenForLevel = nodeName;\n }\n tokens.unshift(bestTokenForLevel);\n }\n return uniqueCSSSelector();\n },\n query(root, selector) {\n return root.querySelector(selector) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(selector));\n }\n};\n\n\n/***/ }),\n\n/***/ \"./src/injected/injected.ts\":\n/*!**********************************!*\\\n !*** ./src/injected/injected.ts ***!\n \\**********************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst utils_1 = __webpack_require__(/*! ./utils */ \"./src/injected/utils.ts\");\nconst cssSelectorEngine_1 = __webpack_require__(/*! ./cssSelectorEngine */ \"./src/injected/cssSelectorEngine.ts\");\nconst xpathSelectorEngine_1 = __webpack_require__(/*! ./xpathSelectorEngine */ \"./src/injected/xpathSelectorEngine.ts\");\nconst textSelectorEngine_1 = __webpack_require__(/*! ./textSelectorEngine */ \"./src/injected/textSelectorEngine.ts\");\nfunction createAttributeEngine(attribute) {\n const engine = {\n name: attribute,\n create(root, target) {\n const value = target.getAttribute(attribute);\n if (!value)\n return;\n if (root.querySelector(`[${attribute}=${value}]`) === target)\n return value;\n },\n query(root, selector) {\n return root.querySelector(`[${attribute}=${selector}]`) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(`[${attribute}=${selector}]`));\n }\n };\n return engine;\n}\nclass Injected {\n constructor(customEngines) {\n const defaultEngines = [\n cssSelectorEngine_1.CSSEngine,\n xpathSelectorEngine_1.XPathEngine,\n textSelectorEngine_1.TextEngine,\n createAttributeEngine('id'),\n createAttributeEngine('data-testid'),\n createAttributeEngine('data-test-id'),\n createAttributeEngine('data-test'),\n ];\n this.utils = new utils_1.Utils();\n this.engines = new Map();\n for (const engine of [...defaultEngines, ...customEngines])\n this.engines.set(engine.name, engine);\n }\n querySelector(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelector'])\n throw new Error('Node is not queryable.');\n let element = root;\n for (const { engine, selector } of parsed) {\n const next = engine.query(element.shadowRoot || element, selector);\n if (!next)\n return;\n element = next;\n }\n return element;\n }\n querySelectorAll(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelectorAll'])\n throw new Error('Node is not queryable.');\n let set = new Set([root]);\n for (const { engine, selector } of parsed) {\n const newSet = new Set();\n for (const prev of set) {\n for (const next of engine.queryAll(prev.shadowRoot || prev, selector)) {\n if (newSet.has(next))\n continue;\n newSet.add(next);\n }\n }\n set = newSet;\n }\n return Array.from(set);\n }\n _parseSelector(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = [];\n const append = () => {\n const part = selector.substring(start, index);\n const eqIndex = part.indexOf('=');\n if (eqIndex === -1)\n throw new Error(`Cannot parse selector ${selector}`);\n const name = part.substring(0, eqIndex).trim();\n const body = part.substring(eqIndex + 1);\n const engine = this.engines.get(name.toLowerCase());\n if (!engine)\n throw new Error(`Unknown engine ${name} while parsing selector ${selector}`);\n result.push({ engine, selector: body });\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === '\\\\' && index + 1 < selector.length) {\n index += 2;\n }\n else if (c === quote) {\n quote = undefined;\n index++;\n }\n else if (!quote && c === '>' && selector[index + 1] === '>') {\n append();\n index += 2;\n start = index;\n }\n else {\n index++;\n }\n }\n append();\n return result;\n }\n isVisible(element) {\n if (!element.ownerDocument || !element.ownerDocument.defaultView)\n return true;\n const style = element.ownerDocument.defaultView.getComputedStyle(element);\n if (!style || style.visibility === 'hidden')\n return false;\n const rect = element.getBoundingClientRect();\n return !!(rect.top || rect.bottom || rect.width || rect.height);\n }\n pollMutation(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n return Promise.resolve(success);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const observer = new MutationObserver(() => {\n if (timedOut) {\n observer.disconnect();\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success) {\n observer.disconnect();\n fulfill(success);\n }\n });\n observer.observe(document, {\n childList: true,\n subtree: true,\n attributes: true\n });\n return result;\n }\n pollRaf(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onRaf = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n requestAnimationFrame(onRaf);\n };\n onRaf();\n return result;\n }\n pollInterval(selector, pollInterval, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onTimeout = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n setTimeout(onTimeout, pollInterval);\n };\n onTimeout();\n return result;\n }\n}\nexports.default = Injected;\n\n\n/***/ }),\n\n/***/ \"./src/injected/textSelectorEngine.ts\":\n/*!********************************************!*\\\n !*** ./src/injected/textSelectorEngine.ts ***!\n \\********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TextEngine = {\n name: 'text',\n create(root, targetElement, type) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n for (let child = targetElement.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 3 /* Node.TEXT_NODE */) {\n const text = child.nodeValue;\n if (!text)\n continue;\n if (text.match(/^\\s*[a-zA-Z0-9]+\\s*$/) && exports.TextEngine.query(root, text.trim()) === targetElement)\n return text.trim();\n if (exports.TextEngine.query(root, JSON.stringify(text)) === targetElement)\n return JSON.stringify(text);\n }\n }\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n return element;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n result.push(element);\n }\n return result;\n }\n};\nfunction createMatcher(selector) {\n if (selector[0] === '\"' && selector[selector.length - 1] === '\"') {\n const parsed = JSON.parse(selector);\n return text => text === parsed;\n }\n if (selector[0] === '/' && selector.lastIndexOf('/') > 0) {\n const lastSlash = selector.lastIndexOf('/');\n const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));\n return text => re.test(text);\n }\n selector = selector.trim();\n return text => text.trim() === selector;\n}\n\n\n/***/ }),\n\n/***/ \"./src/injected/utils.ts\":\n/*!*******************************!*\\\n !*** ./src/injected/utils.ts ***!\n \\*******************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass Utils {\n parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE && element.parentNode.host)\n return element.parentNode.host;\n }\n deepElementFromPoint(document, x, y) {\n let container = document;\n let element;\n while (container) {\n const innerElement = container.elementFromPoint(x, y);\n if (!innerElement || element === innerElement)\n break;\n element = innerElement;\n container = element.shadowRoot;\n }\n return element;\n }\n}\nexports.Utils = Utils;\n\n\n/***/ }),\n\n/***/ \"./src/injected/xpathSelectorEngine.ts\":\n/*!*********************************************!*\\\n !*** ./src/injected/xpathSelectorEngine.ts ***!\n \\*********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst maxTextLength = 80;\nconst minMeaningfulSelectorLegth = 100;\nexports.XPathEngine = {\n name: 'xpath',\n create(root, targetElement, type) {\n const maybeDocument = root instanceof Document ? root : root.ownerDocument;\n if (!maybeDocument)\n return;\n const document = maybeDocument;\n const xpathCache = new Map();\n if (type === 'notext')\n return createNoText(root, targetElement);\n const tokens = [];\n function evaluateXPath(expression) {\n let nodes = xpathCache.get(expression);\n if (!nodes) {\n nodes = [];\n try {\n const result = document.evaluate(expression, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = result.iterateNext(); node; node = result.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n nodes.push(node);\n }\n }\n catch (e) {\n }\n xpathCache.set(expression, nodes);\n }\n return nodes;\n }\n function uniqueXPathSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n let selector = '//' + path.join('/');\n while (selector.includes('///'))\n selector = selector.replace('///', '//');\n if (selector.endsWith('/'))\n selector = selector.substring(0, selector.length - 1);\n const nodes = evaluateXPath(selector);\n if (nodes[nodes.length - 1] === targetElement)\n return selector;\n // If we are looking at a small set of elements with long selector, fall back to ordinal.\n if (nodes.length < 5 && selector.length > minMeaningfulSelectorLegth) {\n const index = nodes.indexOf(targetElement);\n if (index !== -1)\n return `(${selector})[${index + 1}]`;\n }\n return undefined;\n }\n function escapeAndCap(text) {\n text = text.substring(0, maxTextLength);\n // XPath 1.0 does not support quote escaping.\n // 1. If there are no single quotes - use them.\n if (text.indexOf(`'`) === -1)\n return `'${text}'`;\n // 2. If there are no double quotes - use them to enclose text.\n if (text.indexOf(`\"`) === -1)\n return `\"${text}\"`;\n // 3. Otherwise, use popular |concat| trick.\n const Q = `'`;\n return `concat(${text.split(Q).map(token => Q + token + Q).join(`, \"'\", `)})`;\n }\n const defaultAttributes = new Set(['title', 'aria-label', 'disabled', 'role']);\n const importantAttributes = new Map([\n ['form', ['action']],\n ['img', ['alt']],\n ['input', ['placeholder', 'type', 'name', 'value']],\n ]);\n let usedTextConditions = false;\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n const tag = nodeName === 'svg' ? '*' : nodeName;\n const tagConditions = [];\n if (nodeName === 'svg')\n tagConditions.push('local-name()=\"svg\"');\n const attrConditions = [];\n const importantAttrs = [...defaultAttributes, ...(importantAttributes.get(tag) || [])];\n for (const attr of importantAttrs) {\n const value = element.getAttribute(attr);\n if (value && value.length < maxTextLength)\n attrConditions.push(`normalize-space(@${attr})=${escapeAndCap(value)}`);\n else if (value)\n attrConditions.push(`starts-with(normalize-space(@${attr}), ${escapeAndCap(value)})`);\n }\n const text = document.evaluate('normalize-space(.)', element).stringValue;\n const textConditions = [];\n if (tag !== 'select' && text.length && !usedTextConditions) {\n if (text.length < maxTextLength)\n textConditions.push(`normalize-space(.)=${escapeAndCap(text)}`);\n else\n textConditions.push(`starts-with(normalize-space(.), ${escapeAndCap(text)})`);\n usedTextConditions = true;\n }\n // Always retain the last tag.\n const conditions = [...tagConditions, ...textConditions, ...attrConditions];\n const token = conditions.length ? `${tag}[${conditions.join(' and ')}]` : (tokens.length ? '' : tag);\n const selector = uniqueXPathSelector(token);\n if (selector)\n return selector;\n // Ordinal is the weakest signal.\n const parent = element.parentElement;\n let tagWithOrdinal = tag;\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n if (sameTagSiblings.length > 1)\n tagWithOrdinal += `[${1 + siblings.indexOf(element)}]`;\n }\n // Do not include text into this token, only tag / attributes.\n // Topmost node will get all the text.\n const nonTextConditions = [...tagConditions, ...attrConditions];\n const levelToken = nonTextConditions.length ? `${tagWithOrdinal}[${nonTextConditions.join(' and ')}]` : tokens.length ? '' : tagWithOrdinal;\n tokens.unshift(levelToken);\n }\n return uniqueXPathSelector();\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n return node;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n result.push(node);\n }\n return result;\n }\n};\nfunction createNoText(root, targetElement) {\n const steps = [];\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n if (element.getAttribute('id')) {\n steps.unshift(`//*[@id=\"${element.getAttribute('id')}\"]`);\n return steps.join('/');\n }\n const siblings = element.parentElement ? Array.from(element.parentElement.children) : [];\n const similarElements = siblings.filter(sibling => element.nodeName === sibling.nodeName);\n const index = similarElements.length === 1 ? 0 : similarElements.indexOf(element) + 1;\n steps.unshift(index ? `${element.nodeName}[${index}]` : element.nodeName);\n }\n return '/' + steps.join('/');\n}\n\n\n/***/ })\n\n/******/ })).default";
exports.source = "(/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./src/injected/injected.ts\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./src/injected/cssSelectorEngine.ts\":\n/*!*******************************************!*\\\n !*** ./src/injected/cssSelectorEngine.ts ***!\n \\*******************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.CSSEngine = {\n name: 'css',\n create(root, targetElement) {\n const tokens = [];\n function uniqueCSSSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n const selector = path.join(' > ');\n const nodes = Array.from(root.querySelectorAll(selector));\n return nodes[0] === targetElement ? selector : undefined;\n }\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n // Element ID is the strongest signal, use it.\n let bestTokenForLevel = '';\n if (element.id) {\n const token = /^[a-zA-Z][a-zA-Z0-9\\-\\_]+$/.test(element.id) ? '#' + element.id : `[id=\"${element.id}\"]`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n bestTokenForLevel = token;\n }\n const parent = element.parentElement;\n // Combine class names until unique.\n const classes = Array.from(element.classList);\n for (let i = 0; i < classes.length; ++i) {\n const token = '.' + classes.slice(0, i + 1).join('.');\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n // Even if not unique, does this subset of classes uniquely identify node as a child?\n if (!bestTokenForLevel && parent) {\n const sameClassSiblings = parent.querySelectorAll(token);\n if (sameClassSiblings.length === 1)\n bestTokenForLevel = token;\n }\n }\n // Ordinal is the weakest signal.\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n const token = sameTagSiblings.length === 1 ? nodeName : `${nodeName}:nth-child(${1 + siblings.indexOf(element)})`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return selector;\n if (!bestTokenForLevel)\n bestTokenForLevel = token;\n }\n else if (!bestTokenForLevel) {\n bestTokenForLevel = nodeName;\n }\n tokens.unshift(bestTokenForLevel);\n }\n return uniqueCSSSelector();\n },\n query(root, selector) {\n return root.querySelector(selector) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(selector));\n }\n};\n\n\n/***/ }),\n\n/***/ \"./src/injected/injected.ts\":\n/*!**********************************!*\\\n !*** ./src/injected/injected.ts ***!\n \\**********************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst utils_1 = __webpack_require__(/*! ./utils */ \"./src/injected/utils.ts\");\nconst cssSelectorEngine_1 = __webpack_require__(/*! ./cssSelectorEngine */ \"./src/injected/cssSelectorEngine.ts\");\nconst xpathSelectorEngine_1 = __webpack_require__(/*! ./xpathSelectorEngine */ \"./src/injected/xpathSelectorEngine.ts\");\nconst textSelectorEngine_1 = __webpack_require__(/*! ./textSelectorEngine */ \"./src/injected/textSelectorEngine.ts\");\nfunction createAttributeEngine(attribute) {\n const engine = {\n name: attribute,\n create(root, target) {\n const value = target.getAttribute(attribute);\n if (!value)\n return;\n if (root.querySelector(`[${attribute}=${value}]`) === target)\n return value;\n },\n query(root, selector) {\n return root.querySelector(`[${attribute}=${selector}]`) || undefined;\n },\n queryAll(root, selector) {\n return Array.from(root.querySelectorAll(`[${attribute}=${selector}]`));\n }\n };\n return engine;\n}\nclass Injected {\n constructor(customEngines) {\n const defaultEngines = [\n cssSelectorEngine_1.CSSEngine,\n xpathSelectorEngine_1.XPathEngine,\n textSelectorEngine_1.TextEngine,\n createAttributeEngine('id'),\n createAttributeEngine('data-testid'),\n createAttributeEngine('data-test-id'),\n createAttributeEngine('data-test'),\n ];\n this.utils = new utils_1.Utils();\n this.engines = new Map();\n for (const engine of [...defaultEngines, ...customEngines])\n this.engines.set(engine.name, engine);\n }\n querySelector(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelector'])\n throw new Error('Node is not queryable.');\n let element = root;\n for (const { engine, selector } of parsed) {\n const next = engine.query(element.shadowRoot || element, selector);\n if (!next)\n return;\n element = next;\n }\n return element;\n }\n querySelectorAll(selector, root) {\n const parsed = this._parseSelector(selector);\n if (!root['querySelectorAll'])\n throw new Error('Node is not queryable.');\n let set = new Set([root]);\n for (const { engine, selector } of parsed) {\n const newSet = new Set();\n for (const prev of set) {\n for (const next of engine.queryAll(prev.shadowRoot || prev, selector)) {\n if (newSet.has(next))\n continue;\n newSet.add(next);\n }\n }\n set = newSet;\n }\n return Array.from(set);\n }\n _parseSelector(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = [];\n const append = () => {\n const part = selector.substring(start, index);\n const eqIndex = part.indexOf('=');\n if (eqIndex === -1)\n throw new Error(`Cannot parse selector ${selector}`);\n const name = part.substring(0, eqIndex).trim();\n const body = part.substring(eqIndex + 1);\n const engine = this.engines.get(name.toLowerCase());\n if (!engine)\n throw new Error(`Unknown engine ${name} while parsing selector ${selector}`);\n result.push({ engine, selector: body });\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === '\\\\' && index + 1 < selector.length) {\n index += 2;\n }\n else if (c === quote) {\n quote = undefined;\n index++;\n }\n else if (!quote && c === '>' && selector[index + 1] === '>') {\n append();\n index += 2;\n start = index;\n }\n else {\n index++;\n }\n }\n append();\n return result;\n }\n isVisible(element) {\n if (!element.ownerDocument || !element.ownerDocument.defaultView)\n return true;\n const style = element.ownerDocument.defaultView.getComputedStyle(element);\n if (!style || style.visibility === 'hidden')\n return false;\n const rect = element.getBoundingClientRect();\n return !!(rect.top || rect.bottom || rect.width || rect.height);\n }\n _pollMutation(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n return Promise.resolve(success);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const observer = new MutationObserver(() => {\n if (timedOut) {\n observer.disconnect();\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success) {\n observer.disconnect();\n fulfill(success);\n }\n });\n observer.observe(document, {\n childList: true,\n subtree: true,\n attributes: true\n });\n return result;\n }\n _pollRaf(selector, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onRaf = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n requestAnimationFrame(onRaf);\n };\n onRaf();\n return result;\n }\n _pollInterval(selector, pollInterval, predicate, timeout) {\n let timedOut = false;\n if (timeout)\n setTimeout(() => timedOut = true, timeout);\n let fulfill;\n const result = new Promise(x => fulfill = x);\n const onTimeout = () => {\n if (timedOut) {\n fulfill();\n return;\n }\n const element = selector === undefined ? undefined : this.querySelector(selector, document);\n const success = predicate(element);\n if (success)\n fulfill(success);\n else\n setTimeout(onTimeout, pollInterval);\n };\n onTimeout();\n return result;\n }\n poll(polling, selector, timeout, predicate) {\n if (polling === 'raf')\n return this._pollRaf(selector, predicate, timeout);\n if (polling === 'mutation')\n return this._pollMutation(selector, predicate, timeout);\n return this._pollInterval(selector, polling, predicate, timeout);\n }\n getElementBorderWidth(node) {\n if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)\n return { left: 0, top: 0 };\n const style = node.ownerDocument.defaultView.getComputedStyle(node);\n return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };\n }\n selectOptions(node, optionsToSelect) {\n if (node.nodeName.toLowerCase() !== 'select')\n throw new Error('Element is not a <select> element.');\n const element = node;\n const options = Array.from(element.options);\n element.value = undefined;\n for (let index = 0; index < options.length; index++) {\n const option = options[index];\n option.selected = optionsToSelect.some(optionToSelect => {\n if (optionToSelect instanceof Node)\n return option === optionToSelect;\n let matches = true;\n if (optionToSelect.value !== undefined)\n matches = matches && optionToSelect.value === option.value;\n if (optionToSelect.label !== undefined)\n matches = matches && optionToSelect.label === option.label;\n if (optionToSelect.index !== undefined)\n matches = matches && optionToSelect.index === index;\n return matches;\n });\n if (option.selected && !element.multiple)\n break;\n }\n element.dispatchEvent(new Event('input', { 'bubbles': true }));\n element.dispatchEvent(new Event('change', { 'bubbles': true }));\n return options.filter(option => option.selected).map(option => option.value);\n }\n fill(node, value) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n return 'Node is not of type HTMLElement';\n const element = node;\n if (!element.isConnected)\n return 'Element is not attached to the DOM';\n if (!element.ownerDocument || !element.ownerDocument.defaultView)\n return 'Element does not belong to a window';\n const style = element.ownerDocument.defaultView.getComputedStyle(element);\n if (!style || style.visibility === 'hidden')\n return 'Element is hidden';\n if (!element.offsetParent && element.tagName !== 'BODY')\n return 'Element is not visible';\n if (element.nodeName.toLowerCase() === 'input') {\n const input = element;\n const type = input.getAttribute('type') || '';\n const kTextInputTypes = new Set(['', 'email', 'number', 'password', 'search', 'tel', 'text', 'url']);\n if (!kTextInputTypes.has(type.toLowerCase()))\n return 'Cannot fill input of type \"' + type + '\".';\n if (type.toLowerCase() === 'number') {\n value = value.trim();\n if (!value || isNaN(Number(value)))\n return 'Cannot type text into input[type=number].';\n }\n if (input.disabled)\n return 'Cannot fill a disabled input.';\n if (input.readOnly)\n return 'Cannot fill a readonly input.';\n input.select();\n input.focus();\n }\n else if (element.nodeName.toLowerCase() === 'textarea') {\n const textarea = element;\n if (textarea.disabled)\n return 'Cannot fill a disabled textarea.';\n if (textarea.readOnly)\n return 'Cannot fill a readonly textarea.';\n textarea.selectionStart = 0;\n textarea.selectionEnd = textarea.value.length;\n textarea.focus();\n }\n else if (element.isContentEditable) {\n const range = element.ownerDocument.createRange();\n range.selectNodeContents(element);\n const selection = element.ownerDocument.defaultView.getSelection();\n if (!selection)\n return 'Element belongs to invisible iframe.';\n selection.removeAllRanges();\n selection.addRange(range);\n element.focus();\n }\n else {\n return 'Element is not an <input>, <textarea> or [contenteditable] element.';\n }\n return false;\n }\n isCheckboxChecked(node) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw new Error('Not a checkbox or radio button');\n let element = node;\n if (element.getAttribute('role') === 'checkbox')\n return element.getAttribute('aria-checked') === 'true';\n if (element.nodeName === 'LABEL') {\n const forId = element.getAttribute('for');\n if (forId && element.ownerDocument)\n element = element.ownerDocument.querySelector(`input[id=\"${forId}\"]`) || undefined;\n else\n element = element.querySelector('input[type=checkbox],input[type=radio]') || undefined;\n }\n if (element && element.nodeName === 'INPUT') {\n const type = element.getAttribute('type');\n if (type && (type.toLowerCase() === 'checkbox' || type.toLowerCase() === 'radio'))\n return element.checked;\n }\n throw new Error('Not a checkbox');\n }\n waitForStablePosition(node, timeout) {\n if (!node.isConnected)\n throw new Error('Element is not attached to the DOM');\n const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n if (!element)\n throw new Error('Element is not attached to the DOM');\n let lastRect;\n let counter = 0;\n return this.poll('raf', undefined, timeout, () => {\n // First raf happens in the same animation frame as evaluation, so it does not produce\n // any client rect difference compared to synchronous call. We skip the synchronous call\n // and only force layout during actual rafs as a small optimisation.\n if (++counter === 1)\n return false;\n const clientRect = element.getBoundingClientRect();\n const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };\n const isStable = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;\n lastRect = rect;\n return isStable;\n });\n }\n waitForHitTargetAt(node, timeout, point) {\n const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n if (!element)\n throw new Error('Element is not attached to the DOM');\n return this.poll('raf', undefined, timeout, () => {\n let hitElement = this.utils.deepElementFromPoint(document, point.x, point.y);\n while (hitElement && hitElement !== element)\n hitElement = this.utils.parentElementOrShadowHost(hitElement);\n return hitElement === element;\n });\n }\n}\nexports.default = Injected;\n\n\n/***/ }),\n\n/***/ \"./src/injected/textSelectorEngine.ts\":\n/*!********************************************!*\\\n !*** ./src/injected/textSelectorEngine.ts ***!\n \\********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.TextEngine = {\n name: 'text',\n create(root, targetElement, type) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n for (let child = targetElement.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 3 /* Node.TEXT_NODE */) {\n const text = child.nodeValue;\n if (!text)\n continue;\n if (text.match(/^\\s*[a-zA-Z0-9]+\\s*$/) && exports.TextEngine.query(root, text.trim()) === targetElement)\n return text.trim();\n if (exports.TextEngine.query(root, JSON.stringify(text)) === targetElement)\n return JSON.stringify(text);\n }\n }\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n return element;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const matcher = createMatcher(selector);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const element = node.parentElement;\n const text = node.nodeValue;\n if (element && text && matcher(text))\n result.push(element);\n }\n return result;\n }\n};\nfunction createMatcher(selector) {\n if (selector[0] === '\"' && selector[selector.length - 1] === '\"') {\n const parsed = JSON.parse(selector);\n return text => text === parsed;\n }\n if (selector[0] === '/' && selector.lastIndexOf('/') > 0) {\n const lastSlash = selector.lastIndexOf('/');\n const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));\n return text => re.test(text);\n }\n selector = selector.trim().toLowerCase();\n return text => text.trim().toLowerCase() === selector;\n}\n\n\n/***/ }),\n\n/***/ \"./src/injected/utils.ts\":\n/*!*******************************!*\\\n !*** ./src/injected/utils.ts ***!\n \\*******************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nclass Utils {\n parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE && element.parentNode.host)\n return element.parentNode.host;\n }\n deepElementFromPoint(document, x, y) {\n let container = document;\n let element;\n while (container) {\n const innerElement = container.elementFromPoint(x, y);\n if (!innerElement || element === innerElement)\n break;\n element = innerElement;\n container = element.shadowRoot;\n }\n return element;\n }\n}\nexports.Utils = Utils;\n\n\n/***/ }),\n\n/***/ \"./src/injected/xpathSelectorEngine.ts\":\n/*!*********************************************!*\\\n !*** ./src/injected/xpathSelectorEngine.ts ***!\n \\*********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n/**\n * Copyright (c) Microsoft Corporation.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst maxTextLength = 80;\nconst minMeaningfulSelectorLegth = 100;\nexports.XPathEngine = {\n name: 'xpath',\n create(root, targetElement, type) {\n const maybeDocument = root instanceof Document ? root : root.ownerDocument;\n if (!maybeDocument)\n return;\n const document = maybeDocument;\n const xpathCache = new Map();\n if (type === 'notext')\n return createNoText(root, targetElement);\n const tokens = [];\n function evaluateXPath(expression) {\n let nodes = xpathCache.get(expression);\n if (!nodes) {\n nodes = [];\n try {\n const result = document.evaluate(expression, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = result.iterateNext(); node; node = result.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n nodes.push(node);\n }\n }\n catch (e) {\n }\n xpathCache.set(expression, nodes);\n }\n return nodes;\n }\n function uniqueXPathSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n let selector = '//' + path.join('/');\n while (selector.includes('///'))\n selector = selector.replace('///', '//');\n if (selector.endsWith('/'))\n selector = selector.substring(0, selector.length - 1);\n const nodes = evaluateXPath(selector);\n if (nodes[nodes.length - 1] === targetElement)\n return selector;\n // If we are looking at a small set of elements with long selector, fall back to ordinal.\n if (nodes.length < 5 && selector.length > minMeaningfulSelectorLegth) {\n const index = nodes.indexOf(targetElement);\n if (index !== -1)\n return `(${selector})[${index + 1}]`;\n }\n return undefined;\n }\n function escapeAndCap(text) {\n text = text.substring(0, maxTextLength);\n // XPath 1.0 does not support quote escaping.\n // 1. If there are no single quotes - use them.\n if (text.indexOf(`'`) === -1)\n return `'${text}'`;\n // 2. If there are no double quotes - use them to enclose text.\n if (text.indexOf(`\"`) === -1)\n return `\"${text}\"`;\n // 3. Otherwise, use popular |concat| trick.\n const Q = `'`;\n return `concat(${text.split(Q).map(token => Q + token + Q).join(`, \"'\", `)})`;\n }\n const defaultAttributes = new Set(['title', 'aria-label', 'disabled', 'role']);\n const importantAttributes = new Map([\n ['form', ['action']],\n ['img', ['alt']],\n ['input', ['placeholder', 'type', 'name', 'value']],\n ]);\n let usedTextConditions = false;\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n const nodeName = element.nodeName.toLowerCase();\n const tag = nodeName === 'svg' ? '*' : nodeName;\n const tagConditions = [];\n if (nodeName === 'svg')\n tagConditions.push('local-name()=\"svg\"');\n const attrConditions = [];\n const importantAttrs = [...defaultAttributes, ...(importantAttributes.get(tag) || [])];\n for (const attr of importantAttrs) {\n const value = element.getAttribute(attr);\n if (value && value.length < maxTextLength)\n attrConditions.push(`normalize-space(@${attr})=${escapeAndCap(value)}`);\n else if (value)\n attrConditions.push(`starts-with(normalize-space(@${attr}), ${escapeAndCap(value)})`);\n }\n const text = document.evaluate('normalize-space(.)', element).stringValue;\n const textConditions = [];\n if (tag !== 'select' && text.length && !usedTextConditions) {\n if (text.length < maxTextLength)\n textConditions.push(`normalize-space(.)=${escapeAndCap(text)}`);\n else\n textConditions.push(`starts-with(normalize-space(.), ${escapeAndCap(text)})`);\n usedTextConditions = true;\n }\n // Always retain the last tag.\n const conditions = [...tagConditions, ...textConditions, ...attrConditions];\n const token = conditions.length ? `${tag}[${conditions.join(' and ')}]` : (tokens.length ? '' : tag);\n const selector = uniqueXPathSelector(token);\n if (selector)\n return selector;\n // Ordinal is the weakest signal.\n const parent = element.parentElement;\n let tagWithOrdinal = tag;\n if (parent) {\n const siblings = Array.from(parent.children);\n const sameTagSiblings = siblings.filter(sibling => (sibling).nodeName.toLowerCase() === nodeName);\n if (sameTagSiblings.length > 1)\n tagWithOrdinal += `[${1 + siblings.indexOf(element)}]`;\n }\n // Do not include text into this token, only tag / attributes.\n // Topmost node will get all the text.\n const nonTextConditions = [...tagConditions, ...attrConditions];\n const levelToken = nonTextConditions.length ? `${tagWithOrdinal}[${nonTextConditions.join(' and ')}]` : tokens.length ? '' : tagWithOrdinal;\n tokens.unshift(levelToken);\n }\n return uniqueXPathSelector();\n },\n query(root, selector) {\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n return node;\n }\n },\n queryAll(root, selector) {\n const result = [];\n const document = root instanceof Document ? root : root.ownerDocument;\n if (!document)\n return result;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n result.push(node);\n }\n return result;\n }\n};\nfunction createNoText(root, targetElement) {\n const steps = [];\n for (let element = targetElement; element && element !== root; element = element.parentElement) {\n if (element.getAttribute('id')) {\n steps.unshift(`//*[@id=\"${element.getAttribute('id')}\"]`);\n return steps.join('/');\n }\n const siblings = element.parentElement ? Array.from(element.parentElement.children) : [];\n const similarElements = siblings.filter(sibling => element.nodeName === sibling.nodeName);\n const index = similarElements.length === 1 ? 0 : similarElements.indexOf(element) + 1;\n steps.unshift(index ? `${element.nodeName}[${index}]` : element.nodeName);\n }\n return '/' + steps.join('/');\n}\n\n\n/***/ })\n\n/******/ })).default";
//# sourceMappingURL=injectedSource.js.map

@@ -27,2 +27,6 @@ /**

static evaluationString(fun: Function | string, ...args: any[]): string;
static evaluationScript(fun: Function | string | {
path?: string;
content?: string;
}, ...args: any[]): Promise<string>;
static installApiHooks(className: string, classType: any): void;

@@ -37,2 +41,5 @@ static addEventListener(emitter: platform.EventEmitterType, eventName: (string | symbol), handler: (...args: any[]) => void): RegisteredListener;

static isNumber(obj: any): obj is number;
static isRegExp(obj: any): obj is RegExp;
static isObject(obj: any): obj is NonNullable<object>;
static isBoolean(obj: any): obj is boolean;
static waitForEvent(emitter: platform.EventEmitterType, eventName: (string | symbol), predicate: Function, timeout: number, abortPromise: Promise<Error>): Promise<any>;

@@ -42,2 +49,3 @@ static waitWithTimeout<T>(promise: Promise<T>, taskName: string, timeout: number): Promise<T>;

static completeUserURL(urlString: string): string;
static trimMiddle(string: string, maxLength: number): string;
}

@@ -44,0 +52,0 @@ export declare function assert(value: any, message?: string): asserts value;

@@ -35,2 +35,18 @@ "use strict";

}
static async evaluationScript(fun, ...args) {
if (!exports.helper.isString(fun) && typeof fun !== 'function') {
if (fun.content !== undefined) {
fun = fun.content;
}
else if (fun.path !== undefined) {
let contents = await platform.readFileAsync(fun.path, 'utf8');
contents += '//# sourceURL=' + fun.path.replace(/\n/g, '');
fun = contents;
}
else {
throw new Error('Either path or content property must be present');
}
}
return exports.helper.evaluationString(fun, ...args);
}
static installApiHooks(className, classType) {

@@ -46,12 +62,24 @@ const log = platform.debug('pw:api');

Reflect.set(classType.prototype, methodName, function (...args) {
const syncStack = {};
Error.captureStackTrace(syncStack);
if (log.enabled) {
if (args.length)
log(`${className}.${methodName} %o`, args);
else
log(`${className}.${methodName}`);
const frames = syncStack.stack.substring('Error\n'.length)
.split('\n')
.map((f) => f.replace(/\s+at\s/, '').trim());
const userCall = frames.length <= 1 || !frames[1].includes('playwright/lib');
if (userCall) {
const match = /([^/\\]+)(:\d+:\d+)[)]?$/.exec(frames[1]);
let location = '';
if (match) {
const fileName = exports.helper.trimMiddle(match[1], 20 - match[2].length);
location = `\u001b[33m[${fileName}${match[2]}]\u001b[39m `;
}
if (args.length)
log(`${location}${className}.${methodName} %o`, args);
else
log(`${location}${className}.${methodName}`);
}
}
if (!isAsync)
return method.call(this, ...args);
const syncStack = {};
Error.captureStackTrace(syncStack);
return method.call(this, ...args).catch((e) => {

@@ -82,2 +110,11 @@ const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1);

}
static isRegExp(obj) {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
}
static isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
static isBoolean(obj) {
return typeof obj === 'boolean' || obj instanceof Boolean;
}
static async waitForEvent(emitter, eventName, predicate, timeout, abortPromise) {

@@ -196,2 +233,9 @@ let eventTimeout;

}
static trimMiddle(string, maxLength) {
if (string.length <= maxLength)
return string;
const leftHalf = maxLength >> 1;
const rightHalf = maxLength - leftHalf - 1;
return string.substr(0, leftHalf) + '\u2026' + string.substr(this.length - rightHalf, rightHalf);
}
}

@@ -198,0 +242,0 @@ function assert(value, message) {

@@ -18,2 +18,3 @@ /**

import { Utils } from './utils';
import * as types from '../types';
declare type Predicate = (element: Element | undefined) => any;

@@ -28,6 +29,16 @@ declare class Injected {

isVisible(element: Element): boolean;
pollMutation(selector: string | undefined, predicate: Predicate, timeout: number): Promise<any>;
pollRaf(selector: string | undefined, predicate: Predicate, timeout: number): Promise<any>;
pollInterval(selector: string | undefined, pollInterval: number, predicate: Predicate, timeout: number): Promise<any>;
private _pollMutation;
private _pollRaf;
private _pollInterval;
poll(polling: 'raf' | 'mutation' | number, selector: string | undefined, timeout: number, predicate: Predicate): Promise<any>;
getElementBorderWidth(node: Node): {
left: number;
top: number;
};
selectOptions(node: Node, optionsToSelect: (Node | types.SelectOption)[]): string[];
fill(node: Node, value: string): string | false;
isCheckboxChecked(node: Node): boolean;
waitForStablePosition(node: Node, timeout: number): Promise<any>;
waitForHitTargetAt(node: Node, timeout: number, point: types.Point): Promise<any>;
}
export default Injected;

@@ -135,3 +135,3 @@ "use strict";

}
pollMutation(selector, predicate, timeout) {
_pollMutation(selector, predicate, timeout) {
let timedOut = false;

@@ -166,3 +166,3 @@ if (timeout)

}
pollRaf(selector, predicate, timeout) {
_pollRaf(selector, predicate, timeout) {
let timedOut = false;

@@ -188,3 +188,3 @@ if (timeout)

}
pollInterval(selector, pollInterval, predicate, timeout) {
_pollInterval(selector, pollInterval, predicate, timeout) {
let timedOut = false;

@@ -210,4 +210,152 @@ if (timeout)

}
poll(polling, selector, timeout, predicate) {
if (polling === 'raf')
return this._pollRaf(selector, predicate, timeout);
if (polling === 'mutation')
return this._pollMutation(selector, predicate, timeout);
return this._pollInterval(selector, polling, predicate, timeout);
}
getElementBorderWidth(node) {
if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)
return { left: 0, top: 0 };
const style = node.ownerDocument.defaultView.getComputedStyle(node);
return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };
}
selectOptions(node, optionsToSelect) {
if (node.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a <select> element.');
const element = node;
const options = Array.from(element.options);
element.value = undefined;
for (let index = 0; index < options.length; index++) {
const option = options[index];
option.selected = optionsToSelect.some(optionToSelect => {
if (optionToSelect instanceof Node)
return option === optionToSelect;
let matches = true;
if (optionToSelect.value !== undefined)
matches = matches && optionToSelect.value === option.value;
if (optionToSelect.label !== undefined)
matches = matches && optionToSelect.label === option.label;
if (optionToSelect.index !== undefined)
matches = matches && optionToSelect.index === index;
return matches;
});
if (option.selected && !element.multiple)
break;
}
element.dispatchEvent(new Event('input', { 'bubbles': true }));
element.dispatchEvent(new Event('change', { 'bubbles': true }));
return options.filter(option => option.selected).map(option => option.value);
}
fill(node, value) {
if (node.nodeType !== Node.ELEMENT_NODE)
return 'Node is not of type HTMLElement';
const element = node;
if (!element.isConnected)
return 'Element is not attached to the DOM';
if (!element.ownerDocument || !element.ownerDocument.defaultView)
return 'Element does not belong to a window';
const style = element.ownerDocument.defaultView.getComputedStyle(element);
if (!style || style.visibility === 'hidden')
return 'Element is hidden';
if (!element.offsetParent && element.tagName !== 'BODY')
return 'Element is not visible';
if (element.nodeName.toLowerCase() === 'input') {
const input = element;
const type = input.getAttribute('type') || '';
const kTextInputTypes = new Set(['', 'email', 'number', 'password', 'search', 'tel', 'text', 'url']);
if (!kTextInputTypes.has(type.toLowerCase()))
return 'Cannot fill input of type "' + type + '".';
if (type.toLowerCase() === 'number') {
value = value.trim();
if (!value || isNaN(Number(value)))
return 'Cannot type text into input[type=number].';
}
if (input.disabled)
return 'Cannot fill a disabled input.';
if (input.readOnly)
return 'Cannot fill a readonly input.';
input.select();
input.focus();
}
else if (element.nodeName.toLowerCase() === 'textarea') {
const textarea = element;
if (textarea.disabled)
return 'Cannot fill a disabled textarea.';
if (textarea.readOnly)
return 'Cannot fill a readonly textarea.';
textarea.selectionStart = 0;
textarea.selectionEnd = textarea.value.length;
textarea.focus();
}
else if (element.isContentEditable) {
const range = element.ownerDocument.createRange();
range.selectNodeContents(element);
const selection = element.ownerDocument.defaultView.getSelection();
if (!selection)
return 'Element belongs to invisible iframe.';
selection.removeAllRanges();
selection.addRange(range);
element.focus();
}
else {
return 'Element is not an <input>, <textarea> or [contenteditable] element.';
}
return false;
}
isCheckboxChecked(node) {
if (node.nodeType !== Node.ELEMENT_NODE)
throw new Error('Not a checkbox or radio button');
let element = node;
if (element.getAttribute('role') === 'checkbox')
return element.getAttribute('aria-checked') === 'true';
if (element.nodeName === 'LABEL') {
const forId = element.getAttribute('for');
if (forId && element.ownerDocument)
element = element.ownerDocument.querySelector(`input[id="${forId}"]`) || undefined;
else
element = element.querySelector('input[type=checkbox],input[type=radio]') || undefined;
}
if (element && element.nodeName === 'INPUT') {
const type = element.getAttribute('type');
if (type && (type.toLowerCase() === 'checkbox' || type.toLowerCase() === 'radio'))
return element.checked;
}
throw new Error('Not a checkbox');
}
waitForStablePosition(node, timeout) {
if (!node.isConnected)
throw new Error('Element is not attached to the DOM');
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
if (!element)
throw new Error('Element is not attached to the DOM');
let lastRect;
let counter = 0;
return this.poll('raf', undefined, timeout, () => {
// First raf happens in the same animation frame as evaluation, so it does not produce
// any client rect difference compared to synchronous call. We skip the synchronous call
// and only force layout during actual rafs as a small optimisation.
if (++counter === 1)
return false;
const clientRect = element.getBoundingClientRect();
const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };
const isStable = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;
lastRect = rect;
return isStable;
});
}
waitForHitTargetAt(node, timeout, point) {
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
if (!element)
throw new Error('Element is not attached to the DOM');
return this.poll('raf', undefined, timeout, () => {
let hitElement = this.utils.deepElementFromPoint(document, point.x, point.y);
while (hitElement && hitElement !== element)
hitElement = this.utils.parentElementOrShadowHost(hitElement);
return hitElement === element;
});
}
}
exports.default = Injected;
//# sourceMappingURL=injected.js.map

@@ -323,3 +323,3 @@ /******/ (function(modules) { // webpackBootstrap

}
pollMutation(selector, predicate, timeout) {
_pollMutation(selector, predicate, timeout) {
let timedOut = false;

@@ -354,3 +354,3 @@ if (timeout)

}
pollRaf(selector, predicate, timeout) {
_pollRaf(selector, predicate, timeout) {
let timedOut = false;

@@ -376,3 +376,3 @@ if (timeout)

}
pollInterval(selector, pollInterval, predicate, timeout) {
_pollInterval(selector, pollInterval, predicate, timeout) {
let timedOut = false;

@@ -398,2 +398,150 @@ if (timeout)

}
poll(polling, selector, timeout, predicate) {
if (polling === 'raf')
return this._pollRaf(selector, predicate, timeout);
if (polling === 'mutation')
return this._pollMutation(selector, predicate, timeout);
return this._pollInterval(selector, polling, predicate, timeout);
}
getElementBorderWidth(node) {
if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)
return { left: 0, top: 0 };
const style = node.ownerDocument.defaultView.getComputedStyle(node);
return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };
}
selectOptions(node, optionsToSelect) {
if (node.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a <select> element.');
const element = node;
const options = Array.from(element.options);
element.value = undefined;
for (let index = 0; index < options.length; index++) {
const option = options[index];
option.selected = optionsToSelect.some(optionToSelect => {
if (optionToSelect instanceof Node)
return option === optionToSelect;
let matches = true;
if (optionToSelect.value !== undefined)
matches = matches && optionToSelect.value === option.value;
if (optionToSelect.label !== undefined)
matches = matches && optionToSelect.label === option.label;
if (optionToSelect.index !== undefined)
matches = matches && optionToSelect.index === index;
return matches;
});
if (option.selected && !element.multiple)
break;
}
element.dispatchEvent(new Event('input', { 'bubbles': true }));
element.dispatchEvent(new Event('change', { 'bubbles': true }));
return options.filter(option => option.selected).map(option => option.value);
}
fill(node, value) {
if (node.nodeType !== Node.ELEMENT_NODE)
return 'Node is not of type HTMLElement';
const element = node;
if (!element.isConnected)
return 'Element is not attached to the DOM';
if (!element.ownerDocument || !element.ownerDocument.defaultView)
return 'Element does not belong to a window';
const style = element.ownerDocument.defaultView.getComputedStyle(element);
if (!style || style.visibility === 'hidden')
return 'Element is hidden';
if (!element.offsetParent && element.tagName !== 'BODY')
return 'Element is not visible';
if (element.nodeName.toLowerCase() === 'input') {
const input = element;
const type = input.getAttribute('type') || '';
const kTextInputTypes = new Set(['', 'email', 'number', 'password', 'search', 'tel', 'text', 'url']);
if (!kTextInputTypes.has(type.toLowerCase()))
return 'Cannot fill input of type "' + type + '".';
if (type.toLowerCase() === 'number') {
value = value.trim();
if (!value || isNaN(Number(value)))
return 'Cannot type text into input[type=number].';
}
if (input.disabled)
return 'Cannot fill a disabled input.';
if (input.readOnly)
return 'Cannot fill a readonly input.';
input.select();
input.focus();
}
else if (element.nodeName.toLowerCase() === 'textarea') {
const textarea = element;
if (textarea.disabled)
return 'Cannot fill a disabled textarea.';
if (textarea.readOnly)
return 'Cannot fill a readonly textarea.';
textarea.selectionStart = 0;
textarea.selectionEnd = textarea.value.length;
textarea.focus();
}
else if (element.isContentEditable) {
const range = element.ownerDocument.createRange();
range.selectNodeContents(element);
const selection = element.ownerDocument.defaultView.getSelection();
if (!selection)
return 'Element belongs to invisible iframe.';
selection.removeAllRanges();
selection.addRange(range);
element.focus();
}
else {
return 'Element is not an <input>, <textarea> or [contenteditable] element.';
}
return false;
}
isCheckboxChecked(node) {
if (node.nodeType !== Node.ELEMENT_NODE)
throw new Error('Not a checkbox or radio button');
let element = node;
if (element.getAttribute('role') === 'checkbox')
return element.getAttribute('aria-checked') === 'true';
if (element.nodeName === 'LABEL') {
const forId = element.getAttribute('for');
if (forId && element.ownerDocument)
element = element.ownerDocument.querySelector(`input[id="${forId}"]`) || undefined;
else
element = element.querySelector('input[type=checkbox],input[type=radio]') || undefined;
}
if (element && element.nodeName === 'INPUT') {
const type = element.getAttribute('type');
if (type && (type.toLowerCase() === 'checkbox' || type.toLowerCase() === 'radio'))
return element.checked;
}
throw new Error('Not a checkbox');
}
waitForStablePosition(node, timeout) {
if (!node.isConnected)
throw new Error('Element is not attached to the DOM');
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
if (!element)
throw new Error('Element is not attached to the DOM');
let lastRect;
let counter = 0;
return this.poll('raf', undefined, timeout, () => {
// First raf happens in the same animation frame as evaluation, so it does not produce
// any client rect difference compared to synchronous call. We skip the synchronous call
// and only force layout during actual rafs as a small optimisation.
if (++counter === 1)
return false;
const clientRect = element.getBoundingClientRect();
const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };
const isStable = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;
lastRect = rect;
return isStable;
});
}
waitForHitTargetAt(node, timeout, point) {
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
if (!element)
throw new Error('Element is not attached to the DOM');
return this.poll('raf', undefined, timeout, () => {
let hitElement = this.utils.deepElementFromPoint(document, point.x, point.y);
while (hitElement && hitElement !== element)
hitElement = this.utils.parentElementOrShadowHost(hitElement);
return hitElement === element;
});
}
}

@@ -489,4 +637,4 @@ exports.default = Injected;

}
selector = selector.trim();
return text => text.trim() === selector;
selector = selector.trim().toLowerCase();
return text => text.trim().toLowerCase() === selector;
}

@@ -493,0 +641,0 @@

@@ -77,5 +77,5 @@ "use strict";

}
selector = selector.trim();
return text => text.trim() === selector;
selector = selector.trim().toLowerCase();
return text => text.trim().toLowerCase() === selector;
}
//# sourceMappingURL=textSelectorEngine.js.map

@@ -16,10 +16,5 @@ /**

*/
import * as types from './types';
export declare type Modifier = 'Alt' | 'Control' | 'Meta' | 'Shift';
export declare type Button = 'left' | 'right' | 'middle';
export declare type PointerActionOptions = {
modifiers?: Modifier[];
relativePoint?: types.Point;
};
export declare type ClickOptions = PointerActionOptions & {
export declare type MouseClickOptions = {
delay?: number;

@@ -29,3 +24,3 @@ button?: Button;

};
export declare type MultiClickOptions = PointerActionOptions & {
export declare type MouseMultiClickOptions = {
delay?: number;

@@ -85,5 +80,5 @@ button?: Button;

}): Promise<void>;
click(x: number, y: number, options?: ClickOptions): Promise<void>;
dblclick(x: number, y: number, options?: MultiClickOptions): Promise<void>;
tripleclick(x: number, y: number, options?: MultiClickOptions): Promise<void>;
click(x: number, y: number, options?: MouseClickOptions): Promise<void>;
dblclick(x: number, y: number, options?: MouseMultiClickOptions): Promise<void>;
tripleclick(x: number, y: number, options?: MouseMultiClickOptions): Promise<void>;
}

@@ -138,2 +138,4 @@ /**

};
export declare function verifyHeaders(headers: Headers): Headers;
export declare function mergeHeaders(headers: (Headers | undefined | null)[]): Headers;
export {};

@@ -286,2 +286,30 @@ "use strict";

};
function verifyHeaders(headers) {
const result = {};
for (const key of Object.keys(headers)) {
const value = headers[key];
helper_1.assert(helper_1.helper.isString(value), `Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
result[key] = value;
}
return result;
}
exports.verifyHeaders = verifyHeaders;
function mergeHeaders(headers) {
const lowerCaseToValue = new Map();
const lowerCaseToOriginalCase = new Map();
for (const h of headers) {
if (!h)
continue;
for (const key of Object.keys(h)) {
const lower = key.toLowerCase();
lowerCaseToOriginalCase.set(lower, key);
lowerCaseToValue.set(lower, h[key]);
}
}
const result = {};
for (const [lower, value] of lowerCaseToValue)
result[lowerCaseToOriginalCase.get(lower)] = value;
return result;
}
exports.mergeHeaders = mergeHeaders;
//# sourceMappingURL=network.js.map

@@ -40,3 +40,3 @@ /**

navigateFrame(frame: frames.Frame, url: string, referrer: string | undefined): Promise<frames.GotoResult>;
setExtraHTTPHeaders(extraHTTPHeaders: network.Headers): Promise<void>;
updateExtraHTTPHeaders(): Promise<void>;
setViewportSize(viewportSize: types.Size): Promise<void>;

@@ -177,3 +177,6 @@ setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;

evaluate: types.Evaluate;
evaluateOnNewDocument(pageFunction: Function | string, ...args: any[]): Promise<void>;
addInitScript(script: Function | string | {
path?: string;
content?: string;
}, ...args: any[]): Promise<void>;
setCacheEnabled(enabled?: boolean): Promise<void>;

@@ -190,14 +193,14 @@ route(url: types.URLMatch, handler: (request: network.Request) => void): Promise<void>;

isClosed(): boolean;
click(selector: string, options?: frames.WaitForOptions & input.ClickOptions): Promise<void>;
dblclick(selector: string, options?: frames.WaitForOptions & input.MultiClickOptions): Promise<void>;
tripleclick(selector: string, options?: frames.WaitForOptions & input.MultiClickOptions): Promise<void>;
fill(selector: string, value: string, options?: frames.WaitForOptions): Promise<void>;
focus(selector: string, options?: frames.WaitForOptions): Promise<void>;
hover(selector: string, options?: frames.WaitForOptions & input.PointerActionOptions): Promise<void>;
select(selector: string, value: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | undefined, options?: frames.WaitForOptions): Promise<string[]>;
type(selector: string, text: string, options?: frames.WaitForOptions & {
click(selector: string, options?: dom.ClickOptions & types.WaitForOptions): Promise<void>;
dblclick(selector: string, options?: dom.MultiClickOptions & types.WaitForOptions): Promise<void>;
tripleclick(selector: string, options?: dom.MultiClickOptions & types.WaitForOptions): Promise<void>;
fill(selector: string, value: string, options?: types.WaitForOptions): Promise<void>;
focus(selector: string, options?: types.WaitForOptions): Promise<void>;
hover(selector: string, options?: dom.PointerActionOptions & types.WaitForOptions): Promise<void>;
select(selector: string, value: string | dom.ElementHandle | types.SelectOption | string[] | dom.ElementHandle[] | types.SelectOption[] | undefined, options?: types.WaitForOptions): Promise<string[]>;
type(selector: string, text: string, options?: {
delay?: number;
}): Promise<void>;
check(selector: string, options?: frames.WaitForOptions): Promise<void>;
uncheck(selector: string, options?: frames.WaitForOptions): Promise<void>;
} & types.WaitForOptions): Promise<void>;
check(selector: string, options?: types.WaitForOptions): Promise<void>;
uncheck(selector: string, options?: types.WaitForOptions): Promise<void>;
waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & {

@@ -214,3 +217,3 @@ visibility?: types.Visibility;

}
export declare class Worker {
export declare class Worker extends platform.EventEmitter {
private _url;

@@ -217,0 +220,0 @@ private _executionContextPromise;

@@ -23,2 +23,3 @@ "use strict";

const js = require("./javascript");
const network = require("./network");
const screenshotter_1 = require("./screenshotter");

@@ -170,9 +171,4 @@ const timeoutSettings_1 = require("./timeoutSettings");

setExtraHTTPHeaders(headers) {
this._state.extraHTTPHeaders = {};
for (const key of Object.keys(headers)) {
const value = headers[key];
helper_1.assert(helper_1.helper.isString(value), `Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
this._state.extraHTTPHeaders[key] = value;
}
return this._delegate.setExtraHTTPHeaders(headers);
this._state.extraHTTPHeaders = network.verifyHeaders(headers);
return this._delegate.updateExtraHTTPHeaders();
}

@@ -248,3 +244,3 @@ async _onBindingCalled(payload, context) {

return helper_1.helper.waitForEvent(this, events_1.Events.Page.Request, (request) => {
if (helper_1.helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp)
if (helper_1.helper.isString(urlOrPredicate) || helper_1.helper.isRegExp(urlOrPredicate))
return platform.urlMatches(request.url(), urlOrPredicate);

@@ -257,3 +253,3 @@ return urlOrPredicate(request);

return helper_1.helper.waitForEvent(this, events_1.Events.Page.Response, (response) => {
if (helper_1.helper.isString(urlOrPredicate) || urlOrPredicate instanceof RegExp)
if (helper_1.helper.isString(urlOrPredicate) || helper_1.helper.isRegExp(urlOrPredicate))
return platform.urlMatches(response.url(), urlOrPredicate);

@@ -297,5 +293,4 @@ return urlOrPredicate(response);

}
async evaluateOnNewDocument(pageFunction, ...args) {
const source = helper_1.helper.evaluationString(pageFunction, ...args);
await this._delegate.evaluateOnNewDocument(source);
async addInitScript(script, ...args) {
await this._delegate.evaluateOnNewDocument(await helper_1.helper.evaluationScript(script, ...args));
}

@@ -398,3 +393,3 @@ async setCacheEnabled(enabled = true) {

this._workers.set(workerId, worker);
this.emit(events_1.Events.Page.WorkerCreated, worker);
this.emit(events_1.Events.Page.Worker, worker);
}

@@ -405,3 +400,3 @@ _removeWorker(workerId) {

return;
this.emit(events_1.Events.Page.WorkerDestroyed, worker);
worker.emit(events_1.Events.Worker.Close, worker);
this._workers.delete(workerId);

@@ -411,3 +406,3 @@ }

for (const [workerId, worker] of this._workers) {
this.emit(events_1.Events.Page.WorkerDestroyed, worker);
worker.emit(events_1.Events.Worker.Close, worker);
this._workers.delete(workerId);

@@ -432,4 +427,5 @@ }

exports.Page = Page;
class Worker {
class Worker extends platform.EventEmitter {
constructor(url) {
super();
this._existingExecutionContext = null;

@@ -436,0 +432,0 @@ this.evaluate = async (pageFunction, ...args) => {

@@ -199,3 +199,3 @@ "use strict";

match = helper_1.helper.globToRegex(match);
if (match instanceof RegExp)
if (helper_1.helper.isRegExp(match))
return match.test(urlString);

@@ -202,0 +202,0 @@ if (typeof match === 'string' && match === urlString)

@@ -34,2 +34,6 @@ /**

timeout?: number;
/**
* Whether to dump stdio of the browser, this is useful for example when
* diagnosing browser launch issues.
*/
dumpio?: boolean;

@@ -36,0 +40,0 @@ env?: {

@@ -26,5 +26,5 @@ /**

export declare class Chromium implements BrowserType {
private _projectRoot;
private _downloadPath;
readonly _revision: string;
constructor(projectRoot: string, preferredRevision: string);
constructor(downloadPath: string, preferredRevision: string);
name(): string;

@@ -31,0 +31,0 @@ launch(options?: LaunchOptions & {

@@ -35,4 +35,4 @@ "use strict";

class Chromium {
constructor(projectRoot, preferredRevision) {
this._projectRoot = projectRoot;
constructor(downloadPath, preferredRevision) {
this._downloadPath = downloadPath;
this._revision = preferredRevision;

@@ -60,3 +60,3 @@ }

const browser = await crBrowser_1.CRBrowser.connect(transport);
await helper_1.helper.waitWithTimeout(browser.waitForTarget(t => t.type() === 'page'), 'first page', timeout);
await helper_1.helper.waitWithTimeout(browser._defaultContext.waitForTarget(t => t.type() === 'page'), 'first page', timeout);
// Hack: for typical launch scenario, ensure that close waits for actual process termination.

@@ -148,2 +148,4 @@ const browserContext = browser._defaultContext;

throw new Error('Playwright manages remote debugging connection itself.');
if (launchType !== 'persistent' && args.find(arg => !arg.startsWith('-')))
throw new Error('Arguments can not specify page to be opened');
const chromeArguments = [...DEFAULT_ARGS];

@@ -157,2 +159,4 @@ chromeArguments.push(`--user-data-dir=${userDataDir}`);

}
if (launchType !== 'persistent')
chromeArguments.push('--no-startup-window');
chromeArguments.push(...args);

@@ -179,3 +183,3 @@ if (args.every(arg => arg.startsWith('-')))

const defaultOptions = {
path: path.join(this._projectRoot, '.local-chromium'),
path: path.join(this._downloadPath, '.local-chromium'),
host: 'https://storage.googleapis.com',

@@ -182,0 +186,0 @@ platform: (() => {

@@ -26,5 +26,5 @@ /**

export declare class Firefox implements BrowserType {
private _projectRoot;
private _downloadPath;
readonly _revision: string;
constructor(projectRoot: string, preferredRevision: string);
constructor(downloadPath: string, preferredRevision: string);
downloadBrowserIfNeeded(onProgress?: OnProgressCallback): Promise<void>;

@@ -31,0 +31,0 @@ name(): string;

@@ -35,4 +35,4 @@ "use strict";

class Firefox {
constructor(projectRoot, preferredRevision) {
this._projectRoot = projectRoot;
constructor(downloadPath, preferredRevision) {
this._downloadPath = downloadPath;
this._revision = preferredRevision;

@@ -74,3 +74,3 @@ }

}
async _launchServer(options = {}, connectionType, userDataDir, port) {
async _launchServer(options = {}, launchType, userDataDir, port) {
const { ignoreDefaultArgs = false, args = [], dumpio = false, executablePath = null, env = process.env, handleSIGHUP = true, handleSIGINT = true, handleSIGTERM = true, timeout = 30000, } = options;

@@ -84,5 +84,5 @@ const firefoxArguments = [];

if (!ignoreDefaultArgs)
firefoxArguments.push(...this._defaultArgs(options, userDataDir, port || 0));
firefoxArguments.push(...this._defaultArgs(options, launchType, userDataDir, port || 0));
else if (Array.isArray(ignoreDefaultArgs))
firefoxArguments.push(...this._defaultArgs(options, userDataDir, port || 0).filter(arg => !ignoreDefaultArgs.includes(arg)));
firefoxArguments.push(...this._defaultArgs(options, launchType, userDataDir, port || 0).filter(arg => !ignoreDefaultArgs.includes(arg)));
else

@@ -130,4 +130,4 @@ firefoxArguments.push(...args);

const browserWSEndpoint = match[1];
browserServer = new browserServer_1.BrowserServer(launchedProcess, gracefullyClose, connectionType === 'server' ? browserWSEndpoint : null);
return { browserServer, transport: connectionType === 'server' ? undefined : new platform.WebSocketTransport(browserWSEndpoint) };
browserServer = new browserServer_1.BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? browserWSEndpoint : null);
return { browserServer, transport: launchType === 'server' ? undefined : new platform.WebSocketTransport(browserWSEndpoint) };
}

@@ -147,3 +147,3 @@ async connect(options) {

}
_defaultArgs(options = {}, userDataDir, port) {
_defaultArgs(options = {}, launchType, userDataDir, port) {
const { devtools = false, headless = !devtools, args = [], } = options;

@@ -157,2 +157,4 @@ if (devtools)

throw new Error('Use the port parameter instead of -juggler argument');
if (launchType !== 'persistent' && args.find(arg => !arg.startsWith('-')))
throw new Error('Arguments can not specify page to be opened');
const firefoxArguments = ['-no-remote'];

@@ -181,3 +183,3 @@ if (headless) {

const defaultOptions = {
path: path.join(this._projectRoot, '.local-firefox'),
path: path.join(this._downloadPath, '.local-firefox'),
host: 'https://playwright.azureedge.net',

@@ -184,0 +186,0 @@ platform: (() => {

@@ -17,2 +17,3 @@ /**

import * as types from '../types';
import * as api from '../api';
import { TimeoutError } from '../errors';

@@ -23,2 +24,3 @@ import { Chromium } from './chromium';

export declare class Playwright {
readonly selectors: api.Selectors;
readonly devices: types.Devices;

@@ -28,10 +30,9 @@ readonly errors: {

};
readonly chromium: Chromium;
readonly firefox: Firefox;
readonly webkit: WebKit;
constructor(projectRoot: string, revisions: {
chromium_revision: string;
firefox_revision: string;
webkit_revision: string;
readonly chromium: (Chromium | undefined);
readonly firefox: (Firefox | undefined);
readonly webkit: (WebKit | undefined);
constructor(options: {
downloadPath: string;
browsers: Array<('firefox' | 'webkit' | 'chromium')>;
});
}

@@ -18,2 +18,4 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const api = require("../api");
const helper_1 = require("../helper");
const errors_1 = require("../errors");

@@ -24,9 +26,19 @@ const deviceDescriptors_1 = require("../deviceDescriptors");

const firefox_1 = require("./firefox");
const packageJSON = require('../../package.json');
for (const className in api) {
if (typeof api[className] === 'function')
helper_1.helper.installApiHooks(className[0].toLowerCase() + className.substring(1), api[className]);
}
class Playwright {
constructor(projectRoot, revisions) {
constructor(options) {
this.selectors = api.Selectors._instance();
const { downloadPath, browsers, } = options;
this.devices = deviceDescriptors_1.DeviceDescriptors;
this.errors = { TimeoutError: errors_1.TimeoutError };
this.chromium = new chromium_1.Chromium(projectRoot, revisions.chromium_revision);
this.firefox = new firefox_1.Firefox(projectRoot, revisions.firefox_revision);
this.webkit = new webkit_1.WebKit(projectRoot, revisions.webkit_revision);
if (browsers.includes('chromium'))
this.chromium = new chromium_1.Chromium(downloadPath, packageJSON.playwright.chromium_revision);
if (browsers.includes('webkit'))
this.webkit = new webkit_1.WebKit(downloadPath, packageJSON.playwright.webkit_revision);
if (browsers.includes('firefox'))
this.firefox = new firefox_1.Firefox(downloadPath, packageJSON.playwright.firefox_revision);
}

@@ -33,0 +45,0 @@ }

@@ -22,9 +22,9 @@ /**

import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType';
import { ConnectOptions } from '../browser';
import { ConnectOptions, LaunchType } from '../browser';
import { BrowserServer } from './browserServer';
import { BrowserContext } from '../browserContext';
export declare class WebKit implements BrowserType {
private _projectRoot;
private _downloadPath;
readonly _revision: string;
constructor(projectRoot: string, preferredRevision: string);
constructor(downloadPath: string, preferredRevision: string);
name(): string;

@@ -46,3 +46,3 @@ downloadBrowserIfNeeded(onProgress?: OnProgressCallback): Promise<void>;

};
_defaultArgs(options: BrowserArgOptions | undefined, userDataDir: string, port: number): string[];
_defaultArgs(options: BrowserArgOptions | undefined, launchType: LaunchType, userDataDir: string, port: number): string[];
_createBrowserFetcher(options?: BrowserFetcherOptions): BrowserFetcher;

@@ -49,0 +49,0 @@ _resolveExecutablePath(): {

@@ -33,3 +33,2 @@ "use strict";

const wkConnection_1 = require("../webkit/wkConnection");
const transport_1 = require("../transport");
const ws = require("ws");

@@ -40,4 +39,4 @@ const uuidv4 = require("uuid/v4");

class WebKit {
constructor(projectRoot, preferredRevision) {
this._projectRoot = projectRoot;
constructor(downloadPath, preferredRevision) {
this._downloadPath = downloadPath;
this._revision = preferredRevision;

@@ -72,3 +71,3 @@ }

const { browserServer, transport } = await this._launchServer(options, 'persistent', userDataDir);
const browser = await wkBrowser_1.WKBrowser.connect(transport);
const browser = await wkBrowser_1.WKBrowser.connect(transport, undefined, true);
await helper_1.helper.waitWithTimeout(browser._waitForFirstPageTarget(), 'first page', timeout);

@@ -81,3 +80,3 @@ // Hack: for typical launch scenario, ensure that close waits for actual process termination.

async _launchServer(options = {}, launchType, userDataDir, port) {
const { ignoreDefaultArgs = false, args = [], dumpio = false, executablePath = null, env = process.env, handleSIGINT = true, handleSIGTERM = true, handleSIGHUP = true, timeout = 30000 } = options;
const { ignoreDefaultArgs = false, args = [], dumpio = false, executablePath = null, env = process.env, handleSIGINT = true, handleSIGTERM = true, handleSIGHUP = true, } = options;
let temporaryUserDataDir = null;

@@ -90,5 +89,5 @@ if (!userDataDir) {

if (!ignoreDefaultArgs)
webkitArguments.push(...this._defaultArgs(options, userDataDir, port || 0));
webkitArguments.push(...this._defaultArgs(options, launchType, userDataDir, port || 0));
else if (Array.isArray(ignoreDefaultArgs))
webkitArguments.push(...this._defaultArgs(options, userDataDir, port || 0).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));
webkitArguments.push(...this._defaultArgs(options, launchType, userDataDir, port || 0).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));
else

@@ -129,5 +128,3 @@ webkitArguments.push(...args);

});
const timeoutError = new errors_1.TimeoutError(`Timed out after ${timeout} ms while trying to connect to WebKit! The only WebKit revision guaranteed to work is r${this._revision}`);
await processLauncher_1.waitForLine(launchedProcess, launchedProcess.stdout, /^Web Inspector is reading from pipe #3$/, timeout, timeoutError);
transport = new transport_1.DeferWriteTransport(new pipeTransport_1.PipeTransport(launchedProcess.stdio[3], launchedProcess.stdio[4]));
transport = new pipeTransport_1.PipeTransport(launchedProcess.stdio[3], launchedProcess.stdio[4]);
browserServer = new browserServer_1.BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? await wrapTransportWithWebSocket(transport, port || 0) : null);

@@ -149,3 +146,3 @@ return { browserServer, transport };

}
_defaultArgs(options = {}, userDataDir, port) {
_defaultArgs(options = {}, launchType, userDataDir, port) {
const { devtools = false, headless = !devtools, args = [], } = options;

@@ -157,6 +154,11 @@ if (devtools)

throw new Error('Pass userDataDir parameter instead of specifying --user-data-dir argument');
if (launchType !== 'persistent' && args.find(arg => !arg.startsWith('-')))
throw new Error('Arguments can not specify page to be opened');
const webkitArguments = ['--inspector-pipe'];
if (headless)
webkitArguments.push('--headless');
webkitArguments.push(`--user-data-dir=${userDataDir}`);
if (launchType === 'persistent')
webkitArguments.push(`--user-data-dir=${userDataDir}`);
else
webkitArguments.push(`--no-startup-window`);
webkitArguments.push(...args);

@@ -172,3 +174,3 @@ return webkitArguments;

const defaultOptions = {
path: path.join(this._projectRoot, '.local-webkit'),
path: path.join(this._downloadPath, '.local-webkit'),
host: 'https://playwright.azureedge.net',

@@ -320,14 +322,2 @@ platform: (() => {

sockets.add(socket);
// Following two messages are reporting the default browser context and the default page.
socket.send(JSON.stringify({
method: 'Browser.pageProxyCreated',
params: { pageProxyInfo: { pageProxyId: '5', browserContextId: '0000000000000002' } }
}));
socket.send(JSON.stringify({
method: 'Target.targetCreated',
params: {
targetInfo: { targetId: 'page-6', type: 'page', isPaused: false }
},
pageProxyId: '5'
}));
socket.on('message', (message) => {

@@ -334,0 +324,0 @@ const parsedMessage = JSON.parse(Buffer.from(message).toString());

@@ -18,2 +18,3 @@ /**

import * as dom from './dom';
import Injected from './injected/injected';
declare type Boxed<Args extends any[]> = {

@@ -24,2 +25,3 @@ [Index in keyof Args]: Args[Index] | js.JSHandle<Args[Index]>;

declare type PageFunctionOn<On, Args extends any[], R = any> = string | ((on: On, ...args: Args) => R | Promise<R>);
declare type PageFunctionWithInjected<On, Args extends any[], R = any> = string | ((injected: Injected, on: On, ...args: Args) => R | Promise<R>);
declare type Handle<T> = T extends Node ? dom.ElementHandle<T> : js.JSHandle<T>;

@@ -32,2 +34,3 @@ export declare type Evaluate = <Args extends any[], R>(pageFunction: PageFunction<Args, R>, ...args: Boxed<Args>) => Promise<R>;

export declare type EvaluateHandleOn<T> = <Args extends any[], R>(pageFunction: PageFunctionOn<T, Args, R>, ...args: Boxed<Args>) => Promise<Handle<R>>;
export declare type EvaluateWithInjected<T> = <Args extends any[], R>(pageFunction: PageFunctionWithInjected<T, Args, R>, ...args: Boxed<Args>) => Promise<R>;
export declare type Size = {

@@ -46,2 +49,5 @@ width: number;

};
export declare type WaitForOptions = TimeoutOptions & {
waitFor?: boolean;
};
export declare type Visibility = 'visible' | 'hidden' | 'any';

@@ -48,0 +54,0 @@ export declare type Polling = 'raf' | 'mutation' | number;

@@ -281,2 +281,3 @@ "use strict";

'Enter': 'insertNewline:',
'NumpadEnter': 'insertNewline:',
'Escape': 'cancelOperation:',

@@ -295,2 +296,3 @@ 'ArrowUp': 'moveUp:',

'Shift+Enter': 'insertNewline:',
'Shift+NumpadEnter': 'insertNewline:',
'Shift+Tab': 'insertBacktab:',

@@ -311,2 +313,3 @@ 'Shift+Escape': 'cancelOperation:',

'Control+Enter': 'insertLineBreak:',
'Control+NumpadEnter': 'insertLineBreak:',
'Control+Quote': 'insertSingleQuoteIgnoringSubstitution:',

@@ -333,2 +336,3 @@ 'Control+KeyA': 'moveToBeginningOfParagraph:',

'Shift+Control+Enter': 'insertLineBreak:',
'Shift+Control+NumpadEnter': 'insertLineBreak:',
'Shift+Control+Tab': 'selectPreviousKeyView:',

@@ -351,2 +355,3 @@ 'Shift+Control+Quote': 'insertDoubleQuoteIgnoringSubstitution:',

'Alt+Enter': 'insertNewlineIgnoringFieldEditor:',
'Alt+NumpadEnter': 'insertNewlineIgnoringFieldEditor:',
'Alt+Escape': 'complete:',

@@ -363,2 +368,3 @@ 'Alt+ArrowUp': ['moveBackward:', 'moveToBeginningOfParagraph:'],

'Shift+Alt+Enter': 'insertNewlineIgnoringFieldEditor:',
'Shift+Alt+NumpadEnter': 'insertNewlineIgnoringFieldEditor:',
'Shift+Alt+Escape': 'complete:',

@@ -365,0 +371,0 @@ 'Shift+Alt+ArrowUp': 'moveParagraphBackwardAndModifySelection:',

@@ -19,18 +19,23 @@ /**

import { BrowserContext, BrowserContextOptions } from '../browserContext';
import * as network from '../network';
import { Page } from '../page';
import { ConnectionTransport } from '../transport';
import * as types from '../types';
import { Protocol } from './protocol';
import { PageProxyMessageReceivedPayload } from './wkConnection';
import { WKSession, PageProxyMessageReceivedPayload } from './wkConnection';
import { WKPageProxy } from './wkPageProxy';
import * as platform from '../platform';
import { TimeoutSettings } from '../timeoutSettings';
export declare class WKBrowser extends platform.EventEmitter implements Browser {
private readonly _connection;
private readonly _browserSession;
readonly _defaultContext: BrowserContext;
private readonly _contexts;
private readonly _pageProxies;
private readonly _attachToDefaultContext;
readonly _browserSession: WKSession;
readonly _defaultContext: WKBrowserContext;
readonly _contexts: Map<string, WKBrowserContext>;
readonly _pageProxies: Map<string, WKPageProxy>;
private readonly _eventListeners;
private _firstPageProxyCallback?;
private readonly _firstPageProxyPromise;
static connect(transport: ConnectionTransport, slowMo?: number): Promise<WKBrowser>;
constructor(transport: ConnectionTransport);
static connect(transport: ConnectionTransport, slowMo?: number, attachToDefaultContext?: boolean): Promise<WKBrowser>;
constructor(transport: ConnectionTransport, attachToDefaultContext: boolean);
_onDisconnect(): void;

@@ -47,4 +52,31 @@ newContext(options?: BrowserContextOptions): Promise<BrowserContext>;

close(): Promise<void>;
_createBrowserContext(browserContextId: string | undefined, options: BrowserContextOptions): BrowserContext;
_setDebugFunction(debugFunction: (message: string) => void): void;
}
export declare class WKBrowserContext extends platform.EventEmitter implements BrowserContext {
readonly _browser: WKBrowser;
readonly _browserContextId: string | undefined;
readonly _options: BrowserContextOptions;
readonly _timeoutSettings: TimeoutSettings;
private _closed;
readonly _evaluateOnNewDocumentSources: string[];
constructor(browser: WKBrowser, browserContextId: string | undefined, options: BrowserContextOptions);
_initialize(): Promise<void>;
_existingPages(): Page[];
setDefaultNavigationTimeout(timeout: number): void;
setDefaultTimeout(timeout: number): void;
pages(): Promise<Page[]>;
newPage(): Promise<Page>;
cookies(...urls: string[]): Promise<network.NetworkCookie[]>;
setCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
clearCookies(): Promise<void>;
setPermissions(origin: string, permissions: string[]): Promise<void>;
clearPermissions(): Promise<void>;
setGeolocation(geolocation: types.Geolocation | null): Promise<void>;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
addInitScript(script: Function | string | {
path?: string;
content?: string;
}, ...args: any[]): Promise<void>;
close(): Promise<void>;
_browserClosed(): void;
}

@@ -22,2 +22,3 @@ "use strict";

const helper_1 = require("../helper");
const network = require("../network");
const transport_1 = require("../transport");

@@ -28,5 +29,6 @@ const events_1 = require("../events");

const platform = require("../platform");
const timeoutSettings_1 = require("../timeoutSettings");
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/605.1.15';
class WKBrowser extends platform.EventEmitter {
constructor(transport) {
constructor(transport, attachToDefaultContext) {
super();

@@ -36,4 +38,5 @@ this._contexts = new Map();

this._connection = new wkConnection_1.WKConnection(transport, this._onDisconnect.bind(this));
this._attachToDefaultContext = attachToDefaultContext;
this._browserSession = this._connection.browserSession;
this._defaultContext = this._createBrowserContext(undefined, {});
this._defaultContext = new WKBrowserContext(this, undefined, browserContext_1.validateBrowserContextOptions({}));
this._eventListeners = [

@@ -47,8 +50,8 @@ helper_1.helper.addEventListener(this._browserSession, 'Browser.pageProxyCreated', this._onPageProxyCreated.bind(this)),

}
static async connect(transport, slowMo = 0) {
const browser = new WKBrowser(transport_1.SlowMoTransport.wrap(transport, slowMo));
static async connect(transport, slowMo = 0, attachToDefaultContext = false) {
const browser = new WKBrowser(transport_1.SlowMoTransport.wrap(transport, slowMo), attachToDefaultContext);
return browser;
}
_onDisconnect() {
for (const context of this.contexts())
for (const context of this._contexts.values())
context._browserClosed();

@@ -61,9 +64,6 @@ for (const pageProxy of this._pageProxies.values())

async newContext(options = {}) {
options = browserContext_1.validateBrowserContextOptions(options);
const { browserContextId } = await this._browserSession.send('Browser.createContext');
options.userAgent = options.userAgent || DEFAULT_USER_AGENT;
const context = this._createBrowserContext(browserContextId, options);
if (options.ignoreHTTPSErrors)
await this._browserSession.send('Browser.setIgnoreCertificateErrors', { browserContextId, ignore: true });
if (options.locale)
await this._browserSession.send('Browser.setLanguages', { browserContextId, languages: [options.locale] });
const context = new WKBrowserContext(this, browserContextId, options);
await context._initialize();

@@ -94,2 +94,4 @@ this._contexts.set(browserContextId, context);

}
if (!context && !this._attachToDefaultContext)
return;
if (!context)

@@ -100,16 +102,5 @@ context = this._defaultContext;

});
const pageProxy = new wkPageProxy_1.WKPageProxy(pageProxySession, context, () => {
if (!pageProxyInfo.openerId)
return null;
const opener = this._pageProxies.get(pageProxyInfo.openerId);
if (!opener)
return null;
return opener;
});
const opener = pageProxyInfo.openerId ? this._pageProxies.get(pageProxyInfo.openerId) : undefined;
const pageProxy = new wkPageProxy_1.WKPageProxy(pageProxySession, context, opener || null);
this._pageProxies.set(pageProxyId, pageProxy);
if (pageProxyInfo.openerId) {
const opener = this._pageProxies.get(pageProxyInfo.openerId);
if (opener)
opener.onPopupCreated(pageProxy);
}
if (this._firstPageProxyCallback) {

@@ -123,2 +114,4 @@ this._firstPageProxyCallback();

const pageProxy = this._pageProxies.get(pageProxyId);
if (!pageProxy)
return;
pageProxy.didClose();

@@ -130,2 +123,4 @@ pageProxy.dispose();

const pageProxy = this._pageProxies.get(event.pageProxyId);
if (!pageProxy)
return;
pageProxy.dispatchMessageToSession(event.message);

@@ -135,2 +130,4 @@ }

const pageProxy = this._pageProxies.get(event.pageProxyId);
if (!pageProxy)
return;
pageProxy.handleProvisionalLoadFailed(event);

@@ -148,66 +145,2 @@ }

}
_createBrowserContext(browserContextId, options) {
browserContext_1.BrowserContext.validateOptions(options);
const context = new browserContext_1.BrowserContext({
pages: async () => {
const pageProxies = Array.from(this._pageProxies.values()).filter(proxy => proxy._browserContext === context);
return await Promise.all(pageProxies.map(proxy => proxy.page()));
},
existingPages: () => {
const pages = [];
for (const pageProxy of this._pageProxies.values()) {
if (pageProxy._browserContext !== context)
continue;
const page = pageProxy.existingPage();
if (page)
pages.push(page);
}
return pages;
},
newPage: async () => {
const { pageProxyId } = await this._browserSession.send('Browser.createPage', { browserContextId });
const pageProxy = this._pageProxies.get(pageProxyId);
return await pageProxy.page();
},
close: async () => {
helper_1.assert(browserContextId, 'Non-incognito profiles cannot be closed!');
await this._browserSession.send('Browser.deleteContext', { browserContextId: browserContextId });
this._contexts.delete(browserContextId);
},
cookies: async () => {
const { cookies } = await this._browserSession.send('Browser.getAllCookies', { browserContextId });
return cookies.map((c) => ({
...c,
expires: c.expires === 0 ? -1 : c.expires
}));
},
clearCookies: async () => {
await this._browserSession.send('Browser.deleteAllCookies', { browserContextId });
},
setCookies: async (cookies) => {
const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined }));
await this._browserSession.send('Browser.setCookies', { cookies: cc, browserContextId });
},
setPermissions: async (origin, permissions) => {
const webPermissionToProtocol = new Map([
['geolocation', 'geolocation'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._browserSession.send('Browser.grantPermissions', { origin, browserContextId, permissions: filtered });
},
clearPermissions: async () => {
await this._browserSession.send('Browser.resetPermissions', { browserContextId });
},
setGeolocation: async (geolocation) => {
const payload = geolocation ? { ...geolocation, timestamp: Date.now() } : undefined;
await this._browserSession.send('Browser.setGeolocationOverride', { browserContextId, geolocation: payload });
}
}, options);
return context;
}
_setDebugFunction(debugFunction) {

@@ -218,2 +151,115 @@ this._connection._debugFunction = debugFunction;

exports.WKBrowser = WKBrowser;
class WKBrowserContext extends platform.EventEmitter {
constructor(browser, browserContextId, options) {
super();
this._closed = false;
this._browser = browser;
this._browserContextId = browserContextId;
this._timeoutSettings = new timeoutSettings_1.TimeoutSettings();
this._options = options;
this._evaluateOnNewDocumentSources = [];
}
async _initialize() {
if (this._options.ignoreHTTPSErrors)
await this._browser._browserSession.send('Browser.setIgnoreCertificateErrors', { browserContextId: this._browserContextId, ignore: true });
if (this._options.locale)
await this._browser._browserSession.send('Browser.setLanguages', { browserContextId: this._browserContextId, languages: [this._options.locale] });
const entries = Object.entries(this._options.permissions || {});
await Promise.all(entries.map(entry => this.setPermissions(entry[0], entry[1])));
if (this._options.geolocation)
await this.setGeolocation(this._options.geolocation);
}
_existingPages() {
const pages = [];
for (const pageProxy of this._browser._pageProxies.values()) {
if (pageProxy._browserContext !== this)
continue;
const page = pageProxy.existingPage();
if (page)
pages.push(page);
}
return pages;
}
setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout);
}
async pages() {
const pageProxies = Array.from(this._browser._pageProxies.values()).filter(proxy => proxy._browserContext === this);
const pages = await Promise.all(pageProxies.map(proxy => proxy.page()));
return pages.filter(page => !!page);
}
async newPage() {
browserContext_1.assertBrowserContextIsNotOwned(this);
const { pageProxyId } = await this._browser._browserSession.send('Browser.createPage', { browserContextId: this._browserContextId });
const pageProxy = this._browser._pageProxies.get(pageProxyId);
const page = await pageProxy.page();
return page;
}
async cookies(...urls) {
const { cookies } = await this._browser._browserSession.send('Browser.getAllCookies', { browserContextId: this._browserContextId });
return network.filterCookies(cookies.map((c) => ({
...c,
expires: c.expires === 0 ? -1 : c.expires
})), urls);
}
async setCookies(cookies) {
const cc = network.rewriteCookies(cookies).map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined }));
await this._browser._browserSession.send('Browser.setCookies', { cookies: cc, browserContextId: this._browserContextId });
}
async clearCookies() {
await this._browser._browserSession.send('Browser.deleteAllCookies', { browserContextId: this._browserContextId });
}
async setPermissions(origin, permissions) {
const webPermissionToProtocol = new Map([
['geolocation', 'geolocation'],
]);
const filtered = permissions.map(permission => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._browser._browserSession.send('Browser.grantPermissions', { origin, browserContextId: this._browserContextId, permissions: filtered });
}
async clearPermissions() {
await this._browser._browserSession.send('Browser.resetPermissions', { browserContextId: this._browserContextId });
}
async setGeolocation(geolocation) {
if (geolocation)
geolocation = browserContext_1.verifyGeolocation(geolocation);
this._options.geolocation = geolocation || undefined;
const payload = geolocation ? { ...geolocation, timestamp: Date.now() } : undefined;
await this._browser._browserSession.send('Browser.setGeolocationOverride', { browserContextId: this._browserContextId, geolocation: payload });
}
async setExtraHTTPHeaders(headers) {
this._options.extraHTTPHeaders = network.verifyHeaders(headers);
for (const page of this._existingPages())
await page._delegate.updateExtraHTTPHeaders();
}
async addInitScript(script, ...args) {
const source = await helper_1.helper.evaluationScript(script, ...args);
this._evaluateOnNewDocumentSources.push(source);
for (const page of this._existingPages())
await page._delegate._updateBootstrapScript();
}
async close() {
if (this._closed)
return;
helper_1.assert(this._browserContextId, 'Non-incognito profiles cannot be closed!');
await this._browser._browserSession.send('Browser.deleteContext', { browserContextId: this._browserContextId });
this._browser._contexts.delete(this._browserContextId);
this._closed = true;
this.emit(events_1.Events.BrowserContext.Close);
}
_browserClosed() {
this._closed = true;
for (const page of this._existingPages())
page._didClose();
this.emit(events_1.Events.BrowserContext.Close);
}
}
exports.WKBrowserContext = WKBrowserContext;
//# sourceMappingURL=wkBrowser.js.map

@@ -44,5 +44,5 @@ "use strict";

rawSend(message) {
message = JSON.stringify(message);
this._debugFunction('SEND ► ' + message);
this._transport.send(message);
const data = JSON.stringify(message);
this._debugFunction('SEND ► ' + (rewriteInjectedScriptEvaluationLog(message) || data));
this._transport.send(data);
}

@@ -148,2 +148,8 @@ _dispatchMessage(message) {

exports.isSwappedOutError = isSwappedOutError;
function rewriteInjectedScriptEvaluationLog(message) {
// Injected script is very long and clutters protocol logs.
// To increase development velocity, we skip replace it with short description in the log.
if (message.params && message.params.message && message.params.message.includes('Runtime.evaluate') && message.params.message.includes('src/injected/injected.ts'))
return `{"id":${message.id},"method":"${message.method}","params":{"message":[evaluate injected script],"targetId":"${message.params.targetId}"},"pageProxyId":${message.pageProxyId}}`;
}
//# sourceMappingURL=wkConnection.js.map

@@ -23,3 +23,2 @@ /**

import { Protocol } from './protocol';
import { BrowserContext } from '../browserContext';
import { RawMouseImpl, RawKeyboardImpl } from './wkInput';

@@ -29,2 +28,4 @@ import * as types from '../types';

import * as platform from '../platform';
import { WKPageProxy } from './wkPageProxy';
import { WKBrowserContext } from './wkBrowser';
export declare class WKPage implements PageDelegate {

@@ -37,3 +38,3 @@ readonly rawMouse: RawMouseImpl;

private readonly _pageProxySession;
private readonly _openerResolver;
private readonly _opener;
private readonly _requestIdToRequest;

@@ -45,3 +46,4 @@ private readonly _workers;

private readonly _bootstrapScripts;
constructor(browserContext: BrowserContext, pageProxySession: WKSession, openerResolver: () => Promise<Page | null>);
private readonly _browserContext;
constructor(browserContext: WKBrowserContext, pageProxySession: WKSession, opener: WKPageProxy | null);
private _initializePageProxySession;

@@ -74,3 +76,4 @@ private _setSession;

private static _setEmulateMedia;
setExtraHTTPHeaders(headers: network.Headers): Promise<void>;
updateExtraHTTPHeaders(): Promise<void>;
_calculateExtraHTTPHeaders(): network.Headers;
setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void>;

@@ -90,3 +93,4 @@ setViewportSize(viewportSize: types.Size): Promise<void>;

evaluateOnNewDocument(script: string): Promise<void>;
private _setBootstrapScripts;
private _calculateBootstrapScript;
_updateBootstrapScript(): Promise<void>;
closePage(runBeforeUnload: boolean): Promise<void>;

@@ -93,0 +97,0 @@ getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>;

@@ -21,2 +21,3 @@ "use strict";

const dom = require("../dom");
const network = require("../network");
const events_1 = require("../events");

@@ -35,3 +36,3 @@ const wkExecutionContext_1 = require("./wkExecutionContext");

class WKPage {
constructor(browserContext, pageProxySession, openerResolver) {
constructor(browserContext, pageProxySession, opener) {
this._provisionalPage = null;

@@ -42,3 +43,3 @@ this._requestIdToRequest = new Map();

this._pageProxySession = pageProxySession;
this._openerResolver = openerResolver;
this._opener = opener;
this.rawKeyboard = new wkInput_1.RawKeyboardImpl(pageProxySession);

@@ -50,2 +51,3 @@ this.rawMouse = new wkInput_1.RawMouseImpl(pageProxySession);

this._session = undefined;
this._browserContext = browserContext;
this._page.on(events_1.Events.Page.FrameDetached, frame => this._removeContextsForFrame(frame, false));

@@ -118,14 +120,6 @@ }

promises.push(WKPage._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme));
if (this._bootstrapScripts.length) {
const source = this._bootstrapScripts.join(';');
promises.push(session.send('Page.setBootstrapScript', { source }));
}
promises.push(session.send('Page.setBootstrapScript', { source: this._calculateBootstrapScript() }));
if (contextOptions.bypassCSP)
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
if (this._page._state.extraHTTPHeaders || contextOptions.locale) {
const headers = this._page._state.extraHTTPHeaders || {};
if (contextOptions.locale)
headers['Accept-Language'] = contextOptions.locale;
promises.push(session.send('Network.setExtraHTTPHeaders', { headers }));
}
promises.push(session.send('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() }));
if (this._page._state.hasTouch)

@@ -333,8 +327,14 @@ promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true }));

}
async setExtraHTTPHeaders(headers) {
const copy = { ...headers };
async updateExtraHTTPHeaders() {
await this._updateState('Network.setExtraHTTPHeaders', { headers: this._calculateExtraHTTPHeaders() });
}
_calculateExtraHTTPHeaders() {
const headers = network.mergeHeaders([
this._page.context()._options.extraHTTPHeaders,
this._page._state.extraHTTPHeaders
]);
const locale = this._page.context()._options.locale;
if (locale)
copy['Accept-Language'] = locale;
await this._updateState('Network.setExtraHTTPHeaders', { headers: copy });
headers['Accept-Language'] = locale;
return headers;
}

@@ -382,3 +382,4 @@ async setEmulateMedia(mediaType, colorScheme) {

async opener() {
return await this._openerResolver();
const openerPage = this._opener ? await this._opener.page() : null;
return openerPage && !openerPage.isClosed() ? openerPage : null;
}

@@ -405,3 +406,3 @@ async reload() {

this._bootstrapScripts.unshift(script);
await this._setBootstrapScripts();
await this._updateBootstrapScript();
await Promise.all(this._page.frames().map(frame => frame.evaluate(script).catch(helper_1.debugError)));

@@ -411,8 +412,10 @@ }

this._bootstrapScripts.push(script);
await this._setBootstrapScripts();
await this._updateBootstrapScript();
}
async _setBootstrapScripts() {
const source = this._bootstrapScripts.join(';');
await this._updateState('Page.setBootstrapScript', { source });
_calculateBootstrapScript() {
return [...this._browserContext._evaluateOnNewDocumentSources, ...this._bootstrapScripts].join(';');
}
async _updateBootstrapScript() {
await this._updateState('Page.setBootstrapScript', { source: this._calculateBootstrapScript() });
}
async closePage(runBeforeUnload) {

@@ -419,0 +422,0 @@ this._pageProxySession.send('Target.close', {

@@ -16,18 +16,18 @@ /**

*/
import { BrowserContext } from '../browserContext';
import { Page } from '../page';
import { Protocol } from './protocol';
import { WKSession } from './wkConnection';
import { WKBrowserContext } from './wkBrowser';
export declare class WKPageProxy {
private readonly _pageProxySession;
readonly _browserContext: BrowserContext;
private readonly _openerResolver;
private _pagePromise;
private _wkPage;
private readonly _firstTargetPromise;
private _firstTargetCallback?;
private _pagePausedOnStart;
readonly _browserContext: WKBrowserContext;
private readonly _opener;
private readonly _pagePromise;
private _pagePromiseFulfill;
private _pagePromiseReject;
private readonly _wkPage;
private _initialized;
private readonly _sessions;
private readonly _eventListeners;
constructor(pageProxySession: WKSession, browserContext: BrowserContext, openerResolver: () => (WKPageProxy | null));
constructor(pageProxySession: WKSession, browserContext: WKBrowserContext, opener: WKPageProxy | null);
didClose(): void;

@@ -38,6 +38,4 @@ dispose(): void;

handleProvisionalLoadFailed(event: Protocol.Browser.provisionalLoadFailedPayload): void;
page(): Promise<Page>;
page(): Promise<Page | null>;
existingPage(): Page | undefined;
onPopupCreated(popupPageProxy: WKPageProxy): void;
private _initializeWKPage;
private _onTargetCreated;

@@ -44,0 +42,0 @@ private _resumeTarget;

@@ -24,11 +24,10 @@ "use strict";

class WKPageProxy {
constructor(pageProxySession, browserContext, openerResolver) {
this._pagePromise = null;
this._wkPage = null;
this._pagePausedOnStart = false;
constructor(pageProxySession, browserContext, opener) {
this._pagePromiseFulfill = () => { };
this._pagePromiseReject = () => { };
this._initialized = false;
this._sessions = new Map();
this._pageProxySession = pageProxySession;
this._browserContext = browserContext;
this._openerResolver = openerResolver;
this._firstTargetPromise = new Promise(r => this._firstTargetCallback = r);
this._opener = opener;
this._eventListeners = [

@@ -40,5 +39,10 @@ helper_1.helper.addEventListener(this._pageProxySession, 'Target.targetCreated', this._onTargetCreated.bind(this)),

];
this._pagePromise = new Promise((f, r) => {
this._pagePromiseFulfill = f;
this._pagePromiseReject = r;
});
this._wkPage = new wkPage_1.WKPage(this._browserContext, this._pageProxySession, this._opener);
}
didClose() {
if (this._wkPage)
if (this._initialized)
this._wkPage.didClose(false);

@@ -52,4 +56,3 @@ }

this._sessions.clear();
if (this._wkPage)
this._wkPage.dispose();
this._wkPage.dispose();
}

@@ -67,3 +70,3 @@ dispatchMessageToSession(message) {

handleProvisionalLoadFailed(event) {
if (!this._wkPage)
if (!this._initialized)
return;

@@ -78,37 +81,8 @@ if (!this._isProvisionalCrossProcessLoadInProgress())

async page() {
if (!this._pagePromise)
this._pagePromise = this._initializeWKPage();
return this._pagePromise;
}
existingPage() {
return this._wkPage ? this._wkPage._page : undefined;
return this._initialized ? this._wkPage._page : undefined;
}
onPopupCreated(popupPageProxy) {
if (this._wkPage)
popupPageProxy.page().then(page => this._wkPage._page.emit(events_1.Events.Page.Popup, page));
}
async _initializeWKPage() {
await this._firstTargetPromise;
let session;
for (const anySession of this._sessions.values()) {
if (!anySession[isPovisionalSymbol]) {
session = anySession;
break;
}
}
helper_1.assert(session, 'One non-provisional target session must exist');
this._wkPage = new wkPage_1.WKPage(this._browserContext, this._pageProxySession, async () => {
const pageProxy = this._openerResolver();
if (!pageProxy)
return null;
return await pageProxy.page();
});
await this._wkPage.initialize(session);
if (this._pagePausedOnStart) {
this._resumeTarget(session.sessionId);
this._pagePausedOnStart = false;
}
return this._wkPage._page;
}
_onTargetCreated(event) {
async _onTargetCreated(event) {
const { targetInfo } = event;

@@ -124,26 +98,36 @@ const session = new wkConnection_1.WKSession(this._pageProxySession.connection, targetInfo.targetId, `The ${targetInfo.type} has been closed.`, (message) => {

this._sessions.set(targetInfo.targetId, session);
if (this._firstTargetCallback) {
this._firstTargetCallback();
this._firstTargetCallback = undefined;
}
if (targetInfo.isProvisional) {
session[isPovisionalSymbol] = true;
if (this._wkPage) {
const provisionalPageInitialized = this._wkPage.initializeProvisionalPage(session);
if (targetInfo.isPaused)
provisionalPageInitialized.then(() => this._resumeTarget(targetInfo.targetId));
if (!this._initialized) {
helper_1.assert(!targetInfo.isProvisional);
this._initialized = true;
let page = null;
let error;
try {
await this._wkPage.initialize(session);
page = this._wkPage._page;
}
else if (targetInfo.isPaused) {
catch (e) {
if (!this._pageProxySession.isDisposed())
error = e;
}
if (error)
this._pagePromiseReject(error);
else
this._pagePromiseFulfill(page);
if (targetInfo.isPaused)
this._resumeTarget(targetInfo.targetId);
if (page && this._opener) {
this._opener.page().then(openerPage => {
if (!openerPage || page.isClosed())
return;
openerPage.emit(events_1.Events.Page.Popup, page);
});
}
}
else if (this._pagePromise) {
helper_1.assert(!this._pagePausedOnStart);
// This is the first time page target is created, will resume
// after finishing intialization.
this._pagePausedOnStart = !!targetInfo.isPaused;
else {
helper_1.assert(targetInfo.isProvisional);
session[isPovisionalSymbol] = true;
const provisionalPageInitialized = this._wkPage.initializeProvisionalPage(session);
if (targetInfo.isPaused)
provisionalPageInitialized.then(() => this._resumeTarget(targetInfo.targetId));
}
else if (targetInfo.isPaused) {
this._resumeTarget(targetInfo.targetId);
}
}

@@ -159,4 +143,3 @@ _resumeTarget(targetId) {

this._sessions.delete(targetId);
if (this._wkPage)
this._wkPage.onSessionDestroyed(session, crashed);
this._wkPage.onSessionDestroyed(session, crashed);
}

@@ -178,4 +161,3 @@ _onDispatchMessageFromTarget(event) {

newSession[isPovisionalSymbol] = undefined;
if (this._wkPage)
this._wkPage.onProvisionalLoadCommitted(newSession);
this._wkPage.onProvisionalLoadCommitted(newSession);
}

@@ -182,0 +164,0 @@ }

{
"name": "playwright-core",
"version": "0.11.0",
"version": "0.11.1-next.1582929239274",
"description": "A high-level API to automate web browsers",

@@ -11,5 +11,5 @@ "repository": "github:Microsoft/playwright",

"playwright": {
"chromium_revision": "740847",
"firefox_revision": "1029",
"webkit_revision": "1150"
"chromium_revision": "745253",
"firefox_revision": "1032",
"webkit_revision": "1162"
},

@@ -25,3 +25,3 @@ "scripts": {

"test-doclint": "node utils/doclint/check_public_api/test/test.js && node utils/doclint/preprocessor/test.js",
"test": "npm run lint --silent && npm run coverage && npm run test-doclint && node utils/testrunner/test/test.js",
"test": "npm run lint --silent && npm run ccoverage && npm run fcoverage && npm run wcoverage && npm run test-doclint && node utils/testrunner/test/test.js",
"prepare": "node prepare.js",

@@ -67,2 +67,3 @@ "lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe --ext js,ts ./src || eslint --ext js,ts ./src) && npm run tsc && npm run doc",

"@typescript-eslint/parser": "^2.6.1",
"colors": "^1.4.0",
"commonmark": "^0.28.1",

@@ -69,0 +70,0 @@ "cross-env": "^5.0.5",

# Playwright
[![npm version](https://img.shields.io/npm/v/playwright.svg?style=flat)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge-if-release -->[![Chromium version](https://img.shields.io/badge/chromium-82.0.4057.0-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge-if-release -->[![Firefox version](https://img.shields.io/badge/firefox-73.0b13-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> [![WebKit version](https://img.shields.io/badge/webkit-13.0.4-blue.svg?logo=safari)](https://webkit.org/) [![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://join.slack.com/t/playwright/shared_invite/enQtOTEyMTUxMzgxMjIwLThjMDUxZmIyNTRiMTJjNjIyMzdmZDA3MTQxZWUwZTFjZjQwNGYxZGM5MzRmNzZlMWI5ZWUyOTkzMjE5Njg1NDg)
###### [API](https://github.com/microsoft/playwright/blob/v0.11.0/docs/api.md) | [FAQ](#faq) | [Contributing](#contributing)
###### [API](https://github.com/microsoft/playwright/blob/v0.11.1/docs/api.md) | [Changelog](https://github.com/microsoft/playwright/releases) | [FAQ](#faq) | [Contributing](#contributing)

@@ -183,2 +183,3 @@

* [API documentation](https://github.com/microsoft/playwright/blob/master/docs/api.md)
* [API documentation](docs/api.md)
* [Community showcase](docs/showcase.md)

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc