mirror of
https://github.com/TheBinaryNinja/tvapp2.git
synced 2026-06-04 10:45:41 -04:00
build: push tvapp v2 docker files
This commit is contained in:
62
node_modules/playwright-core/lib/server/accessibility.js
generated
vendored
Normal file
62
node_modules/playwright-core/lib/server/accessibility.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Accessibility = void 0;
|
||||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Accessibility {
|
||||
constructor(getAXTree) {
|
||||
this._getAXTree = void 0;
|
||||
this._getAXTree = getAXTree;
|
||||
}
|
||||
async snapshot(options = {}) {
|
||||
const {
|
||||
interestingOnly = true,
|
||||
root = null
|
||||
} = options;
|
||||
const {
|
||||
tree,
|
||||
needle
|
||||
} = await this._getAXTree(root || undefined);
|
||||
if (!interestingOnly) {
|
||||
if (root) return needle && serializeTree(needle)[0];
|
||||
return serializeTree(tree)[0];
|
||||
}
|
||||
const interestingNodes = new Set();
|
||||
collectInterestingNodes(interestingNodes, tree, false);
|
||||
if (root && (!needle || !interestingNodes.has(needle))) return null;
|
||||
return serializeTree(needle || tree, interestingNodes)[0];
|
||||
}
|
||||
}
|
||||
exports.Accessibility = Accessibility;
|
||||
function collectInterestingNodes(collection, node, insideControl) {
|
||||
if (node.isInteresting(insideControl)) collection.add(node);
|
||||
if (node.isLeafNode()) return;
|
||||
insideControl = insideControl || node.isControl();
|
||||
for (const child of node.children()) collectInterestingNodes(collection, child, insideControl);
|
||||
}
|
||||
function serializeTree(node, whitelistedNodes) {
|
||||
const children = [];
|
||||
for (const child of node.children()) children.push(...serializeTree(child, whitelistedNodes));
|
||||
if (whitelistedNodes && !whitelistedNodes.has(node)) return children;
|
||||
const serializedNode = node.serialize();
|
||||
if (children.length) serializedNode.children = children;
|
||||
return [serializedNode];
|
||||
}
|
||||
441
node_modules/playwright-core/lib/server/android/android.js
generated
vendored
Normal file
441
node_modules/playwright-core/lib/server/android/android.js
generated
vendored
Normal file
@@ -0,0 +1,441 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.AndroidDevice = exports.Android = void 0;
|
||||
var _utilsBundle = require("../../utilsBundle");
|
||||
var _events = require("events");
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("../../utils");
|
||||
var _fileUtils = require("../../utils/fileUtils");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _progress = require("../progress");
|
||||
var _crBrowser = require("../chromium/crBrowser");
|
||||
var _helper = require("../helper");
|
||||
var _transport = require("../../protocol/transport");
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _processLauncher = require("../../utils/processLauncher");
|
||||
var _timeoutSettings = require("../../common/timeoutSettings");
|
||||
var _instrumentation = require("../instrumentation");
|
||||
var _chromiumSwitches = require("../chromium/chromiumSwitches");
|
||||
var _registry = require("../registry");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const ARTIFACTS_FOLDER = _path.default.join(_os.default.tmpdir(), 'playwright-artifacts-');
|
||||
class Android extends _instrumentation.SdkObject {
|
||||
constructor(parent, backend) {
|
||||
super(parent, 'android');
|
||||
this._backend = void 0;
|
||||
this._devices = new Map();
|
||||
this._timeoutSettings = void 0;
|
||||
this._backend = backend;
|
||||
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||
}
|
||||
setDefaultTimeout(timeout) {
|
||||
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
async devices(options) {
|
||||
const devices = (await this._backend.devices(options)).filter(d => d.status === 'device');
|
||||
const newSerials = new Set();
|
||||
for (const d of devices) {
|
||||
newSerials.add(d.serial);
|
||||
if (this._devices.has(d.serial)) continue;
|
||||
const device = await AndroidDevice.create(this, d, options);
|
||||
this._devices.set(d.serial, device);
|
||||
}
|
||||
for (const d of this._devices.keys()) {
|
||||
if (!newSerials.has(d)) this._devices.delete(d);
|
||||
}
|
||||
return [...this._devices.values()];
|
||||
}
|
||||
_deviceClosed(device) {
|
||||
this._devices.delete(device.serial);
|
||||
}
|
||||
}
|
||||
exports.Android = Android;
|
||||
class AndroidDevice extends _instrumentation.SdkObject {
|
||||
constructor(android, backend, model, options) {
|
||||
super(android, 'android-device');
|
||||
this._backend = void 0;
|
||||
this.model = void 0;
|
||||
this.serial = void 0;
|
||||
this._options = void 0;
|
||||
this._driverPromise = void 0;
|
||||
this._lastId = 0;
|
||||
this._callbacks = new Map();
|
||||
this._pollingWebViews = void 0;
|
||||
this._timeoutSettings = void 0;
|
||||
this._webViews = new Map();
|
||||
this._browserConnections = new Set();
|
||||
this._android = void 0;
|
||||
this._isClosed = false;
|
||||
this._android = android;
|
||||
this._backend = backend;
|
||||
this.model = model;
|
||||
this.serial = backend.serial;
|
||||
this._options = options;
|
||||
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(android._timeoutSettings);
|
||||
}
|
||||
static async create(android, backend, options) {
|
||||
await backend.init();
|
||||
const model = await backend.runCommand('shell:getprop ro.product.model');
|
||||
const device = new AndroidDevice(android, backend, model.toString().trim(), options);
|
||||
await device._init();
|
||||
return device;
|
||||
}
|
||||
async _init() {
|
||||
await this._refreshWebViews();
|
||||
const poll = () => {
|
||||
this._pollingWebViews = setTimeout(() => this._refreshWebViews().then(poll).catch(() => {
|
||||
this.close().catch(() => {});
|
||||
}), 500);
|
||||
};
|
||||
poll();
|
||||
}
|
||||
setDefaultTimeout(timeout) {
|
||||
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
async shell(command) {
|
||||
const result = await this._backend.runCommand(`shell:${command}`);
|
||||
await this._refreshWebViews();
|
||||
return result;
|
||||
}
|
||||
async open(command) {
|
||||
return await this._backend.open(`${command}`);
|
||||
}
|
||||
async screenshot() {
|
||||
return await this._backend.runCommand(`shell:screencap -p`);
|
||||
}
|
||||
async _driver() {
|
||||
if (this._isClosed) return;
|
||||
if (!this._driverPromise) this._driverPromise = this._installDriver();
|
||||
return this._driverPromise;
|
||||
}
|
||||
async _installDriver() {
|
||||
(0, _utilsBundle.debug)('pw:android')('Stopping the old driver');
|
||||
await this.shell(`am force-stop com.microsoft.playwright.androiddriver`);
|
||||
|
||||
// uninstall and install driver on every execution
|
||||
if (!this._options.omitDriverInstall) {
|
||||
(0, _utilsBundle.debug)('pw:android')('Uninstalling the old driver');
|
||||
await this.shell(`cmd package uninstall com.microsoft.playwright.androiddriver`);
|
||||
await this.shell(`cmd package uninstall com.microsoft.playwright.androiddriver.test`);
|
||||
(0, _utilsBundle.debug)('pw:android')('Installing the new driver');
|
||||
const executable = _registry.registry.findExecutable('android');
|
||||
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
|
||||
for (const file of ['android-driver.apk', 'android-driver-target.apk']) {
|
||||
const fullName = _path.default.join(executable.directory, file);
|
||||
if (!_fs.default.existsSync(fullName)) throw new Error(`Please install Android driver apk using '${packageManagerCommand} playwright install android'`);
|
||||
await this.installApk(await _fs.default.promises.readFile(fullName));
|
||||
}
|
||||
} else {
|
||||
(0, _utilsBundle.debug)('pw:android')('Skipping the driver installation');
|
||||
}
|
||||
(0, _utilsBundle.debug)('pw:android')('Starting the new driver');
|
||||
this.shell('am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner').catch(e => (0, _utilsBundle.debug)('pw:android')(e));
|
||||
const socket = await this._waitForLocalAbstract('playwright_android_driver_socket');
|
||||
const transport = new _transport.PipeTransport(socket, socket, socket, 'be');
|
||||
transport.onmessage = message => {
|
||||
const response = JSON.parse(message);
|
||||
const {
|
||||
id,
|
||||
result,
|
||||
error
|
||||
} = response;
|
||||
const callback = this._callbacks.get(id);
|
||||
if (!callback) return;
|
||||
if (error) callback.reject(new Error(error));else callback.fulfill(result);
|
||||
this._callbacks.delete(id);
|
||||
};
|
||||
return transport;
|
||||
}
|
||||
async _waitForLocalAbstract(socketName) {
|
||||
let socket;
|
||||
(0, _utilsBundle.debug)('pw:android')(`Polling the socket localabstract:${socketName}`);
|
||||
while (!socket) {
|
||||
try {
|
||||
socket = await this._backend.open(`localabstract:${socketName}`);
|
||||
} catch (e) {
|
||||
await new Promise(f => setTimeout(f, 250));
|
||||
}
|
||||
}
|
||||
(0, _utilsBundle.debug)('pw:android')(`Connected to localabstract:${socketName}`);
|
||||
return socket;
|
||||
}
|
||||
async send(method, params = {}) {
|
||||
// Patch the timeout in!
|
||||
params.timeout = this._timeoutSettings.timeout(params);
|
||||
const driver = await this._driver();
|
||||
if (!driver) throw new Error('Device is closed');
|
||||
const id = ++this._lastId;
|
||||
const result = new Promise((fulfill, reject) => this._callbacks.set(id, {
|
||||
fulfill,
|
||||
reject
|
||||
}));
|
||||
driver.send(JSON.stringify({
|
||||
id,
|
||||
method,
|
||||
params
|
||||
}));
|
||||
return result;
|
||||
}
|
||||
async close() {
|
||||
if (this._isClosed) return;
|
||||
this._isClosed = true;
|
||||
if (this._pollingWebViews) clearTimeout(this._pollingWebViews);
|
||||
for (const connection of this._browserConnections) await connection.close();
|
||||
if (this._driverPromise) {
|
||||
const driver = await this._driver();
|
||||
driver === null || driver === void 0 || driver.close();
|
||||
}
|
||||
await this._backend.close();
|
||||
this._android._deviceClosed(this);
|
||||
this.emit(AndroidDevice.Events.Close);
|
||||
}
|
||||
async launchBrowser(pkg = 'com.android.chrome', options) {
|
||||
(0, _utilsBundle.debug)('pw:android')('Force-stopping', pkg);
|
||||
await this._backend.runCommand(`shell:am force-stop ${pkg}`);
|
||||
const socketName = (0, _utils.isUnderTest)() ? 'webview_devtools_remote_playwright_test' : 'playwright_' + (0, _utils.createGuid)() + '_devtools_remote';
|
||||
const commandLine = this._defaultArgs(options, socketName).join(' ');
|
||||
(0, _utilsBundle.debug)('pw:android')('Starting', pkg, commandLine);
|
||||
// encode commandLine to base64 to avoid issues (bash encoding) with special characters
|
||||
await this._backend.runCommand(`shell:echo "${Buffer.from(commandLine).toString('base64')}" | base64 -d > /data/local/tmp/chrome-command-line`);
|
||||
await this._backend.runCommand(`shell:am start -a android.intent.action.VIEW -d about:blank ${pkg}`);
|
||||
const browserContext = await this._connectToBrowser(socketName, options);
|
||||
await this._backend.runCommand(`shell:rm /data/local/tmp/chrome-command-line`);
|
||||
return browserContext;
|
||||
}
|
||||
_defaultArgs(options, socketName) {
|
||||
const chromeArguments = ['_', '--disable-fre', '--no-default-browser-check', `--remote-debugging-socket-name=${socketName}`, ..._chromiumSwitches.chromiumSwitches, ...this._innerDefaultArgs(options)];
|
||||
return chromeArguments;
|
||||
}
|
||||
_innerDefaultArgs(options) {
|
||||
const {
|
||||
args = [],
|
||||
proxy
|
||||
} = options;
|
||||
const chromeArguments = [];
|
||||
if (proxy) {
|
||||
chromeArguments.push(`--proxy-server=${proxy.server}`);
|
||||
const proxyBypassRules = [];
|
||||
if (proxy.bypass) proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
|
||||
if (!process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK && !proxyBypassRules.includes('<-loopback>')) proxyBypassRules.push('<-loopback>');
|
||||
if (proxyBypassRules.length > 0) chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(';')}`);
|
||||
}
|
||||
chromeArguments.push(...args);
|
||||
return chromeArguments;
|
||||
}
|
||||
async connectToWebView(socketName) {
|
||||
const webView = this._webViews.get(socketName);
|
||||
if (!webView) throw new Error('WebView has been closed');
|
||||
return await this._connectToBrowser(socketName);
|
||||
}
|
||||
async _connectToBrowser(socketName, options = {}) {
|
||||
const socket = await this._waitForLocalAbstract(socketName);
|
||||
const androidBrowser = new AndroidBrowser(this, socket);
|
||||
await androidBrowser._init();
|
||||
this._browserConnections.add(androidBrowser);
|
||||
const artifactsDir = await _fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
const cleanupArtifactsDir = async () => {
|
||||
const errors = await (0, _fileUtils.removeFolders)([artifactsDir]);
|
||||
for (let i = 0; i < (errors || []).length; ++i) (0, _utilsBundle.debug)('pw:android')(`exception while removing ${artifactsDir}: ${errors[i]}`);
|
||||
};
|
||||
_processLauncher.gracefullyCloseSet.add(cleanupArtifactsDir);
|
||||
socket.on('close', async () => {
|
||||
_processLauncher.gracefullyCloseSet.delete(cleanupArtifactsDir);
|
||||
cleanupArtifactsDir().catch(e => (0, _utilsBundle.debug)('pw:android')(`could not cleanup artifacts dir: ${e}`));
|
||||
});
|
||||
const browserOptions = {
|
||||
name: 'clank',
|
||||
isChromium: true,
|
||||
slowMo: 0,
|
||||
persistent: {
|
||||
...options,
|
||||
noDefaultViewport: true
|
||||
},
|
||||
artifactsDir,
|
||||
downloadsPath: artifactsDir,
|
||||
tracesDir: artifactsDir,
|
||||
browserProcess: new ClankBrowserProcess(androidBrowser),
|
||||
proxy: options.proxy,
|
||||
protocolLogger: _helper.helper.debugProtocolLogger(),
|
||||
browserLogsCollector: new _debugLogger.RecentLogsCollector(),
|
||||
originalLaunchOptions: {}
|
||||
};
|
||||
(0, _browserContext.validateBrowserContextOptions)(options, browserOptions);
|
||||
const browser = await _crBrowser.CRBrowser.connect(this.attribution.playwright, androidBrowser, browserOptions);
|
||||
const controller = new _progress.ProgressController((0, _instrumentation.serverSideCallMetadata)(), this);
|
||||
const defaultContext = browser._defaultContext;
|
||||
await controller.run(async progress => {
|
||||
await defaultContext._loadDefaultContextAsIs(progress);
|
||||
});
|
||||
return defaultContext;
|
||||
}
|
||||
webViews() {
|
||||
return [...this._webViews.values()];
|
||||
}
|
||||
async installApk(content, options) {
|
||||
const args = options && options.args ? options.args : ['-r', '-t', '-S'];
|
||||
(0, _utilsBundle.debug)('pw:android')('Opening install socket');
|
||||
const installSocket = await this._backend.open(`shell:cmd package install ${args.join(' ')} ${content.length}`);
|
||||
(0, _utilsBundle.debug)('pw:android')('Writing driver bytes: ' + content.length);
|
||||
await installSocket.write(content);
|
||||
const success = await new Promise(f => installSocket.on('data', f));
|
||||
(0, _utilsBundle.debug)('pw:android')('Written driver bytes: ' + success);
|
||||
installSocket.close();
|
||||
}
|
||||
async push(content, path, mode = 0o644) {
|
||||
const socket = await this._backend.open(`sync:`);
|
||||
const sendHeader = async (command, length) => {
|
||||
const buffer = Buffer.alloc(command.length + 4);
|
||||
buffer.write(command, 0);
|
||||
buffer.writeUInt32LE(length, command.length);
|
||||
await socket.write(buffer);
|
||||
};
|
||||
const send = async (command, data) => {
|
||||
await sendHeader(command, data.length);
|
||||
await socket.write(data);
|
||||
};
|
||||
await send('SEND', Buffer.from(`${path},${mode}`));
|
||||
const maxChunk = 65535;
|
||||
for (let i = 0; i < content.length; i += maxChunk) await send('DATA', content.slice(i, i + maxChunk));
|
||||
await sendHeader('DONE', Date.now() / 1000 | 0);
|
||||
const result = await new Promise(f => socket.once('data', f));
|
||||
const code = result.slice(0, 4).toString();
|
||||
if (code !== 'OKAY') throw new Error('Could not push: ' + code);
|
||||
socket.close();
|
||||
}
|
||||
async _refreshWebViews() {
|
||||
// possible socketName, eg: webview_devtools_remote_32327, webview_devtools_remote_32327_zeus, webview_devtools_remote_zeus
|
||||
const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).toString().split('\n');
|
||||
if (this._isClosed) return;
|
||||
const socketNames = new Set();
|
||||
for (const line of sockets) {
|
||||
const matchSocketName = line.match(/[^@]+@(.*?webview_devtools_remote_?.*)/);
|
||||
if (!matchSocketName) continue;
|
||||
const socketName = matchSocketName[1];
|
||||
socketNames.add(socketName);
|
||||
if (this._webViews.has(socketName)) continue;
|
||||
|
||||
// possible line: 0000000000000000: 00000002 00000000 00010000 0001 01 5841881 @webview_devtools_remote_zeus
|
||||
// the result: match[1] = ''
|
||||
const match = line.match(/[^@]+@.*?webview_devtools_remote_?(\d*)/);
|
||||
let pid = -1;
|
||||
if (match && match[1]) pid = +match[1];
|
||||
const pkg = await this._extractPkg(pid);
|
||||
if (this._isClosed) return;
|
||||
const webView = {
|
||||
pid,
|
||||
pkg,
|
||||
socketName
|
||||
};
|
||||
this._webViews.set(socketName, webView);
|
||||
this.emit(AndroidDevice.Events.WebViewAdded, webView);
|
||||
}
|
||||
for (const p of this._webViews.keys()) {
|
||||
if (!socketNames.has(p)) {
|
||||
this._webViews.delete(p);
|
||||
this.emit(AndroidDevice.Events.WebViewRemoved, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
async _extractPkg(pid) {
|
||||
let pkg = '';
|
||||
if (pid === -1) return pkg;
|
||||
const procs = (await this._backend.runCommand(`shell:ps -A | grep ${pid}`)).toString().split('\n');
|
||||
for (const proc of procs) {
|
||||
const match = proc.match(/[^\s]+\s+(\d+).*$/);
|
||||
if (!match) continue;
|
||||
pkg = proc.substring(proc.lastIndexOf(' ') + 1);
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
}
|
||||
exports.AndroidDevice = AndroidDevice;
|
||||
AndroidDevice.Events = {
|
||||
WebViewAdded: 'webViewAdded',
|
||||
WebViewRemoved: 'webViewRemoved',
|
||||
Close: 'close'
|
||||
};
|
||||
class AndroidBrowser extends _events.EventEmitter {
|
||||
constructor(device, socket) {
|
||||
super();
|
||||
this.device = void 0;
|
||||
this._socket = void 0;
|
||||
this._receiver = void 0;
|
||||
this._waitForNextTask = (0, _utils.makeWaitForNextTask)();
|
||||
this.onmessage = void 0;
|
||||
this.onclose = void 0;
|
||||
this.setMaxListeners(0);
|
||||
this.device = device;
|
||||
this._socket = socket;
|
||||
this._socket.on('close', () => {
|
||||
this._waitForNextTask(() => {
|
||||
if (this.onclose) this.onclose();
|
||||
});
|
||||
});
|
||||
this._receiver = new _utilsBundle.wsReceiver();
|
||||
this._receiver.on('message', message => {
|
||||
this._waitForNextTask(() => {
|
||||
if (this.onmessage) this.onmessage(JSON.parse(message));
|
||||
});
|
||||
});
|
||||
}
|
||||
async _init() {
|
||||
await this._socket.write(Buffer.from(`GET /devtools/browser HTTP/1.1\r
|
||||
Upgrade: WebSocket\r
|
||||
Connection: Upgrade\r
|
||||
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
||||
Sec-WebSocket-Version: 13\r
|
||||
\r
|
||||
`));
|
||||
// HTTP Upgrade response.
|
||||
await new Promise(f => this._socket.once('data', f));
|
||||
|
||||
// Start sending web frame to receiver.
|
||||
this._socket.on('data', data => this._receiver._write(data, 'binary', () => {}));
|
||||
}
|
||||
async send(s) {
|
||||
await this._socket.write(encodeWebFrame(JSON.stringify(s)));
|
||||
}
|
||||
async close() {
|
||||
this._socket.close();
|
||||
}
|
||||
}
|
||||
function encodeWebFrame(data) {
|
||||
return _utilsBundle.wsSender.frame(Buffer.from(data), {
|
||||
opcode: 1,
|
||||
mask: true,
|
||||
fin: true,
|
||||
readOnly: true
|
||||
})[0];
|
||||
}
|
||||
class ClankBrowserProcess {
|
||||
constructor(browser) {
|
||||
this._browser = void 0;
|
||||
this.onclose = void 0;
|
||||
this._browser = browser;
|
||||
}
|
||||
async kill() {}
|
||||
async close() {
|
||||
await this._browser.close();
|
||||
}
|
||||
}
|
||||
172
node_modules/playwright-core/lib/server/android/backendAdb.js
generated
vendored
Normal file
172
node_modules/playwright-core/lib/server/android/backendAdb.js
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.AdbBackend = void 0;
|
||||
var _utilsBundle = require("../../utilsBundle");
|
||||
var net = _interopRequireWildcard(require("net"));
|
||||
var _events = require("events");
|
||||
var _utils = require("../../utils");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class AdbBackend {
|
||||
async devices(options = {}) {
|
||||
const result = await runCommand('host:devices', options.host, options.port);
|
||||
const lines = result.toString().trim().split('\n');
|
||||
return lines.map(line => {
|
||||
const [serial, status] = line.trim().split('\t');
|
||||
return new AdbDevice(serial, status, options.host, options.port);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.AdbBackend = AdbBackend;
|
||||
class AdbDevice {
|
||||
constructor(serial, status, host, port) {
|
||||
this.serial = void 0;
|
||||
this.status = void 0;
|
||||
this.host = void 0;
|
||||
this.port = void 0;
|
||||
this._closed = false;
|
||||
this.serial = serial;
|
||||
this.status = status;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
async init() {}
|
||||
async close() {
|
||||
this._closed = true;
|
||||
}
|
||||
runCommand(command) {
|
||||
if (this._closed) throw new Error('Device is closed');
|
||||
return runCommand(command, this.host, this.port, this.serial);
|
||||
}
|
||||
async open(command) {
|
||||
if (this._closed) throw new Error('Device is closed');
|
||||
const result = await open(command, this.host, this.port, this.serial);
|
||||
result.becomeSocket();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
async function runCommand(command, host = '127.0.0.1', port = 5037, serial) {
|
||||
(0, _utilsBundle.debug)('pw:adb:runCommand')(command, serial);
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({
|
||||
host,
|
||||
port
|
||||
}));
|
||||
try {
|
||||
if (serial) {
|
||||
await socket.write(encodeMessage(`host:transport:${serial}`));
|
||||
const status = await socket.read(4);
|
||||
(0, _utils.assert)(status.toString() === 'OKAY', status.toString());
|
||||
}
|
||||
await socket.write(encodeMessage(command));
|
||||
const status = await socket.read(4);
|
||||
(0, _utils.assert)(status.toString() === 'OKAY', status.toString());
|
||||
let commandOutput;
|
||||
if (!command.startsWith('shell:')) {
|
||||
const remainingLength = parseInt((await socket.read(4)).toString(), 16);
|
||||
commandOutput = await socket.read(remainingLength);
|
||||
} else {
|
||||
commandOutput = await socket.readAll();
|
||||
}
|
||||
return commandOutput;
|
||||
} finally {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
async function open(command, host = '127.0.0.1', port = 5037, serial) {
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({
|
||||
host,
|
||||
port
|
||||
}));
|
||||
if (serial) {
|
||||
await socket.write(encodeMessage(`host:transport:${serial}`));
|
||||
const status = await socket.read(4);
|
||||
(0, _utils.assert)(status.toString() === 'OKAY', status.toString());
|
||||
}
|
||||
await socket.write(encodeMessage(command));
|
||||
const status = await socket.read(4);
|
||||
(0, _utils.assert)(status.toString() === 'OKAY', status.toString());
|
||||
return socket;
|
||||
}
|
||||
function encodeMessage(message) {
|
||||
let lenHex = message.length.toString(16);
|
||||
lenHex = '0'.repeat(4 - lenHex.length) + lenHex;
|
||||
return Buffer.from(lenHex + message);
|
||||
}
|
||||
class BufferedSocketWrapper extends _events.EventEmitter {
|
||||
constructor(command, socket) {
|
||||
super();
|
||||
this.guid = (0, _utils.createGuid)();
|
||||
this._socket = void 0;
|
||||
this._buffer = Buffer.from([]);
|
||||
this._isSocket = false;
|
||||
this._notifyReader = void 0;
|
||||
this._connectPromise = void 0;
|
||||
this._isClosed = false;
|
||||
this._command = void 0;
|
||||
this._command = command;
|
||||
this._socket = socket;
|
||||
this._connectPromise = new Promise(f => this._socket.on('connect', f));
|
||||
this._socket.on('data', data => {
|
||||
(0, _utilsBundle.debug)('pw:adb:data')(data.toString());
|
||||
if (this._isSocket) {
|
||||
this.emit('data', data);
|
||||
return;
|
||||
}
|
||||
this._buffer = Buffer.concat([this._buffer, data]);
|
||||
if (this._notifyReader) this._notifyReader();
|
||||
});
|
||||
this._socket.on('close', () => {
|
||||
this._isClosed = true;
|
||||
if (this._notifyReader) this._notifyReader();
|
||||
this.close();
|
||||
this.emit('close');
|
||||
});
|
||||
this._socket.on('error', error => this.emit('error', error));
|
||||
}
|
||||
async write(data) {
|
||||
(0, _utilsBundle.debug)('pw:adb:send')(data.toString().substring(0, 100) + '...');
|
||||
await this._connectPromise;
|
||||
await new Promise(f => this._socket.write(data, f));
|
||||
}
|
||||
close() {
|
||||
if (this._isClosed) return;
|
||||
(0, _utilsBundle.debug)('pw:adb')('Close ' + this._command);
|
||||
this._socket.destroy();
|
||||
}
|
||||
async read(length) {
|
||||
await this._connectPromise;
|
||||
(0, _utils.assert)(!this._isSocket, 'Can not read by length in socket mode');
|
||||
while (this._buffer.length < length) await new Promise(f => this._notifyReader = f);
|
||||
const result = this._buffer.slice(0, length);
|
||||
this._buffer = this._buffer.slice(length);
|
||||
(0, _utilsBundle.debug)('pw:adb:recv')(result.toString().substring(0, 100) + '...');
|
||||
return result;
|
||||
}
|
||||
async readAll() {
|
||||
while (!this._isClosed) await new Promise(f => this._notifyReader = f);
|
||||
return this._buffer;
|
||||
}
|
||||
becomeSocket() {
|
||||
(0, _utils.assert)(!this._buffer.length);
|
||||
this._isSocket = true;
|
||||
}
|
||||
}
|
||||
33
node_modules/playwright-core/lib/server/ariaSnapshot.js
generated
vendored
Normal file
33
node_modules/playwright-core/lib/server/ariaSnapshot.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.parseAriaSnapshot = parseAriaSnapshot;
|
||||
exports.parseYamlForAriaSnapshot = parseYamlForAriaSnapshot;
|
||||
var _ariaSnapshot = require("../utils/isomorphic/ariaSnapshot");
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function parseAriaSnapshot(text) {
|
||||
return (0, _ariaSnapshot.parseYamlTemplate)(parseYamlForAriaSnapshot(text));
|
||||
}
|
||||
function parseYamlForAriaSnapshot(text) {
|
||||
const parsed = _utilsBundle.yaml.parse(text);
|
||||
if (!Array.isArray(parsed)) throw new Error('Expected object key starting with "- ":\n\n' + text + '\n');
|
||||
return parsed;
|
||||
}
|
||||
104
node_modules/playwright-core/lib/server/artifact.js
generated
vendored
Normal file
104
node_modules/playwright-core/lib/server/artifact.js
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Artifact = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _utils = require("../utils");
|
||||
var _manualPromise = require("../utils/manualPromise");
|
||||
var _instrumentation = require("./instrumentation");
|
||||
var _errors = require("./errors");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Artifact extends _instrumentation.SdkObject {
|
||||
constructor(parent, localPath, unaccessibleErrorMessage, cancelCallback) {
|
||||
super(parent, 'artifact');
|
||||
this._localPath = void 0;
|
||||
this._unaccessibleErrorMessage = void 0;
|
||||
this._cancelCallback = void 0;
|
||||
this._finishedPromise = new _manualPromise.ManualPromise();
|
||||
this._saveCallbacks = [];
|
||||
this._finished = false;
|
||||
this._deleted = false;
|
||||
this._failureError = void 0;
|
||||
this._localPath = localPath;
|
||||
this._unaccessibleErrorMessage = unaccessibleErrorMessage;
|
||||
this._cancelCallback = cancelCallback;
|
||||
}
|
||||
finishedPromise() {
|
||||
return this._finishedPromise;
|
||||
}
|
||||
localPath() {
|
||||
return this._localPath;
|
||||
}
|
||||
async localPathAfterFinished() {
|
||||
if (this._unaccessibleErrorMessage) throw new Error(this._unaccessibleErrorMessage);
|
||||
await this._finishedPromise;
|
||||
if (this._failureError) throw this._failureError;
|
||||
return this._localPath;
|
||||
}
|
||||
saveAs(saveCallback) {
|
||||
if (this._unaccessibleErrorMessage) throw new Error(this._unaccessibleErrorMessage);
|
||||
if (this._deleted) throw new Error(`File already deleted. Save before deleting.`);
|
||||
if (this._failureError) throw this._failureError;
|
||||
if (this._finished) {
|
||||
saveCallback(this._localPath).catch(() => {});
|
||||
return;
|
||||
}
|
||||
this._saveCallbacks.push(saveCallback);
|
||||
}
|
||||
async failureError() {
|
||||
var _this$_failureError;
|
||||
if (this._unaccessibleErrorMessage) return this._unaccessibleErrorMessage;
|
||||
await this._finishedPromise;
|
||||
return ((_this$_failureError = this._failureError) === null || _this$_failureError === void 0 ? void 0 : _this$_failureError.message) || null;
|
||||
}
|
||||
async cancel() {
|
||||
(0, _utils.assert)(this._cancelCallback !== undefined);
|
||||
return this._cancelCallback();
|
||||
}
|
||||
async delete() {
|
||||
if (this._unaccessibleErrorMessage) return;
|
||||
const fileName = await this.localPathAfterFinished();
|
||||
if (this._deleted) return;
|
||||
this._deleted = true;
|
||||
if (fileName) await _fs.default.promises.unlink(fileName).catch(e => {});
|
||||
}
|
||||
async deleteOnContextClose() {
|
||||
// Compared to "delete", this method does not wait for the artifact to finish.
|
||||
// We use it when closing the context to avoid stalling.
|
||||
if (this._deleted) return;
|
||||
this._deleted = true;
|
||||
if (!this._unaccessibleErrorMessage) await _fs.default.promises.unlink(this._localPath).catch(e => {});
|
||||
await this.reportFinished(new _errors.TargetClosedError());
|
||||
}
|
||||
async reportFinished(error) {
|
||||
if (this._finished) return;
|
||||
this._finished = true;
|
||||
this._failureError = error;
|
||||
if (error) {
|
||||
for (const callback of this._saveCallbacks) await callback('', error);
|
||||
} else {
|
||||
for (const callback of this._saveCallbacks) await callback(this._localPath);
|
||||
}
|
||||
this._saveCallbacks = [];
|
||||
this._finishedPromise.resolve();
|
||||
}
|
||||
}
|
||||
exports.Artifact = Artifact;
|
||||
311
node_modules/playwright-core/lib/server/bidi/bidiBrowser.js
generated
vendored
Normal file
311
node_modules/playwright-core/lib/server/bidi/bidiBrowser.js
generated
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Network = exports.BidiBrowserContext = exports.BidiBrowser = void 0;
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var _browser = require("../browser");
|
||||
var _browserContext = require("../browserContext");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
var _bidiConnection = require("./bidiConnection");
|
||||
var _bidiNetworkManager = require("./bidiNetworkManager");
|
||||
var _bidiPage = require("./bidiPage");
|
||||
var bidi = _interopRequireWildcard(require("./third_party/bidiProtocol"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BidiBrowser extends _browser.Browser {
|
||||
static async connect(parent, transport, options) {
|
||||
const browser = new BidiBrowser(parent, transport, options);
|
||||
if (options.__testHookOnConnectToBrowser) await options.__testHookOnConnectToBrowser();
|
||||
let proxy;
|
||||
if (options.proxy) {
|
||||
proxy = {
|
||||
proxyType: 'manual'
|
||||
};
|
||||
const url = new URL(options.proxy.server); // Validate proxy server.
|
||||
switch (url.protocol) {
|
||||
case 'http:':
|
||||
proxy.httpProxy = url.host;
|
||||
break;
|
||||
case 'https:':
|
||||
proxy.httpsProxy = url.host;
|
||||
break;
|
||||
case 'socks4:':
|
||||
proxy.socksProxy = url.host;
|
||||
proxy.socksVersion = 4;
|
||||
break;
|
||||
case 'socks5:':
|
||||
proxy.socksProxy = url.host;
|
||||
proxy.socksVersion = 5;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid proxy server protocol: ' + options.proxy.server);
|
||||
}
|
||||
if (options.proxy.bypass) proxy.noProxy = options.proxy.bypass.split(',');
|
||||
// TODO: support authentication.
|
||||
}
|
||||
browser._bidiSessionInfo = await browser._browserSession.send('session.new', {
|
||||
capabilities: {
|
||||
alwaysMatch: {
|
||||
acceptInsecureCerts: false,
|
||||
proxy,
|
||||
unhandledPromptBehavior: {
|
||||
default: bidi.Session.UserPromptHandlerType.Ignore
|
||||
},
|
||||
webSocketUrl: true
|
||||
}
|
||||
}
|
||||
});
|
||||
await browser._browserSession.send('session.subscribe', {
|
||||
events: ['browsingContext', 'network', 'log', 'script']
|
||||
});
|
||||
if (options.persistent) {
|
||||
browser._defaultContext = new BidiBrowserContext(browser, undefined, options.persistent);
|
||||
await browser._defaultContext._initialize();
|
||||
// Create default page as we cannot get access to the existing one.
|
||||
const pageDelegate = await browser._defaultContext.newPageDelegate();
|
||||
await pageDelegate.pageOrError();
|
||||
}
|
||||
return browser;
|
||||
}
|
||||
constructor(parent, transport, options) {
|
||||
super(parent, options);
|
||||
this._connection = void 0;
|
||||
this._browserSession = void 0;
|
||||
this._bidiSessionInfo = void 0;
|
||||
this._contexts = new Map();
|
||||
this._bidiPages = new Map();
|
||||
this._eventListeners = void 0;
|
||||
this._connection = new _bidiConnection.BidiConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector);
|
||||
this._browserSession = this._connection.browserSession;
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._browserSession, 'browsingContext.contextCreated', this._onBrowsingContextCreated.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._browserSession, 'script.realmDestroyed', this._onScriptRealmDestroyed.bind(this))];
|
||||
}
|
||||
_onDisconnect() {
|
||||
this._didClose();
|
||||
}
|
||||
async doCreateNewContext(options) {
|
||||
const {
|
||||
userContext
|
||||
} = await this._browserSession.send('browser.createUserContext', {});
|
||||
const context = new BidiBrowserContext(this, userContext, options);
|
||||
await context._initialize();
|
||||
this._contexts.set(userContext, context);
|
||||
return context;
|
||||
}
|
||||
contexts() {
|
||||
return Array.from(this._contexts.values());
|
||||
}
|
||||
version() {
|
||||
return this._bidiSessionInfo.capabilities.browserVersion;
|
||||
}
|
||||
userAgent() {
|
||||
return this._bidiSessionInfo.capabilities.userAgent;
|
||||
}
|
||||
isConnected() {
|
||||
return !this._connection.isClosed();
|
||||
}
|
||||
_onBrowsingContextCreated(event) {
|
||||
if (event.parent) {
|
||||
const parentFrameId = event.parent;
|
||||
for (const page of this._bidiPages.values()) {
|
||||
const parentFrame = page._page._frameManager.frame(parentFrameId);
|
||||
if (!parentFrame) continue;
|
||||
page._session.addFrameBrowsingContext(event.context);
|
||||
page._page._frameManager.frameAttached(event.context, parentFrameId);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
let context = this._contexts.get(event.userContext);
|
||||
if (!context) context = this._defaultContext;
|
||||
if (!context) return;
|
||||
const session = this._connection.createMainFrameBrowsingContextSession(event.context);
|
||||
const opener = event.originalOpener && this._bidiPages.get(event.originalOpener);
|
||||
const page = new _bidiPage.BidiPage(context, session, opener || null);
|
||||
this._bidiPages.set(event.context, page);
|
||||
}
|
||||
_onBrowsingContextDestroyed(event) {
|
||||
if (event.parent) {
|
||||
this._browserSession.removeFrameBrowsingContext(event.context);
|
||||
const parentFrameId = event.parent;
|
||||
for (const page of this._bidiPages.values()) {
|
||||
const parentFrame = page._page._frameManager.frame(parentFrameId);
|
||||
if (!parentFrame) continue;
|
||||
page._page._frameManager.frameDetached(event.context);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const bidiPage = this._bidiPages.get(event.context);
|
||||
if (!bidiPage) return;
|
||||
bidiPage.didClose();
|
||||
this._bidiPages.delete(event.context);
|
||||
}
|
||||
_onScriptRealmDestroyed(event) {
|
||||
for (const page of this._bidiPages.values()) {
|
||||
if (page._onRealmDestroyed(event)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.BidiBrowser = BidiBrowser;
|
||||
class BidiBrowserContext extends _browserContext.BrowserContext {
|
||||
constructor(browser, browserContextId, options) {
|
||||
super(browser, options, browserContextId);
|
||||
this._authenticateProxyViaHeader();
|
||||
}
|
||||
_bidiPages() {
|
||||
return [...this._browser._bidiPages.values()].filter(bidiPage => bidiPage._browserContext === this);
|
||||
}
|
||||
pages() {
|
||||
return this._bidiPages().map(bidiPage => bidiPage._initializedPage).filter(Boolean);
|
||||
}
|
||||
async newPageDelegate() {
|
||||
(0, _browserContext.assertBrowserContextIsNotOwned)(this);
|
||||
const {
|
||||
context
|
||||
} = await this._browser._browserSession.send('browsingContext.create', {
|
||||
type: bidi.BrowsingContext.CreateType.Window,
|
||||
userContext: this._browserContextId
|
||||
});
|
||||
return this._browser._bidiPages.get(context);
|
||||
}
|
||||
async doGetCookies(urls) {
|
||||
const {
|
||||
cookies
|
||||
} = await this._browser._browserSession.send('storage.getCookies', {
|
||||
partition: {
|
||||
type: 'storageKey',
|
||||
userContext: this._browserContextId
|
||||
}
|
||||
});
|
||||
return network.filterCookies(cookies.map(c => {
|
||||
var _c$expiry;
|
||||
const copy = {
|
||||
name: c.name,
|
||||
value: (0, _bidiNetworkManager.bidiBytesValueToString)(c.value),
|
||||
domain: c.domain,
|
||||
path: c.path,
|
||||
httpOnly: c.httpOnly,
|
||||
secure: c.secure,
|
||||
expires: (_c$expiry = c.expiry) !== null && _c$expiry !== void 0 ? _c$expiry : -1,
|
||||
sameSite: c.sameSite ? fromBidiSameSite(c.sameSite) : 'None'
|
||||
};
|
||||
return copy;
|
||||
}), urls);
|
||||
}
|
||||
async addCookies(cookies) {
|
||||
cookies = network.rewriteCookies(cookies);
|
||||
const promises = cookies.map(c => {
|
||||
const cookie = {
|
||||
name: c.name,
|
||||
value: {
|
||||
type: 'string',
|
||||
value: c.value
|
||||
},
|
||||
domain: c.domain,
|
||||
path: c.path,
|
||||
httpOnly: c.httpOnly,
|
||||
secure: c.secure,
|
||||
sameSite: c.sameSite && toBidiSameSite(c.sameSite),
|
||||
expiry: c.expires === -1 || c.expires === undefined ? undefined : Math.round(c.expires)
|
||||
};
|
||||
return this._browser._browserSession.send('storage.setCookie', {
|
||||
cookie,
|
||||
partition: {
|
||||
type: 'storageKey',
|
||||
userContext: this._browserContextId
|
||||
}
|
||||
});
|
||||
});
|
||||
await Promise.all(promises);
|
||||
}
|
||||
async doClearCookies() {
|
||||
await this._browser._browserSession.send('storage.deleteCookies', {
|
||||
partition: {
|
||||
type: 'storageKey',
|
||||
userContext: this._browserContextId
|
||||
}
|
||||
});
|
||||
}
|
||||
async doGrantPermissions(origin, permissions) {}
|
||||
async doClearPermissions() {}
|
||||
async setGeolocation(geolocation) {}
|
||||
async setExtraHTTPHeaders(headers) {}
|
||||
async setUserAgent(userAgent) {}
|
||||
async setOffline(offline) {}
|
||||
async doSetHTTPCredentials(httpCredentials) {
|
||||
this._options.httpCredentials = httpCredentials;
|
||||
for (const page of this.pages()) await page._delegate.updateHttpCredentials();
|
||||
}
|
||||
async doAddInitScript(initScript) {
|
||||
await Promise.all(this.pages().map(page => page._delegate.addInitScript(initScript)));
|
||||
}
|
||||
async doRemoveNonInternalInitScripts() {}
|
||||
async doUpdateRequestInterception() {}
|
||||
onClosePersistent() {}
|
||||
async clearCache() {}
|
||||
async doClose(reason) {
|
||||
if (!this._browserContextId) {
|
||||
// Closing persistent context should close the browser.
|
||||
await this._browser.close({
|
||||
reason
|
||||
});
|
||||
return;
|
||||
}
|
||||
await this._browser._browserSession.send('browser.removeUserContext', {
|
||||
userContext: this._browserContextId
|
||||
});
|
||||
this._browser._contexts.delete(this._browserContextId);
|
||||
}
|
||||
async cancelDownload(uuid) {}
|
||||
}
|
||||
exports.BidiBrowserContext = BidiBrowserContext;
|
||||
function fromBidiSameSite(sameSite) {
|
||||
switch (sameSite) {
|
||||
case 'strict':
|
||||
return 'Strict';
|
||||
case 'lax':
|
||||
return 'Lax';
|
||||
case 'none':
|
||||
return 'None';
|
||||
}
|
||||
return 'None';
|
||||
}
|
||||
function toBidiSameSite(sameSite) {
|
||||
switch (sameSite) {
|
||||
case 'Strict':
|
||||
return bidi.Network.SameSite.Strict;
|
||||
case 'Lax':
|
||||
return bidi.Network.SameSite.Lax;
|
||||
case 'None':
|
||||
return bidi.Network.SameSite.None;
|
||||
}
|
||||
return bidi.Network.SameSite.None;
|
||||
}
|
||||
let Network = exports.Network = void 0;
|
||||
(function (_Network) {
|
||||
let SameSite = /*#__PURE__*/function (SameSite) {
|
||||
SameSite["Strict"] = "strict";
|
||||
SameSite["Lax"] = "lax";
|
||||
SameSite["None"] = "none";
|
||||
return SameSite;
|
||||
}({});
|
||||
_Network.SameSite = SameSite;
|
||||
})(Network || (exports.Network = Network = {}));
|
||||
124
node_modules/playwright-core/lib/server/bidi/bidiChromium.js
generated
vendored
Normal file
124
node_modules/playwright-core/lib/server/bidi/bidiChromium.js
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiChromium = void 0;
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _utils = require("../../utils");
|
||||
var _browserType = require("../browserType");
|
||||
var _chromiumSwitches = require("../chromium/chromiumSwitches");
|
||||
var _bidiBrowser = require("./bidiBrowser");
|
||||
var _bidiConnection = require("./bidiConnection");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BidiChromium extends _browserType.BrowserType {
|
||||
constructor(parent) {
|
||||
super(parent, 'bidi');
|
||||
this._useBidi = true;
|
||||
}
|
||||
async connectToTransport(transport, options) {
|
||||
// Chrome doesn't support Bidi, we create Bidi over CDP which is used by Chrome driver.
|
||||
// bidiOverCdp depends on chromium-bidi which we only have in devDependencies, so
|
||||
// we load bidiOverCdp dynamically.
|
||||
const bidiTransport = await require('./bidiOverCdp').connectBidiOverCdp(transport);
|
||||
transport[kBidiOverCdpWrapper] = bidiTransport;
|
||||
return _bidiBrowser.BidiBrowser.connect(this.attribution.playwright, bidiTransport, options);
|
||||
}
|
||||
doRewriteStartupLog(error) {
|
||||
if (!error.logs) return error;
|
||||
if (error.logs.includes('Missing X server')) error.logs = '\n' + (0, _utils.wrapInASCIIBox)(_browserType.kNoXServerRunningError, 1);
|
||||
// These error messages are taken from Chromium source code as of July, 2020:
|
||||
// https://github.com/chromium/chromium/blob/70565f67e79f79e17663ad1337dc6e63ee207ce9/content/browser/zygote_host/zygote_host_impl_linux.cc
|
||||
if (!error.logs.includes('crbug.com/357670') && !error.logs.includes('No usable sandbox!') && !error.logs.includes('crbug.com/638180')) return error;
|
||||
error.logs = [`Chromium sandboxing failed!`, `================================`, `To avoid the sandboxing issue, do either of the following:`, ` - (preferred): Configure your environment to support sandboxing`, ` - (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option`, `================================`, ``].join('\n');
|
||||
return error;
|
||||
}
|
||||
amendEnvironment(env, userDataDir, executable, browserArguments) {
|
||||
return env;
|
||||
}
|
||||
attemptToGracefullyCloseBrowser(transport) {
|
||||
const bidiTransport = transport[kBidiOverCdpWrapper];
|
||||
if (bidiTransport) transport = bidiTransport;
|
||||
transport.send({
|
||||
method: 'browser.close',
|
||||
params: {},
|
||||
id: _bidiConnection.kBrowserCloseMessageId
|
||||
});
|
||||
}
|
||||
defaultArgs(options, isPersistent, userDataDir) {
|
||||
const chromeArguments = this._innerDefaultArgs(options);
|
||||
chromeArguments.push(`--user-data-dir=${userDataDir}`);
|
||||
chromeArguments.push('--remote-debugging-port=0');
|
||||
if (isPersistent) chromeArguments.push('about:blank');else chromeArguments.push('--no-startup-window');
|
||||
return chromeArguments;
|
||||
}
|
||||
readyState(options) {
|
||||
(0, _utils.assert)(options.useWebSocket);
|
||||
return new ChromiumReadyState();
|
||||
}
|
||||
_innerDefaultArgs(options) {
|
||||
const {
|
||||
args = []
|
||||
} = options;
|
||||
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
|
||||
if (userDataDirArg) throw this._createUserDataDirArgMisuseError('--user-data-dir');
|
||||
if (args.find(arg => arg.startsWith('--remote-debugging-pipe'))) throw new Error('Playwright manages remote debugging connection itself.');
|
||||
if (args.find(arg => !arg.startsWith('-'))) throw new Error('Arguments can not specify page to be opened');
|
||||
const chromeArguments = [..._chromiumSwitches.chromiumSwitches];
|
||||
if (_os.default.platform() === 'darwin') {
|
||||
// See https://github.com/microsoft/playwright/issues/7362
|
||||
chromeArguments.push('--enable-use-zoom-for-dsf=false');
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1407025.
|
||||
if (options.headless) chromeArguments.push('--use-angle');
|
||||
}
|
||||
if (options.devtools) chromeArguments.push('--auto-open-devtools-for-tabs');
|
||||
if (options.headless) {
|
||||
chromeArguments.push('--headless');
|
||||
chromeArguments.push('--hide-scrollbars', '--mute-audio', '--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4');
|
||||
}
|
||||
if (options.chromiumSandbox !== true) chromeArguments.push('--no-sandbox');
|
||||
const proxy = options.proxyOverride || options.proxy;
|
||||
if (proxy) {
|
||||
const proxyURL = new URL(proxy.server);
|
||||
const isSocks = proxyURL.protocol === 'socks5:';
|
||||
// https://www.chromium.org/developers/design-documents/network-settings
|
||||
if (isSocks && !this.attribution.playwright.options.socksProxyPort) {
|
||||
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
|
||||
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
|
||||
}
|
||||
chromeArguments.push(`--proxy-server=${proxy.server}`);
|
||||
const proxyBypassRules = [];
|
||||
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
|
||||
if (this.attribution.playwright.options.socksProxyPort) proxyBypassRules.push('<-loopback>');
|
||||
if (proxy.bypass) proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
|
||||
if (!process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK && !proxyBypassRules.includes('<-loopback>')) proxyBypassRules.push('<-loopback>');
|
||||
if (proxyBypassRules.length > 0) chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(';')}`);
|
||||
}
|
||||
chromeArguments.push(...args);
|
||||
return chromeArguments;
|
||||
}
|
||||
}
|
||||
exports.BidiChromium = BidiChromium;
|
||||
class ChromiumReadyState extends _browserType.BrowserReadyState {
|
||||
onBrowserOutput(message) {
|
||||
const match = message.match(/DevTools listening on (.*)/);
|
||||
if (match) this._wsEndpoint.resolve(match[1]);
|
||||
}
|
||||
}
|
||||
const kBidiOverCdpWrapper = Symbol('kBidiConnectionWrapper');
|
||||
204
node_modules/playwright-core/lib/server/bidi/bidiConnection.js
generated
vendored
Normal file
204
node_modules/playwright-core/lib/server/bidi/bidiConnection.js
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.kBrowserCloseMessageId = exports.BidiSession = exports.BidiConnection = void 0;
|
||||
var _events = require("events");
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _helper = require("../helper");
|
||||
var _protocolError = require("../protocolError");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// BidiPlaywright uses this special id to issue Browser.close command which we
|
||||
// should ignore.
|
||||
const kBrowserCloseMessageId = exports.kBrowserCloseMessageId = 0;
|
||||
class BidiConnection {
|
||||
constructor(transport, onDisconnect, protocolLogger, browserLogsCollector) {
|
||||
this._transport = void 0;
|
||||
this._onDisconnect = void 0;
|
||||
this._protocolLogger = void 0;
|
||||
this._browserLogsCollector = void 0;
|
||||
this._browserDisconnectedLogs = void 0;
|
||||
this._lastId = 0;
|
||||
this._closed = false;
|
||||
this.browserSession = void 0;
|
||||
this._browsingContextToSession = new Map();
|
||||
this._transport = transport;
|
||||
this._onDisconnect = onDisconnect;
|
||||
this._protocolLogger = protocolLogger;
|
||||
this._browserLogsCollector = browserLogsCollector;
|
||||
this.browserSession = new BidiSession(this, '', message => {
|
||||
this.rawSend(message);
|
||||
});
|
||||
this._transport.onmessage = this._dispatchMessage.bind(this);
|
||||
// onclose should be set last, since it can be immediately called.
|
||||
this._transport.onclose = this._onClose.bind(this);
|
||||
}
|
||||
nextMessageId() {
|
||||
return ++this._lastId;
|
||||
}
|
||||
rawSend(message) {
|
||||
this._protocolLogger('send', message);
|
||||
this._transport.send(message);
|
||||
}
|
||||
_dispatchMessage(message) {
|
||||
this._protocolLogger('receive', message);
|
||||
const object = message;
|
||||
// Bidi messages do not have a common session identifier, so we
|
||||
// route them based on BrowsingContext.
|
||||
if (object.type === 'event') {
|
||||
var _object$params$source;
|
||||
// Route page events to the right session.
|
||||
let context;
|
||||
if ('context' in object.params) context = object.params.context;else if (object.method === 'log.entryAdded' || object.method === 'script.message') context = (_object$params$source = object.params.source) === null || _object$params$source === void 0 ? void 0 : _object$params$source.context;
|
||||
if (context) {
|
||||
const session = this._browsingContextToSession.get(context);
|
||||
if (session) {
|
||||
session.dispatchMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (message.id) {
|
||||
// Find caller session.
|
||||
for (const session of this._browsingContextToSession.values()) {
|
||||
if (session.hasCallback(message.id)) {
|
||||
session.dispatchMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.browserSession.dispatchMessage(message);
|
||||
}
|
||||
_onClose(reason) {
|
||||
this._closed = true;
|
||||
this._transport.onmessage = undefined;
|
||||
this._transport.onclose = undefined;
|
||||
this._browserDisconnectedLogs = _helper.helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
|
||||
this.browserSession.dispose();
|
||||
this._onDisconnect();
|
||||
}
|
||||
isClosed() {
|
||||
return this._closed;
|
||||
}
|
||||
close() {
|
||||
if (!this._closed) this._transport.close();
|
||||
}
|
||||
createMainFrameBrowsingContextSession(bowsingContextId) {
|
||||
const result = new BidiSession(this, bowsingContextId, message => this.rawSend(message));
|
||||
this._browsingContextToSession.set(bowsingContextId, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.BidiConnection = BidiConnection;
|
||||
class BidiSession extends _events.EventEmitter {
|
||||
constructor(connection, sessionId, rawSend) {
|
||||
super();
|
||||
this.connection = void 0;
|
||||
this.sessionId = void 0;
|
||||
this._disposed = false;
|
||||
this._rawSend = void 0;
|
||||
this._callbacks = new Map();
|
||||
this._crashed = false;
|
||||
this._browsingContexts = new Set();
|
||||
this.on = void 0;
|
||||
this.addListener = void 0;
|
||||
this.off = void 0;
|
||||
this.removeListener = void 0;
|
||||
this.once = void 0;
|
||||
this.setMaxListeners(0);
|
||||
this.connection = connection;
|
||||
this.sessionId = sessionId;
|
||||
this._rawSend = rawSend;
|
||||
this.on = super.on;
|
||||
this.off = super.removeListener;
|
||||
this.addListener = super.addListener;
|
||||
this.removeListener = super.removeListener;
|
||||
this.once = super.once;
|
||||
}
|
||||
addFrameBrowsingContext(context) {
|
||||
this._browsingContexts.add(context);
|
||||
this.connection._browsingContextToSession.set(context, this);
|
||||
}
|
||||
removeFrameBrowsingContext(context) {
|
||||
this._browsingContexts.delete(context);
|
||||
this.connection._browsingContextToSession.delete(context);
|
||||
}
|
||||
async send(method, params) {
|
||||
if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs) throw new _protocolError.ProtocolError(this._crashed ? 'crashed' : 'closed', undefined, this.connection._browserDisconnectedLogs);
|
||||
const id = this.connection.nextMessageId();
|
||||
const messageObj = {
|
||||
id,
|
||||
method,
|
||||
params
|
||||
};
|
||||
this._rawSend(messageObj);
|
||||
return new Promise((resolve, reject) => {
|
||||
this._callbacks.set(id, {
|
||||
resolve,
|
||||
reject,
|
||||
error: new _protocolError.ProtocolError('error', method)
|
||||
});
|
||||
});
|
||||
}
|
||||
sendMayFail(method, params) {
|
||||
return this.send(method, params).catch(error => _debugLogger.debugLogger.log('error', error));
|
||||
}
|
||||
markAsCrashed() {
|
||||
this._crashed = true;
|
||||
}
|
||||
isDisposed() {
|
||||
return this._disposed;
|
||||
}
|
||||
dispose() {
|
||||
this._disposed = true;
|
||||
this.connection._browsingContextToSession.delete(this.sessionId);
|
||||
for (const context of this._browsingContexts) this.connection._browsingContextToSession.delete(context);
|
||||
this._browsingContexts.clear();
|
||||
for (const callback of this._callbacks.values()) {
|
||||
callback.error.type = this._crashed ? 'crashed' : 'closed';
|
||||
callback.error.logs = this.connection._browserDisconnectedLogs;
|
||||
callback.reject(callback.error);
|
||||
}
|
||||
this._callbacks.clear();
|
||||
}
|
||||
hasCallback(id) {
|
||||
return this._callbacks.has(id);
|
||||
}
|
||||
dispatchMessage(message) {
|
||||
const object = message;
|
||||
if (object.id === kBrowserCloseMessageId) return;
|
||||
if (object.id && this._callbacks.has(object.id)) {
|
||||
const callback = this._callbacks.get(object.id);
|
||||
this._callbacks.delete(object.id);
|
||||
if (object.type === 'error') {
|
||||
callback.error.setMessage(object.error + '\nMessage: ' + object.message);
|
||||
callback.reject(callback.error);
|
||||
} else if (object.type === 'success') {
|
||||
callback.resolve(object.result);
|
||||
} else {
|
||||
callback.error.setMessage('Internal error, unexpected response type: ' + JSON.stringify(object));
|
||||
callback.reject(callback.error);
|
||||
}
|
||||
} else if (object.id) {
|
||||
// Response might come after session has been disposed and rejected all callbacks.
|
||||
} else {
|
||||
Promise.resolve().then(() => this.emit(object.method, object.params));
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.BidiSession = BidiSession;
|
||||
156
node_modules/playwright-core/lib/server/bidi/bidiExecutionContext.js
generated
vendored
Normal file
156
node_modules/playwright-core/lib/server/bidi/bidiExecutionContext.js
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiExecutionContext = void 0;
|
||||
var _utilityScriptSerializers = require("../isomorphic/utilityScriptSerializers");
|
||||
var js = _interopRequireWildcard(require("../javascript"));
|
||||
var _bidiDeserializer = require("./third_party/bidiDeserializer");
|
||||
var bidi = _interopRequireWildcard(require("./third_party/bidiProtocol"));
|
||||
var _bidiSerializer = require("./third_party/bidiSerializer");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BidiExecutionContext {
|
||||
constructor(session, realmInfo) {
|
||||
this._session = void 0;
|
||||
this._target = void 0;
|
||||
this._session = session;
|
||||
if (realmInfo.type === 'window') {
|
||||
// Simple realm does not seem to work for Window contexts.
|
||||
this._target = {
|
||||
context: realmInfo.context,
|
||||
sandbox: realmInfo.sandbox
|
||||
};
|
||||
} else {
|
||||
this._target = {
|
||||
realm: realmInfo.realm
|
||||
};
|
||||
}
|
||||
}
|
||||
async rawEvaluateJSON(expression) {
|
||||
const response = await this._session.send('script.evaluate', {
|
||||
expression,
|
||||
target: this._target,
|
||||
serializationOptions: {
|
||||
maxObjectDepth: 10,
|
||||
maxDomDepth: 10
|
||||
},
|
||||
awaitPromise: true,
|
||||
userActivation: true
|
||||
});
|
||||
if (response.type === 'success') return _bidiDeserializer.BidiDeserializer.deserialize(response.result);
|
||||
if (response.type === 'exception') throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
async rawEvaluateHandle(expression) {
|
||||
const response = await this._session.send('script.evaluate', {
|
||||
expression,
|
||||
target: this._target,
|
||||
resultOwnership: bidi.Script.ResultOwnership.Root,
|
||||
// Necessary for the handle to be returned.
|
||||
serializationOptions: {
|
||||
maxObjectDepth: 0,
|
||||
maxDomDepth: 0
|
||||
},
|
||||
awaitPromise: true,
|
||||
userActivation: true
|
||||
});
|
||||
if (response.type === 'success') {
|
||||
if ('handle' in response.result) return response.result.handle;
|
||||
throw new js.JavaScriptErrorInEvaluate('Cannot get handle: ' + JSON.stringify(response.result));
|
||||
}
|
||||
if (response.type === 'exception') throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
async evaluateWithArguments(functionDeclaration, returnByValue, utilityScript, values, objectIds) {
|
||||
const response = await this._session.send('script.callFunction', {
|
||||
functionDeclaration,
|
||||
target: this._target,
|
||||
arguments: [{
|
||||
handle: utilityScript._objectId
|
||||
}, ...values.map(_bidiSerializer.BidiSerializer.serialize), ...objectIds.map(handle => ({
|
||||
handle
|
||||
}))],
|
||||
resultOwnership: returnByValue ? undefined : bidi.Script.ResultOwnership.Root,
|
||||
// Necessary for the handle to be returned.
|
||||
serializationOptions: returnByValue ? {} : {
|
||||
maxObjectDepth: 0,
|
||||
maxDomDepth: 0
|
||||
},
|
||||
awaitPromise: true,
|
||||
userActivation: true
|
||||
});
|
||||
if (response.type === 'exception') throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
if (response.type === 'success') {
|
||||
if (returnByValue) return (0, _utilityScriptSerializers.parseEvaluationResultValue)(_bidiDeserializer.BidiDeserializer.deserialize(response.result));
|
||||
const objectId = 'handle' in response.result ? response.result.handle : undefined;
|
||||
return utilityScript._context.createHandle({
|
||||
objectId,
|
||||
...response.result
|
||||
});
|
||||
}
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
async getProperties(context, objectId) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
createHandle(context, jsRemoteObject) {
|
||||
const remoteObject = jsRemoteObject;
|
||||
return new js.JSHandle(context, remoteObject.type, renderPreview(remoteObject), jsRemoteObject.objectId, remoteObjectValue(remoteObject));
|
||||
}
|
||||
async releaseHandle(objectId) {
|
||||
await this._session.send('script.disown', {
|
||||
target: this._target,
|
||||
handles: [objectId]
|
||||
});
|
||||
}
|
||||
async rawCallFunction(functionDeclaration, arg) {
|
||||
const response = await this._session.send('script.callFunction', {
|
||||
functionDeclaration,
|
||||
target: this._target,
|
||||
arguments: [arg],
|
||||
resultOwnership: bidi.Script.ResultOwnership.Root,
|
||||
// Necessary for the handle to be returned.
|
||||
serializationOptions: {
|
||||
maxObjectDepth: 0,
|
||||
maxDomDepth: 0
|
||||
},
|
||||
awaitPromise: true,
|
||||
userActivation: true
|
||||
});
|
||||
if (response.type === 'exception') throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails));
|
||||
if (response.type === 'success') return response.result;
|
||||
throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response));
|
||||
}
|
||||
}
|
||||
exports.BidiExecutionContext = BidiExecutionContext;
|
||||
function renderPreview(remoteObject) {
|
||||
if (remoteObject.type === 'undefined') return 'undefined';
|
||||
if (remoteObject.type === 'null') return 'null';
|
||||
if ('value' in remoteObject) return String(remoteObject.value);
|
||||
return `<${remoteObject.type}>`;
|
||||
}
|
||||
function remoteObjectValue(remoteObject) {
|
||||
if (remoteObject.type === 'undefined') return undefined;
|
||||
if (remoteObject.type === 'null') return null;
|
||||
if (remoteObject.type === 'number' && typeof remoteObject.value === 'string') return js.parseUnserializableValue(remoteObject.value);
|
||||
if ('value' in remoteObject) return remoteObject.value;
|
||||
return undefined;
|
||||
}
|
||||
104
node_modules/playwright-core/lib/server/bidi/bidiFirefox.js
generated
vendored
Normal file
104
node_modules/playwright-core/lib/server/bidi/bidiFirefox.js
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiFirefox = void 0;
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("../../utils");
|
||||
var _browserType = require("../browserType");
|
||||
var _bidiBrowser = require("./bidiBrowser");
|
||||
var _bidiConnection = require("./bidiConnection");
|
||||
var _firefoxPrefs = require("./third_party/firefoxPrefs");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BidiFirefox extends _browserType.BrowserType {
|
||||
constructor(parent) {
|
||||
super(parent, 'bidi');
|
||||
this._useBidi = true;
|
||||
}
|
||||
async connectToTransport(transport, options) {
|
||||
return _bidiBrowser.BidiBrowser.connect(this.attribution.playwright, transport, options);
|
||||
}
|
||||
doRewriteStartupLog(error) {
|
||||
if (!error.logs) return error;
|
||||
// https://github.com/microsoft/playwright/issues/6500
|
||||
if (error.logs.includes(`as root in a regular user's session is not supported.`)) error.logs = '\n' + (0, _utils.wrapInASCIIBox)(`Firefox is unable to launch if the $HOME folder isn't owned by the current user.\nWorkaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ? ' in your GitHub Actions workflow file' : ''} when running Playwright.`, 1);
|
||||
if (error.logs.includes('no DISPLAY environment variable specified')) error.logs = '\n' + (0, _utils.wrapInASCIIBox)(_browserType.kNoXServerRunningError, 1);
|
||||
return error;
|
||||
}
|
||||
amendEnvironment(env, userDataDir, executable, browserArguments) {
|
||||
if (!_path.default.isAbsolute(_os.default.homedir())) throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${_os.default.platform() === 'win32' ? 'USERPROFILE' : 'HOME'} to a relative path?`);
|
||||
env = {
|
||||
...env,
|
||||
'MOZ_CRASHREPORTER': '1',
|
||||
'MOZ_CRASHREPORTER_NO_REPORT': '1',
|
||||
'MOZ_CRASHREPORTER_SHUTDOWN': '1'
|
||||
};
|
||||
if (_os.default.platform() === 'linux') {
|
||||
// Always remove SNAP_NAME and SNAP_INSTANCE_NAME env variables since they
|
||||
// confuse Firefox: in our case, builds never come from SNAP.
|
||||
// See https://github.com/microsoft/playwright/issues/20555
|
||||
return {
|
||||
...env,
|
||||
SNAP_NAME: undefined,
|
||||
SNAP_INSTANCE_NAME: undefined
|
||||
};
|
||||
}
|
||||
return env;
|
||||
}
|
||||
attemptToGracefullyCloseBrowser(transport) {
|
||||
transport.send({
|
||||
method: 'browser.close',
|
||||
params: {},
|
||||
id: _bidiConnection.kBrowserCloseMessageId
|
||||
});
|
||||
}
|
||||
async prepareUserDataDir(options, userDataDir) {
|
||||
await (0, _firefoxPrefs.createProfile)({
|
||||
path: userDataDir,
|
||||
preferences: options.firefoxUserPrefs || {}
|
||||
});
|
||||
}
|
||||
defaultArgs(options, isPersistent, userDataDir) {
|
||||
const {
|
||||
args = [],
|
||||
headless
|
||||
} = options;
|
||||
const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
|
||||
if (userDataDirArg) throw this._createUserDataDirArgMisuseError('--profile');
|
||||
const firefoxArguments = ['--remote-debugging-port=0'];
|
||||
if (headless) firefoxArguments.push('--headless');else firefoxArguments.push('--foreground');
|
||||
firefoxArguments.push(`--profile`, userDataDir);
|
||||
firefoxArguments.push(...args);
|
||||
return firefoxArguments;
|
||||
}
|
||||
readyState(options) {
|
||||
(0, _utils.assert)(options.useWebSocket);
|
||||
return new FirefoxReadyState();
|
||||
}
|
||||
}
|
||||
exports.BidiFirefox = BidiFirefox;
|
||||
class FirefoxReadyState extends _browserType.BrowserReadyState {
|
||||
onBrowserOutput(message) {
|
||||
// Bidi WebSocket in Firefox.
|
||||
const match = message.match(/WebDriver BiDi listening on (ws:\/\/.*)$/);
|
||||
if (match) this._wsEndpoint.resolve(match[1] + '/session');
|
||||
}
|
||||
}
|
||||
158
node_modules/playwright-core/lib/server/bidi/bidiInput.js
generated
vendored
Normal file
158
node_modules/playwright-core/lib/server/bidi/bidiInput.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.RawTouchscreenImpl = exports.RawMouseImpl = exports.RawKeyboardImpl = void 0;
|
||||
var bidi = _interopRequireWildcard(require("./third_party/bidiProtocol"));
|
||||
var _bidiKeyboard = require("./third_party/bidiKeyboard");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class RawKeyboardImpl {
|
||||
constructor(session) {
|
||||
this._session = void 0;
|
||||
this._session = session;
|
||||
}
|
||||
setSession(session) {
|
||||
this._session = session;
|
||||
}
|
||||
async keydown(modifiers, code, keyCode, keyCodeWithoutLocation, key, location, autoRepeat, text) {
|
||||
const actions = [];
|
||||
actions.push({
|
||||
type: 'keyDown',
|
||||
value: (0, _bidiKeyboard.getBidiKeyValue)(key)
|
||||
});
|
||||
// TODO: add modifiers?
|
||||
await this._performActions(actions);
|
||||
}
|
||||
async keyup(modifiers, code, keyCode, keyCodeWithoutLocation, key, location) {
|
||||
const actions = [];
|
||||
actions.push({
|
||||
type: 'keyUp',
|
||||
value: (0, _bidiKeyboard.getBidiKeyValue)(key)
|
||||
});
|
||||
await this._performActions(actions);
|
||||
}
|
||||
async sendText(text) {
|
||||
const actions = [];
|
||||
for (const char of text) {
|
||||
const value = (0, _bidiKeyboard.getBidiKeyValue)(char);
|
||||
actions.push({
|
||||
type: 'keyDown',
|
||||
value
|
||||
});
|
||||
actions.push({
|
||||
type: 'keyUp',
|
||||
value
|
||||
});
|
||||
}
|
||||
await this._performActions(actions);
|
||||
}
|
||||
async _performActions(actions) {
|
||||
await this._session.send('input.performActions', {
|
||||
context: this._session.sessionId,
|
||||
actions: [{
|
||||
type: 'key',
|
||||
id: 'pw_keyboard',
|
||||
actions
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RawKeyboardImpl = RawKeyboardImpl;
|
||||
class RawMouseImpl {
|
||||
constructor(session) {
|
||||
this._session = void 0;
|
||||
this._session = session;
|
||||
}
|
||||
async move(x, y, button, buttons, modifiers, forClick) {
|
||||
// Bidi throws when x/y are not integers.
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
await this._performActions([{
|
||||
type: 'pointerMove',
|
||||
x,
|
||||
y
|
||||
}]);
|
||||
}
|
||||
async down(x, y, button, buttons, modifiers, clickCount) {
|
||||
await this._performActions([{
|
||||
type: 'pointerDown',
|
||||
button: toBidiButton(button)
|
||||
}]);
|
||||
}
|
||||
async up(x, y, button, buttons, modifiers, clickCount) {
|
||||
await this._performActions([{
|
||||
type: 'pointerUp',
|
||||
button: toBidiButton(button)
|
||||
}]);
|
||||
}
|
||||
async wheel(x, y, buttons, modifiers, deltaX, deltaY) {
|
||||
// Bidi throws when x/y are not integers.
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
await this._session.send('input.performActions', {
|
||||
context: this._session.sessionId,
|
||||
actions: [{
|
||||
type: 'wheel',
|
||||
id: 'pw_mouse_wheel',
|
||||
actions: [{
|
||||
type: 'scroll',
|
||||
x,
|
||||
y,
|
||||
deltaX,
|
||||
deltaY
|
||||
}]
|
||||
}]
|
||||
});
|
||||
}
|
||||
async _performActions(actions) {
|
||||
await this._session.send('input.performActions', {
|
||||
context: this._session.sessionId,
|
||||
actions: [{
|
||||
type: 'pointer',
|
||||
id: 'pw_mouse',
|
||||
parameters: {
|
||||
pointerType: bidi.Input.PointerType.Mouse
|
||||
},
|
||||
actions
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RawMouseImpl = RawMouseImpl;
|
||||
class RawTouchscreenImpl {
|
||||
constructor(session) {
|
||||
this._session = void 0;
|
||||
this._session = session;
|
||||
}
|
||||
async tap(x, y, modifiers) {}
|
||||
}
|
||||
exports.RawTouchscreenImpl = RawTouchscreenImpl;
|
||||
function toBidiButton(button) {
|
||||
switch (button) {
|
||||
case 'left':
|
||||
return 0;
|
||||
case 'right':
|
||||
return 2;
|
||||
case 'middle':
|
||||
return 1;
|
||||
}
|
||||
throw new Error('Unknown button: ' + button);
|
||||
}
|
||||
338
node_modules/playwright-core/lib/server/bidi/bidiNetworkManager.js
generated
vendored
Normal file
338
node_modules/playwright-core/lib/server/bidi/bidiNetworkManager.js
generated
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiNetworkManager = void 0;
|
||||
exports.bidiBytesValueToString = bidiBytesValueToString;
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
var bidi = _interopRequireWildcard(require("./third_party/bidiProtocol"));
|
||||
var _cookieStore = require("../cookieStore");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BidiNetworkManager {
|
||||
constructor(bidiSession, page, onNavigationResponseStarted) {
|
||||
this._session = void 0;
|
||||
this._requests = void 0;
|
||||
this._page = void 0;
|
||||
this._eventListeners = void 0;
|
||||
this._onNavigationResponseStarted = void 0;
|
||||
this._userRequestInterceptionEnabled = false;
|
||||
this._protocolRequestInterceptionEnabled = false;
|
||||
this._credentials = void 0;
|
||||
this._intercepId = void 0;
|
||||
this._session = bidiSession;
|
||||
this._requests = new Map();
|
||||
this._page = page;
|
||||
this._onNavigationResponseStarted = onNavigationResponseStarted;
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(bidiSession, 'network.beforeRequestSent', this._onBeforeRequestSent.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'network.responseStarted', this._onResponseStarted.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'network.responseCompleted', this._onResponseCompleted.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'network.fetchError', this._onFetchError.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'network.authRequired', this._onAuthRequired.bind(this))];
|
||||
}
|
||||
dispose() {
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
}
|
||||
_onBeforeRequestSent(param) {
|
||||
if (param.request.url.startsWith('data:')) return;
|
||||
const redirectedFrom = param.redirectCount ? this._requests.get(param.request.request) || null : null;
|
||||
const frame = redirectedFrom ? redirectedFrom.request.frame() : param.context ? this._page._frameManager.frame(param.context) : null;
|
||||
if (!frame) return;
|
||||
if (redirectedFrom) this._requests.delete(redirectedFrom._id);
|
||||
let route;
|
||||
if (param.intercepts) {
|
||||
// We do not support intercepting redirects.
|
||||
if (redirectedFrom) {
|
||||
var _redirectedFrom$_orig;
|
||||
this._session.sendMayFail('network.continueRequest', {
|
||||
request: param.request.request,
|
||||
...(((_redirectedFrom$_orig = redirectedFrom._originalRequestRoute) === null || _redirectedFrom$_orig === void 0 ? void 0 : _redirectedFrom$_orig._alreadyContinuedHeaders) || {})
|
||||
});
|
||||
} else {
|
||||
route = new BidiRouteImpl(this._session, param.request.request);
|
||||
}
|
||||
}
|
||||
const request = new BidiRequest(frame, redirectedFrom, param, route);
|
||||
this._requests.set(request._id, request);
|
||||
this._page._frameManager.requestStarted(request.request, route);
|
||||
}
|
||||
_onResponseStarted(params) {
|
||||
const request = this._requests.get(params.request.request);
|
||||
if (!request) return;
|
||||
const getResponseBody = async () => {
|
||||
throw new Error(`Response body is not available for requests in Bidi`);
|
||||
};
|
||||
const timings = params.request.timings;
|
||||
const startTime = timings.requestTime;
|
||||
function relativeToStart(time) {
|
||||
if (!time) return -1;
|
||||
return (time - startTime) / 1000;
|
||||
}
|
||||
const timing = {
|
||||
startTime: startTime / 1000,
|
||||
requestStart: relativeToStart(timings.requestStart),
|
||||
responseStart: relativeToStart(timings.responseStart),
|
||||
domainLookupStart: relativeToStart(timings.dnsStart),
|
||||
domainLookupEnd: relativeToStart(timings.dnsEnd),
|
||||
connectStart: relativeToStart(timings.connectStart),
|
||||
secureConnectionStart: relativeToStart(timings.tlsStart),
|
||||
connectEnd: relativeToStart(timings.connectEnd)
|
||||
};
|
||||
const response = new network.Response(request.request, params.response.status, params.response.statusText, fromBidiHeaders(params.response.headers), timing, getResponseBody, false);
|
||||
response._serverAddrFinished();
|
||||
response._securityDetailsFinished();
|
||||
// "raw" headers are the same as "provisional" headers in Bidi.
|
||||
response.setRawResponseHeaders(null);
|
||||
response.setResponseHeadersSize(params.response.headersSize);
|
||||
this._page._frameManager.requestReceivedResponse(response);
|
||||
if (params.navigation) this._onNavigationResponseStarted(params);
|
||||
}
|
||||
_onResponseCompleted(params) {
|
||||
const request = this._requests.get(params.request.request);
|
||||
if (!request) return;
|
||||
const response = request.request._existingResponse();
|
||||
// TODO: body size is the encoded size
|
||||
response.setTransferSize(params.response.bodySize);
|
||||
response.setEncodedBodySize(params.response.bodySize);
|
||||
|
||||
// Keep redirected requests in the map for future reference as redirectedFrom.
|
||||
const isRedirected = response.status() >= 300 && response.status() <= 399;
|
||||
const responseEndTime = params.request.timings.responseEnd / 1000 - response.timing().startTime;
|
||||
if (isRedirected) {
|
||||
response._requestFinished(responseEndTime);
|
||||
} else {
|
||||
this._requests.delete(request._id);
|
||||
response._requestFinished(responseEndTime);
|
||||
}
|
||||
response._setHttpVersion(params.response.protocol);
|
||||
this._page._frameManager.reportRequestFinished(request.request, response);
|
||||
}
|
||||
_onFetchError(params) {
|
||||
const request = this._requests.get(params.request.request);
|
||||
if (!request) return;
|
||||
this._requests.delete(request._id);
|
||||
const response = request.request._existingResponse();
|
||||
if (response) {
|
||||
response.setTransferSize(null);
|
||||
response.setEncodedBodySize(null);
|
||||
response._requestFinished(-1);
|
||||
}
|
||||
request.request._setFailureText(params.errorText);
|
||||
// TODO: support canceled flag
|
||||
this._page._frameManager.requestFailed(request.request, params.errorText === 'NS_BINDING_ABORTED');
|
||||
}
|
||||
_onAuthRequired(params) {
|
||||
var _params$response$auth;
|
||||
const isBasic = (_params$response$auth = params.response.authChallenges) === null || _params$response$auth === void 0 ? void 0 : _params$response$auth.some(challenge => challenge.scheme.startsWith('Basic'));
|
||||
const credentials = this._page._browserContext._options.httpCredentials;
|
||||
if (isBasic && credentials) {
|
||||
this._session.sendMayFail('network.continueWithAuth', {
|
||||
request: params.request.request,
|
||||
action: 'provideCredentials',
|
||||
credentials: {
|
||||
type: 'password',
|
||||
username: credentials.username,
|
||||
password: credentials.password
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._session.sendMayFail('network.continueWithAuth', {
|
||||
request: params.request.request,
|
||||
action: 'default'
|
||||
});
|
||||
}
|
||||
}
|
||||
async setRequestInterception(value) {
|
||||
this._userRequestInterceptionEnabled = value;
|
||||
await this._updateProtocolRequestInterception();
|
||||
}
|
||||
async setCredentials(credentials) {
|
||||
this._credentials = credentials;
|
||||
await this._updateProtocolRequestInterception();
|
||||
}
|
||||
async _updateProtocolRequestInterception(initial) {
|
||||
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
|
||||
if (enabled === this._protocolRequestInterceptionEnabled) return;
|
||||
this._protocolRequestInterceptionEnabled = enabled;
|
||||
if (initial && !enabled) return;
|
||||
const cachePromise = this._session.send('network.setCacheBehavior', {
|
||||
cacheBehavior: enabled ? 'bypass' : 'default'
|
||||
});
|
||||
let interceptPromise = Promise.resolve(undefined);
|
||||
if (enabled) {
|
||||
interceptPromise = this._session.send('network.addIntercept', {
|
||||
phases: [bidi.Network.InterceptPhase.AuthRequired, bidi.Network.InterceptPhase.BeforeRequestSent],
|
||||
urlPatterns: [{
|
||||
type: 'pattern'
|
||||
}]
|
||||
// urlPatterns: [{ type: 'string', pattern: '*' }],
|
||||
}).then(r => {
|
||||
this._intercepId = r.intercept;
|
||||
});
|
||||
} else if (this._intercepId) {
|
||||
interceptPromise = this._session.send('network.removeIntercept', {
|
||||
intercept: this._intercepId
|
||||
});
|
||||
this._intercepId = undefined;
|
||||
}
|
||||
await Promise.all([cachePromise, interceptPromise]);
|
||||
}
|
||||
}
|
||||
exports.BidiNetworkManager = BidiNetworkManager;
|
||||
class BidiRequest {
|
||||
constructor(frame, redirectedFrom, payload, route) {
|
||||
var _payload$navigation;
|
||||
this.request = void 0;
|
||||
this._id = void 0;
|
||||
this._redirectedTo = void 0;
|
||||
// Only first request in the chain can be intercepted, so this will
|
||||
// store the first and only Route in the chain (if any).
|
||||
this._originalRequestRoute = void 0;
|
||||
this._id = payload.request.request;
|
||||
if (redirectedFrom) redirectedFrom._redirectedTo = this;
|
||||
// TODO: missing in the spec?
|
||||
const postDataBuffer = null;
|
||||
this.request = new network.Request(frame._page._browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, (_payload$navigation = payload.navigation) !== null && _payload$navigation !== void 0 ? _payload$navigation : undefined, payload.request.url, 'other', payload.request.method, postDataBuffer, fromBidiHeaders(payload.request.headers));
|
||||
// "raw" headers are the same as "provisional" headers in Bidi.
|
||||
this.request.setRawRequestHeaders(null);
|
||||
this.request._setBodySize(payload.request.bodySize || 0);
|
||||
this._originalRequestRoute = route !== null && route !== void 0 ? route : redirectedFrom === null || redirectedFrom === void 0 ? void 0 : redirectedFrom._originalRequestRoute;
|
||||
route === null || route === void 0 || route._setRequest(this.request);
|
||||
}
|
||||
_finalRequest() {
|
||||
let request = this;
|
||||
while (request._redirectedTo) request = request._redirectedTo;
|
||||
return request;
|
||||
}
|
||||
}
|
||||
class BidiRouteImpl {
|
||||
constructor(session, requestId) {
|
||||
this._requestId = void 0;
|
||||
this._session = void 0;
|
||||
this._request = void 0;
|
||||
this._alreadyContinuedHeaders = void 0;
|
||||
this._session = session;
|
||||
this._requestId = requestId;
|
||||
}
|
||||
_setRequest(request) {
|
||||
this._request = request;
|
||||
}
|
||||
async continue(overrides) {
|
||||
// Firefox does not update content-length header.
|
||||
let headers = overrides.headers || this._request.headers();
|
||||
if (overrides.postData && headers) {
|
||||
headers = headers.map(header => {
|
||||
if (header.name.toLowerCase() === 'content-length') return {
|
||||
name: header.name,
|
||||
value: overrides.postData.byteLength.toString()
|
||||
};
|
||||
return header;
|
||||
});
|
||||
}
|
||||
this._alreadyContinuedHeaders = headers;
|
||||
await this._session.sendMayFail('network.continueRequest', {
|
||||
request: this._requestId,
|
||||
url: overrides.url,
|
||||
method: overrides.method,
|
||||
...toBidiRequestHeaders(this._alreadyContinuedHeaders),
|
||||
body: overrides.postData ? {
|
||||
type: 'base64',
|
||||
value: Buffer.from(overrides.postData).toString('base64')
|
||||
} : undefined
|
||||
});
|
||||
}
|
||||
async fulfill(response) {
|
||||
const base64body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64');
|
||||
await this._session.sendMayFail('network.provideResponse', {
|
||||
request: this._requestId,
|
||||
statusCode: response.status,
|
||||
reasonPhrase: network.statusText(response.status),
|
||||
...toBidiResponseHeaders(response.headers),
|
||||
body: {
|
||||
type: 'base64',
|
||||
value: base64body
|
||||
}
|
||||
});
|
||||
}
|
||||
async abort(errorCode) {
|
||||
await this._session.sendMayFail('network.failRequest', {
|
||||
request: this._requestId
|
||||
});
|
||||
}
|
||||
}
|
||||
function fromBidiHeaders(bidiHeaders) {
|
||||
const result = [];
|
||||
for (const {
|
||||
name,
|
||||
value
|
||||
} of bidiHeaders) result.push({
|
||||
name,
|
||||
value: bidiBytesValueToString(value)
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function toBidiRequestHeaders(allHeaders) {
|
||||
const bidiHeaders = toBidiHeaders(allHeaders);
|
||||
const cookies = bidiHeaders.filter(h => h.name.toLowerCase() === 'cookie');
|
||||
const headers = bidiHeaders.filter(h => h.name.toLowerCase() !== 'cookie');
|
||||
return {
|
||||
cookies,
|
||||
headers
|
||||
};
|
||||
}
|
||||
function toBidiResponseHeaders(headers) {
|
||||
const setCookieHeaders = headers.filter(h => h.name.toLowerCase() === 'set-cookie');
|
||||
const otherHeaders = headers.filter(h => h.name.toLowerCase() !== 'set-cookie');
|
||||
const rawCookies = setCookieHeaders.map(h => (0, _cookieStore.parseRawCookie)(h.value));
|
||||
const cookies = rawCookies.filter(Boolean).map(c => {
|
||||
return {
|
||||
...c,
|
||||
value: {
|
||||
type: 'string',
|
||||
value: c.value
|
||||
},
|
||||
sameSite: toBidiSameSite(c.sameSite)
|
||||
};
|
||||
});
|
||||
return {
|
||||
cookies,
|
||||
headers: toBidiHeaders(otherHeaders)
|
||||
};
|
||||
}
|
||||
function toBidiHeaders(headers) {
|
||||
return headers.map(({
|
||||
name,
|
||||
value
|
||||
}) => ({
|
||||
name,
|
||||
value: {
|
||||
type: 'string',
|
||||
value
|
||||
}
|
||||
}));
|
||||
}
|
||||
function bidiBytesValueToString(value) {
|
||||
if (value.type === 'string') return value.value;
|
||||
if (value.type === 'base64') return Buffer.from(value.type, 'base64').toString('binary');
|
||||
return 'unknown value type: ' + value.type;
|
||||
}
|
||||
function toBidiSameSite(sameSite) {
|
||||
if (!sameSite) return undefined;
|
||||
if (sameSite === 'Strict') return bidi.Network.SameSite.Strict;
|
||||
if (sameSite === 'Lax') return bidi.Network.SameSite.Lax;
|
||||
return bidi.Network.SameSite.None;
|
||||
}
|
||||
103
node_modules/playwright-core/lib/server/bidi/bidiOverCdp.js
generated
vendored
Normal file
103
node_modules/playwright-core/lib/server/bidi/bidiOverCdp.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.connectBidiOverCdp = connectBidiOverCdp;
|
||||
var bidiMapper = _interopRequireWildcard(require("chromium-bidi/lib/cjs/bidiMapper/BidiMapper"));
|
||||
var bidiCdpConnection = _interopRequireWildcard(require("chromium-bidi/lib/cjs/cdp/CdpConnection"));
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const bidiServerLogger = (prefix, ...args) => {
|
||||
_debugLogger.debugLogger.log(prefix, args);
|
||||
};
|
||||
async function connectBidiOverCdp(cdp) {
|
||||
let server = undefined;
|
||||
const bidiTransport = new BidiTransportImpl();
|
||||
const bidiConnection = new BidiConnection(bidiTransport, () => {
|
||||
var _server;
|
||||
return (_server = server) === null || _server === void 0 ? void 0 : _server.close();
|
||||
});
|
||||
const cdpTransportImpl = new CdpTransportImpl(cdp);
|
||||
const cdpConnection = new bidiCdpConnection.MapperCdpConnection(cdpTransportImpl, bidiServerLogger);
|
||||
// Make sure onclose event is propagated.
|
||||
cdp.onclose = () => {
|
||||
var _bidiConnection$onclo;
|
||||
return (_bidiConnection$onclo = bidiConnection.onclose) === null || _bidiConnection$onclo === void 0 ? void 0 : _bidiConnection$onclo.call(bidiConnection);
|
||||
};
|
||||
server = await bidiMapper.BidiServer.createAndStart(bidiTransport, cdpConnection, await cdpConnection.createBrowserSession(), /* selfTargetId= */'', undefined, bidiServerLogger);
|
||||
return bidiConnection;
|
||||
}
|
||||
class BidiTransportImpl {
|
||||
constructor() {
|
||||
this._handler = void 0;
|
||||
this._bidiConnection = void 0;
|
||||
}
|
||||
setOnMessage(handler) {
|
||||
this._handler = handler;
|
||||
}
|
||||
sendMessage(message) {
|
||||
var _this$_bidiConnection, _this$_bidiConnection2;
|
||||
return (_this$_bidiConnection = (_this$_bidiConnection2 = this._bidiConnection).onmessage) === null || _this$_bidiConnection === void 0 ? void 0 : _this$_bidiConnection.call(_this$_bidiConnection2, message);
|
||||
}
|
||||
close() {
|
||||
var _this$_bidiConnection3, _this$_bidiConnection4;
|
||||
(_this$_bidiConnection3 = (_this$_bidiConnection4 = this._bidiConnection).onclose) === null || _this$_bidiConnection3 === void 0 || _this$_bidiConnection3.call(_this$_bidiConnection4);
|
||||
}
|
||||
}
|
||||
class BidiConnection {
|
||||
constructor(bidiTransport, closeCallback) {
|
||||
this._bidiTransport = void 0;
|
||||
this._closeCallback = void 0;
|
||||
this.onmessage = void 0;
|
||||
this.onclose = void 0;
|
||||
this._bidiTransport = bidiTransport;
|
||||
this._bidiTransport._bidiConnection = this;
|
||||
this._closeCallback = closeCallback;
|
||||
}
|
||||
send(s) {
|
||||
var _this$_bidiTransport$, _this$_bidiTransport;
|
||||
(_this$_bidiTransport$ = (_this$_bidiTransport = this._bidiTransport)._handler) === null || _this$_bidiTransport$ === void 0 || _this$_bidiTransport$.call(_this$_bidiTransport, s);
|
||||
}
|
||||
close() {
|
||||
this._closeCallback();
|
||||
}
|
||||
}
|
||||
class CdpTransportImpl {
|
||||
constructor(connection) {
|
||||
this._connection = void 0;
|
||||
this._handler = void 0;
|
||||
this._bidiConnection = void 0;
|
||||
this._connection = connection;
|
||||
this._connection.onmessage = message => {
|
||||
var _this$_handler;
|
||||
(_this$_handler = this._handler) === null || _this$_handler === void 0 || _this$_handler.call(this, JSON.stringify(message));
|
||||
};
|
||||
}
|
||||
setOnMessage(handler) {
|
||||
this._handler = handler;
|
||||
}
|
||||
sendMessage(message) {
|
||||
return this._connection.send(JSON.parse(message));
|
||||
}
|
||||
close() {
|
||||
this._connection.close();
|
||||
}
|
||||
}
|
||||
529
node_modules/playwright-core/lib/server/bidi/bidiPage.js
generated
vendored
Normal file
529
node_modules/playwright-core/lib/server/bidi/bidiPage.js
generated
vendored
Normal file
@@ -0,0 +1,529 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiPage = void 0;
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var _utils = require("../../utils");
|
||||
var dom = _interopRequireWildcard(require("../dom"));
|
||||
var dialog = _interopRequireWildcard(require("../dialog"));
|
||||
var _page = require("../page");
|
||||
var _bidiInput = require("./bidiInput");
|
||||
var bidi = _interopRequireWildcard(require("./third_party/bidiProtocol"));
|
||||
var _bidiExecutionContext = require("./bidiExecutionContext");
|
||||
var _bidiNetworkManager = require("./bidiNetworkManager");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _bidiPdf = require("./bidiPdf");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
const kPlaywrightBindingChannel = 'playwrightChannel';
|
||||
class BidiPage {
|
||||
constructor(browserContext, bidiSession, opener) {
|
||||
this.rawMouse = void 0;
|
||||
this.rawKeyboard = void 0;
|
||||
this.rawTouchscreen = void 0;
|
||||
this._page = void 0;
|
||||
this._pagePromise = void 0;
|
||||
this._session = void 0;
|
||||
this._opener = void 0;
|
||||
this._realmToContext = void 0;
|
||||
this._sessionListeners = [];
|
||||
this._browserContext = void 0;
|
||||
this._networkManager = void 0;
|
||||
this._pdf = void 0;
|
||||
this._initializedPage = null;
|
||||
this._initScriptIds = [];
|
||||
this._session = bidiSession;
|
||||
this._opener = opener;
|
||||
this.rawKeyboard = new _bidiInput.RawKeyboardImpl(bidiSession);
|
||||
this.rawMouse = new _bidiInput.RawMouseImpl(bidiSession);
|
||||
this.rawTouchscreen = new _bidiInput.RawTouchscreenImpl(bidiSession);
|
||||
this._realmToContext = new Map();
|
||||
this._page = new _page.Page(this, browserContext);
|
||||
this._browserContext = browserContext;
|
||||
this._networkManager = new _bidiNetworkManager.BidiNetworkManager(this._session, this._page, this._onNavigationResponseStarted.bind(this));
|
||||
this._pdf = new _bidiPdf.BidiPDF(this._session);
|
||||
this._page.on(_page.Page.Events.FrameDetached, frame => this._removeContextsForFrame(frame, false));
|
||||
this._sessionListeners = [_eventsHelper.eventsHelper.addEventListener(bidiSession, 'script.realmCreated', this._onRealmCreated.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'script.message', this._onScriptMessage.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.contextDestroyed', this._onBrowsingContextDestroyed.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationStarted', this._onNavigationStarted.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationAborted', this._onNavigationAborted.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationFailed', this._onNavigationFailed.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.fragmentNavigated', this._onFragmentNavigated.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.domContentLoaded', this._onDomContentLoaded.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.load', this._onLoad.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'browsingContext.userPromptOpened', this._onUserPromptOpened.bind(this)), _eventsHelper.eventsHelper.addEventListener(bidiSession, 'log.entryAdded', this._onLogEntryAdded.bind(this))];
|
||||
|
||||
// Initialize main frame.
|
||||
this._pagePromise = this._initialize().finally(async () => {
|
||||
await this._page.initOpener(this._opener);
|
||||
}).then(() => {
|
||||
this._initializedPage = this._page;
|
||||
this._page.reportAsNew();
|
||||
return this._page;
|
||||
}).catch(e => {
|
||||
this._page.reportAsNew(e);
|
||||
return e;
|
||||
});
|
||||
}
|
||||
async _initialize() {
|
||||
// Initialize main frame.
|
||||
this._onFrameAttached(this._session.sessionId, null);
|
||||
await Promise.all([this.updateHttpCredentials(), this.updateRequestInterception(), this._updateViewport(), this._installMainBinding(), this._addAllInitScripts()]);
|
||||
}
|
||||
async _addAllInitScripts() {
|
||||
return Promise.all(this._page.allInitScripts().map(initScript => this.addInitScript(initScript)));
|
||||
}
|
||||
potentiallyUninitializedPage() {
|
||||
return this._page;
|
||||
}
|
||||
didClose() {
|
||||
this._session.dispose();
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._sessionListeners);
|
||||
this._page._didClose();
|
||||
}
|
||||
async pageOrError() {
|
||||
// TODO: Wait for first execution context to be created and maybe about:blank navigated.
|
||||
return this._pagePromise;
|
||||
}
|
||||
_onFrameAttached(frameId, parentFrameId) {
|
||||
return this._page._frameManager.frameAttached(frameId, parentFrameId);
|
||||
}
|
||||
_removeContextsForFrame(frame, notifyFrame) {
|
||||
for (const [contextId, context] of this._realmToContext) {
|
||||
if (context.frame === frame) {
|
||||
this._realmToContext.delete(contextId);
|
||||
if (notifyFrame) frame._contextDestroyed(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
_onRealmCreated(realmInfo) {
|
||||
if (this._realmToContext.has(realmInfo.realm)) return;
|
||||
if (realmInfo.type !== 'window') return;
|
||||
const frame = this._page._frameManager.frame(realmInfo.context);
|
||||
if (!frame) return;
|
||||
const delegate = new _bidiExecutionContext.BidiExecutionContext(this._session, realmInfo);
|
||||
let worldName;
|
||||
if (!realmInfo.sandbox) {
|
||||
worldName = 'main';
|
||||
// Force creating utility world every time the main world is created (e.g. due to navigation).
|
||||
this._touchUtilityWorld(realmInfo.context);
|
||||
} else if (realmInfo.sandbox === UTILITY_WORLD_NAME) {
|
||||
worldName = 'utility';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
const context = new dom.FrameExecutionContext(delegate, frame, worldName);
|
||||
context[contextDelegateSymbol] = delegate;
|
||||
frame._contextCreated(worldName, context);
|
||||
this._realmToContext.set(realmInfo.realm, context);
|
||||
}
|
||||
async _touchUtilityWorld(context) {
|
||||
await this._session.sendMayFail('script.evaluate', {
|
||||
expression: '1 + 1',
|
||||
target: {
|
||||
context,
|
||||
sandbox: UTILITY_WORLD_NAME
|
||||
},
|
||||
serializationOptions: {
|
||||
maxObjectDepth: 10,
|
||||
maxDomDepth: 10
|
||||
},
|
||||
awaitPromise: true,
|
||||
userActivation: true
|
||||
});
|
||||
}
|
||||
_onRealmDestroyed(params) {
|
||||
const context = this._realmToContext.get(params.realm);
|
||||
if (!context) return false;
|
||||
this._realmToContext.delete(params.realm);
|
||||
context.frame._contextDestroyed(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: route the message directly to the browser
|
||||
_onBrowsingContextDestroyed(params) {
|
||||
this._browserContext._browser._onBrowsingContextDestroyed(params);
|
||||
}
|
||||
_onNavigationStarted(params) {
|
||||
const frameId = params.context;
|
||||
this._page._frameManager.frameRequestedNavigation(frameId, params.navigation);
|
||||
const url = params.url.toLowerCase();
|
||||
if (url.startsWith('file:') || url.startsWith('data:') || url === 'about:blank') {
|
||||
// Navigation to file urls doesn't emit network events, so we fire 'commit' event right when navigation is started.
|
||||
// Doing it in domcontentload would be too late as we'd clear frame tree.
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
if (frame) this._page._frameManager.frameCommittedNewDocumentNavigation(frameId, params.url, '', params.navigation, /* initial */false);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: there is no separate event for committed navigation, so we approximate it with responseStarted.
|
||||
_onNavigationResponseStarted(params) {
|
||||
const frameId = params.context;
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
(0, _utils.assert)(frame);
|
||||
this._page._frameManager.frameCommittedNewDocumentNavigation(frameId, params.response.url, '', params.navigation, /* initial */false);
|
||||
// if (!initial)
|
||||
// this._firstNonInitialNavigationCommittedFulfill();
|
||||
}
|
||||
_onDomContentLoaded(params) {
|
||||
const frameId = params.context;
|
||||
this._page._frameManager.frameLifecycleEvent(frameId, 'domcontentloaded');
|
||||
}
|
||||
_onLoad(params) {
|
||||
this._page._frameManager.frameLifecycleEvent(params.context, 'load');
|
||||
}
|
||||
_onNavigationAborted(params) {
|
||||
this._page._frameManager.frameAbortedNavigation(params.context, 'Navigation aborted', params.navigation || undefined);
|
||||
}
|
||||
_onNavigationFailed(params) {
|
||||
this._page._frameManager.frameAbortedNavigation(params.context, 'Navigation failed', params.navigation || undefined);
|
||||
}
|
||||
_onFragmentNavigated(params) {
|
||||
this._page._frameManager.frameCommittedSameDocumentNavigation(params.context, params.url);
|
||||
}
|
||||
_onUserPromptOpened(event) {
|
||||
this._page.emitOnContext(_browserContext.BrowserContext.Events.Dialog, new dialog.Dialog(this._page, event.type, event.message, async (accept, userText) => {
|
||||
await this._session.send('browsingContext.handleUserPrompt', {
|
||||
context: event.context,
|
||||
accept,
|
||||
userText
|
||||
});
|
||||
}, event.defaultValue));
|
||||
}
|
||||
_onLogEntryAdded(params) {
|
||||
var _params$stackTrace;
|
||||
if (params.type !== 'console') return;
|
||||
const entry = params;
|
||||
const context = this._realmToContext.get(params.source.realm);
|
||||
if (!context) return;
|
||||
const callFrame = (_params$stackTrace = params.stackTrace) === null || _params$stackTrace === void 0 ? void 0 : _params$stackTrace.callFrames[0];
|
||||
const location = callFrame !== null && callFrame !== void 0 ? callFrame : {
|
||||
url: '',
|
||||
lineNumber: 1,
|
||||
columnNumber: 1
|
||||
};
|
||||
this._page._addConsoleMessage(entry.method, entry.args.map(arg => context.createHandle({
|
||||
objectId: arg.handle,
|
||||
...arg
|
||||
})), location, params.text || undefined);
|
||||
}
|
||||
async navigateFrame(frame, url, referrer) {
|
||||
const {
|
||||
navigation
|
||||
} = await this._session.send('browsingContext.navigate', {
|
||||
context: frame._id,
|
||||
url
|
||||
});
|
||||
return {
|
||||
newDocumentId: navigation || undefined
|
||||
};
|
||||
}
|
||||
async updateExtraHTTPHeaders() {}
|
||||
async updateEmulateMedia() {}
|
||||
async updateEmulatedViewportSize() {
|
||||
await this._updateViewport();
|
||||
}
|
||||
async updateUserAgent() {}
|
||||
async bringToFront() {
|
||||
await this._session.send('browsingContext.activate', {
|
||||
context: this._session.sessionId
|
||||
});
|
||||
}
|
||||
async _updateViewport() {
|
||||
const options = this._browserContext._options;
|
||||
const deviceSize = this._page.emulatedSize();
|
||||
if (deviceSize === null) return;
|
||||
const viewportSize = deviceSize.viewport;
|
||||
await this._session.send('browsingContext.setViewport', {
|
||||
context: this._session.sessionId,
|
||||
viewport: {
|
||||
width: viewportSize.width,
|
||||
height: viewportSize.height
|
||||
},
|
||||
devicePixelRatio: options.deviceScaleFactor || 1
|
||||
});
|
||||
}
|
||||
async updateRequestInterception() {
|
||||
await this._networkManager.setRequestInterception(this._page.needsRequestInterception());
|
||||
}
|
||||
async updateOffline() {}
|
||||
async updateHttpCredentials() {
|
||||
await this._networkManager.setCredentials(this._browserContext._options.httpCredentials);
|
||||
}
|
||||
async updateFileChooserInterception() {}
|
||||
async reload() {
|
||||
await this._session.send('browsingContext.reload', {
|
||||
context: this._session.sessionId,
|
||||
// ignoreCache: true,
|
||||
wait: bidi.BrowsingContext.ReadinessState.Interactive
|
||||
});
|
||||
}
|
||||
async goBack() {
|
||||
return await this._session.send('browsingContext.traverseHistory', {
|
||||
context: this._session.sessionId,
|
||||
delta: -1
|
||||
}).then(() => true).catch(() => false);
|
||||
}
|
||||
async goForward() {
|
||||
return await this._session.send('browsingContext.traverseHistory', {
|
||||
context: this._session.sessionId,
|
||||
delta: +1
|
||||
}).then(() => true).catch(() => false);
|
||||
}
|
||||
async requestGC() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
// TODO: consider calling this only when bindings are added.
|
||||
async _installMainBinding() {
|
||||
const functionDeclaration = addMainBinding.toString();
|
||||
const args = [{
|
||||
type: 'channel',
|
||||
value: {
|
||||
channel: kPlaywrightBindingChannel,
|
||||
ownership: bidi.Script.ResultOwnership.Root
|
||||
}
|
||||
}];
|
||||
const promises = [];
|
||||
promises.push(this._session.send('script.addPreloadScript', {
|
||||
functionDeclaration,
|
||||
arguments: args
|
||||
}));
|
||||
promises.push(this._session.send('script.callFunction', {
|
||||
functionDeclaration,
|
||||
arguments: args,
|
||||
target: toBidiExecutionContext(await this._page.mainFrame()._mainContext())._target,
|
||||
awaitPromise: false,
|
||||
userActivation: false
|
||||
}));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
async _onScriptMessage(event) {
|
||||
if (event.channel !== kPlaywrightBindingChannel) return;
|
||||
const pageOrError = await this.pageOrError();
|
||||
if (pageOrError instanceof Error) return;
|
||||
const context = this._realmToContext.get(event.source.realm);
|
||||
if (!context) return;
|
||||
if (event.data.type !== 'string') return;
|
||||
await this._page._onBindingCalled(event.data.value, context);
|
||||
}
|
||||
async addInitScript(initScript) {
|
||||
const {
|
||||
script
|
||||
} = await this._session.send('script.addPreloadScript', {
|
||||
// TODO: remove function call from the source.
|
||||
functionDeclaration: `() => { return ${initScript.source} }`,
|
||||
// TODO: push to iframes?
|
||||
contexts: [this._session.sessionId]
|
||||
});
|
||||
if (!initScript.internal) this._initScriptIds.push(script);
|
||||
}
|
||||
async removeNonInternalInitScripts() {
|
||||
const promises = this._initScriptIds.map(script => this._session.send('script.removePreloadScript', {
|
||||
script
|
||||
}));
|
||||
this._initScriptIds = [];
|
||||
await Promise.all(promises);
|
||||
}
|
||||
async closePage(runBeforeUnload) {
|
||||
await this._session.send('browsingContext.close', {
|
||||
context: this._session.sessionId,
|
||||
promptUnload: runBeforeUnload
|
||||
});
|
||||
}
|
||||
async setBackgroundColor(color) {}
|
||||
async takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, scale) {
|
||||
const rect = documentRect || viewportRect;
|
||||
const {
|
||||
data
|
||||
} = await this._session.send('browsingContext.captureScreenshot', {
|
||||
context: this._session.sessionId,
|
||||
format: {
|
||||
type: `image/${format === 'png' ? 'png' : 'jpeg'}`,
|
||||
quality: quality || 80
|
||||
},
|
||||
origin: documentRect ? 'document' : 'viewport',
|
||||
clip: {
|
||||
type: 'box',
|
||||
...rect
|
||||
}
|
||||
});
|
||||
return Buffer.from(data, 'base64');
|
||||
}
|
||||
async getContentFrame(handle) {
|
||||
const executionContext = toBidiExecutionContext(handle._context);
|
||||
const contentWindow = await executionContext.rawCallFunction('e => e.contentWindow', {
|
||||
handle: handle._objectId
|
||||
});
|
||||
if (contentWindow.type === 'window') {
|
||||
const frameId = contentWindow.value.context;
|
||||
const result = this._page._frameManager.frame(frameId);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async getOwnerFrame(handle) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
isElementHandle(remoteObject) {
|
||||
return remoteObject.type === 'node';
|
||||
}
|
||||
async getBoundingBox(handle) {
|
||||
const box = await handle.evaluate(element => {
|
||||
if (!(element instanceof Element)) return null;
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
};
|
||||
});
|
||||
if (!box) return null;
|
||||
const position = await this._framePosition(handle._frame);
|
||||
if (!position) return null;
|
||||
box.x += position.x;
|
||||
box.y += position.y;
|
||||
return box;
|
||||
}
|
||||
|
||||
// TODO: move to Frame.
|
||||
async _framePosition(frame) {
|
||||
if (frame === this._page.mainFrame()) return {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
const element = await frame.frameElement();
|
||||
const box = await element.boundingBox();
|
||||
if (!box) return null;
|
||||
const style = await element.evaluateInUtility(([injected, iframe]) => injected.describeIFrameStyle(iframe), {}).catch(e => 'error:notconnected');
|
||||
if (style === 'error:notconnected' || style === 'transformed') return null;
|
||||
// Content box is offset by border and padding widths.
|
||||
box.x += style.left;
|
||||
box.y += style.top;
|
||||
return box;
|
||||
}
|
||||
async scrollRectIntoViewIfNeeded(handle, rect) {
|
||||
return await handle.evaluateInUtility(([injected, node]) => {
|
||||
node.scrollIntoView({
|
||||
block: 'center',
|
||||
inline: 'center',
|
||||
behavior: 'instant'
|
||||
});
|
||||
}, null).then(() => 'done').catch(e => {
|
||||
if (e instanceof Error && e.message.includes('Node is detached from document')) return 'error:notconnected';
|
||||
if (e instanceof Error && e.message.includes('Node does not have a layout object')) return 'error:notvisible';
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
async setScreencastOptions(options) {}
|
||||
rafCountForStablePosition() {
|
||||
return 1;
|
||||
}
|
||||
async getContentQuads(handle) {
|
||||
const quads = await handle.evaluateInUtility(([injected, node]) => {
|
||||
if (!node.isConnected) return 'error:notconnected';
|
||||
const rects = node.getClientRects();
|
||||
if (!rects) return null;
|
||||
return [...rects].map(rect => [{
|
||||
x: rect.left,
|
||||
y: rect.top
|
||||
}, {
|
||||
x: rect.right,
|
||||
y: rect.top
|
||||
}, {
|
||||
x: rect.right,
|
||||
y: rect.bottom
|
||||
}, {
|
||||
x: rect.left,
|
||||
y: rect.bottom
|
||||
}]);
|
||||
}, null);
|
||||
if (!quads || quads === 'error:notconnected') return quads;
|
||||
// TODO: consider transforming quads to support clicks in iframes.
|
||||
const position = await this._framePosition(handle._frame);
|
||||
if (!position) return null;
|
||||
quads.forEach(quad => quad.forEach(point => {
|
||||
point.x += position.x;
|
||||
point.y += position.y;
|
||||
}));
|
||||
return quads;
|
||||
}
|
||||
async setInputFiles(handle, files) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async setInputFilePaths(handle, paths) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async adoptElementHandle(handle, to) {
|
||||
const fromContext = toBidiExecutionContext(handle._context);
|
||||
const shared = await fromContext.rawCallFunction('x => x', {
|
||||
handle: handle._objectId
|
||||
});
|
||||
// TODO: store sharedId in the handle.
|
||||
if (!('sharedId' in shared)) throw new Error('Element is not a node');
|
||||
const sharedId = shared.sharedId;
|
||||
const executionContext = toBidiExecutionContext(to);
|
||||
const result = await executionContext.rawCallFunction('x => x', {
|
||||
sharedId
|
||||
});
|
||||
if ('handle' in result) return to.createHandle({
|
||||
objectId: result.handle,
|
||||
...result
|
||||
});
|
||||
throw new Error('Failed to adopt element handle.');
|
||||
}
|
||||
async getAccessibilityTree(needle) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async inputActionEpilogue() {}
|
||||
async resetForReuse() {}
|
||||
async pdf(options) {
|
||||
return this._pdf.generate(options);
|
||||
}
|
||||
async getFrameElement(frame) {
|
||||
const parent = frame.parentFrame();
|
||||
if (!parent) throw new Error('Frame has been detached.');
|
||||
const parentContext = await parent._mainContext();
|
||||
const list = await parentContext.evaluateHandle(() => {
|
||||
return [...document.querySelectorAll('iframe,frame')];
|
||||
});
|
||||
const length = await list.evaluate(list => list.length);
|
||||
let foundElement = null;
|
||||
for (let i = 0; i < length; i++) {
|
||||
const element = await list.evaluateHandle((list, i) => list[i], i);
|
||||
const candidate = await element.contentFrame();
|
||||
if (frame === candidate) {
|
||||
foundElement = element;
|
||||
break;
|
||||
} else {
|
||||
element.dispose();
|
||||
}
|
||||
}
|
||||
list.dispose();
|
||||
if (!foundElement) throw new Error('Frame has been detached.');
|
||||
return foundElement;
|
||||
}
|
||||
shouldToggleStyleSheetToSyncAnimations() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
exports.BidiPage = BidiPage;
|
||||
function addMainBinding(callback) {
|
||||
globalThis['__playwright__binding__'] = callback;
|
||||
}
|
||||
function toBidiExecutionContext(executionContext) {
|
||||
return executionContext[contextDelegateSymbol];
|
||||
}
|
||||
const contextDelegateSymbol = Symbol('delegate');
|
||||
140
node_modules/playwright-core/lib/server/bidi/bidiPdf.js
generated
vendored
Normal file
140
node_modules/playwright-core/lib/server/bidi/bidiPdf.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiPDF = void 0;
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const PagePaperFormats = {
|
||||
letter: {
|
||||
width: 8.5,
|
||||
height: 11
|
||||
},
|
||||
legal: {
|
||||
width: 8.5,
|
||||
height: 14
|
||||
},
|
||||
tabloid: {
|
||||
width: 11,
|
||||
height: 17
|
||||
},
|
||||
ledger: {
|
||||
width: 17,
|
||||
height: 11
|
||||
},
|
||||
a0: {
|
||||
width: 33.1,
|
||||
height: 46.8
|
||||
},
|
||||
a1: {
|
||||
width: 23.4,
|
||||
height: 33.1
|
||||
},
|
||||
a2: {
|
||||
width: 16.54,
|
||||
height: 23.4
|
||||
},
|
||||
a3: {
|
||||
width: 11.7,
|
||||
height: 16.54
|
||||
},
|
||||
a4: {
|
||||
width: 8.27,
|
||||
height: 11.7
|
||||
},
|
||||
a5: {
|
||||
width: 5.83,
|
||||
height: 8.27
|
||||
},
|
||||
a6: {
|
||||
width: 4.13,
|
||||
height: 5.83
|
||||
}
|
||||
};
|
||||
const unitToPixels = {
|
||||
'px': 1,
|
||||
'in': 96,
|
||||
'cm': 37.8,
|
||||
'mm': 3.78
|
||||
};
|
||||
function convertPrintParameterToInches(text) {
|
||||
if (text === undefined) return undefined;
|
||||
let unit = text.substring(text.length - 2).toLowerCase();
|
||||
let valueText = '';
|
||||
if (unitToPixels.hasOwnProperty(unit)) {
|
||||
valueText = text.substring(0, text.length - 2);
|
||||
} else {
|
||||
// In case of unknown unit try to parse the whole parameter as number of pixels.
|
||||
// This is consistent with phantom's paperSize behavior.
|
||||
unit = 'px';
|
||||
valueText = text;
|
||||
}
|
||||
const value = Number(valueText);
|
||||
(0, _utils.assert)(!isNaN(value), 'Failed to parse parameter value: ' + text);
|
||||
const pixels = value * unitToPixels[unit];
|
||||
return pixels / 96;
|
||||
}
|
||||
class BidiPDF {
|
||||
constructor(session) {
|
||||
this._session = void 0;
|
||||
this._session = session;
|
||||
}
|
||||
async generate(options) {
|
||||
const {
|
||||
scale = 1,
|
||||
printBackground = false,
|
||||
landscape = false,
|
||||
pageRanges = '',
|
||||
margin = {}
|
||||
} = options;
|
||||
let paperWidth = 8.5;
|
||||
let paperHeight = 11;
|
||||
if (options.format) {
|
||||
const format = PagePaperFormats[options.format.toLowerCase()];
|
||||
(0, _utils.assert)(format, 'Unknown paper format: ' + options.format);
|
||||
paperWidth = format.width;
|
||||
paperHeight = format.height;
|
||||
} else {
|
||||
paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
|
||||
paperHeight = convertPrintParameterToInches(options.height) || paperHeight;
|
||||
}
|
||||
const {
|
||||
data
|
||||
} = await this._session.send('browsingContext.print', {
|
||||
context: this._session.sessionId,
|
||||
background: printBackground,
|
||||
margin: {
|
||||
bottom: convertPrintParameterToInches(margin.bottom) || 0,
|
||||
left: convertPrintParameterToInches(margin.left) || 0,
|
||||
right: convertPrintParameterToInches(margin.right) || 0,
|
||||
top: convertPrintParameterToInches(margin.top) || 0
|
||||
},
|
||||
orientation: landscape ? 'landscape' : 'portrait',
|
||||
page: {
|
||||
width: paperWidth,
|
||||
height: paperHeight
|
||||
},
|
||||
pageRanges: pageRanges ? pageRanges.split(',').map(r => r.trim()) : undefined,
|
||||
scale
|
||||
});
|
||||
return Buffer.from(data, 'base64');
|
||||
}
|
||||
}
|
||||
exports.BidiPDF = BidiPDF;
|
||||
93
node_modules/playwright-core/lib/server/bidi/third_party/bidiDeserializer.js
generated
vendored
Normal file
93
node_modules/playwright-core/lib/server/bidi/third_party/bidiDeserializer.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BidiDeserializer = void 0;
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable object-curly-spacing */
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class BidiDeserializer {
|
||||
static deserialize(result) {
|
||||
var _result$value, _result$value2, _result$value3, _result$value4;
|
||||
if (!result) return undefined;
|
||||
switch (result.type) {
|
||||
case 'array':
|
||||
return (_result$value = result.value) === null || _result$value === void 0 ? void 0 : _result$value.map(value => {
|
||||
return BidiDeserializer.deserialize(value);
|
||||
});
|
||||
case 'set':
|
||||
return (_result$value2 = result.value) === null || _result$value2 === void 0 ? void 0 : _result$value2.reduce((acc, value) => {
|
||||
return acc.add(BidiDeserializer.deserialize(value));
|
||||
}, new Set());
|
||||
case 'object':
|
||||
return (_result$value3 = result.value) === null || _result$value3 === void 0 ? void 0 : _result$value3.reduce((acc, tuple) => {
|
||||
const {
|
||||
key,
|
||||
value
|
||||
} = BidiDeserializer._deserializeTuple(tuple);
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {});
|
||||
case 'map':
|
||||
return (_result$value4 = result.value) === null || _result$value4 === void 0 ? void 0 : _result$value4.reduce((acc, tuple) => {
|
||||
const {
|
||||
key,
|
||||
value
|
||||
} = BidiDeserializer._deserializeTuple(tuple);
|
||||
return acc.set(key, value);
|
||||
}, new Map());
|
||||
case 'promise':
|
||||
return {};
|
||||
case 'regexp':
|
||||
return new RegExp(result.value.pattern, result.value.flags);
|
||||
case 'date':
|
||||
return new Date(result.value);
|
||||
case 'undefined':
|
||||
return undefined;
|
||||
case 'null':
|
||||
return null;
|
||||
case 'number':
|
||||
return BidiDeserializer._deserializeNumber(result.value);
|
||||
case 'bigint':
|
||||
return BigInt(result.value);
|
||||
case 'boolean':
|
||||
return Boolean(result.value);
|
||||
case 'string':
|
||||
return result.value;
|
||||
}
|
||||
throw new Error(`Deserialization of type ${result.type} not supported.`);
|
||||
}
|
||||
static _deserializeNumber(value) {
|
||||
switch (value) {
|
||||
case '-0':
|
||||
return -0;
|
||||
case 'NaN':
|
||||
return NaN;
|
||||
case 'Infinity':
|
||||
return Infinity;
|
||||
case '-Infinity':
|
||||
return -Infinity;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
static _deserializeTuple([serializedKey, serializedValue]) {
|
||||
const key = typeof serializedKey === 'string' ? serializedKey : BidiDeserializer.deserialize(serializedKey);
|
||||
const value = BidiDeserializer.deserialize(serializedValue);
|
||||
return {
|
||||
key,
|
||||
value
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.BidiDeserializer = BidiDeserializer;
|
||||
238
node_modules/playwright-core/lib/server/bidi/third_party/bidiKeyboard.js
generated
vendored
Normal file
238
node_modules/playwright-core/lib/server/bidi/third_party/bidiKeyboard.js
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getBidiKeyValue = void 0;
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable curly */
|
||||
|
||||
const getBidiKeyValue = key => {
|
||||
switch (key) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
key = 'Enter';
|
||||
break;
|
||||
}
|
||||
// Measures the number of code points rather than UTF-16 code units.
|
||||
if ([...key].length === 1) {
|
||||
return key;
|
||||
}
|
||||
switch (key) {
|
||||
case 'Cancel':
|
||||
return '\uE001';
|
||||
case 'Help':
|
||||
return '\uE002';
|
||||
case 'Backspace':
|
||||
return '\uE003';
|
||||
case 'Tab':
|
||||
return '\uE004';
|
||||
case 'Clear':
|
||||
return '\uE005';
|
||||
case 'Enter':
|
||||
return '\uE007';
|
||||
case 'Shift':
|
||||
case 'ShiftLeft':
|
||||
return '\uE008';
|
||||
case 'Control':
|
||||
case 'ControlLeft':
|
||||
return '\uE009';
|
||||
case 'Alt':
|
||||
case 'AltLeft':
|
||||
return '\uE00A';
|
||||
case 'Pause':
|
||||
return '\uE00B';
|
||||
case 'Escape':
|
||||
return '\uE00C';
|
||||
case 'PageUp':
|
||||
return '\uE00E';
|
||||
case 'PageDown':
|
||||
return '\uE00F';
|
||||
case 'End':
|
||||
return '\uE010';
|
||||
case 'Home':
|
||||
return '\uE011';
|
||||
case 'ArrowLeft':
|
||||
return '\uE012';
|
||||
case 'ArrowUp':
|
||||
return '\uE013';
|
||||
case 'ArrowRight':
|
||||
return '\uE014';
|
||||
case 'ArrowDown':
|
||||
return '\uE015';
|
||||
case 'Insert':
|
||||
return '\uE016';
|
||||
case 'Delete':
|
||||
return '\uE017';
|
||||
case 'NumpadEqual':
|
||||
return '\uE019';
|
||||
case 'Numpad0':
|
||||
return '\uE01A';
|
||||
case 'Numpad1':
|
||||
return '\uE01B';
|
||||
case 'Numpad2':
|
||||
return '\uE01C';
|
||||
case 'Numpad3':
|
||||
return '\uE01D';
|
||||
case 'Numpad4':
|
||||
return '\uE01E';
|
||||
case 'Numpad5':
|
||||
return '\uE01F';
|
||||
case 'Numpad6':
|
||||
return '\uE020';
|
||||
case 'Numpad7':
|
||||
return '\uE021';
|
||||
case 'Numpad8':
|
||||
return '\uE022';
|
||||
case 'Numpad9':
|
||||
return '\uE023';
|
||||
case 'NumpadMultiply':
|
||||
return '\uE024';
|
||||
case 'NumpadAdd':
|
||||
return '\uE025';
|
||||
case 'NumpadSubtract':
|
||||
return '\uE027';
|
||||
case 'NumpadDecimal':
|
||||
return '\uE028';
|
||||
case 'NumpadDivide':
|
||||
return '\uE029';
|
||||
case 'F1':
|
||||
return '\uE031';
|
||||
case 'F2':
|
||||
return '\uE032';
|
||||
case 'F3':
|
||||
return '\uE033';
|
||||
case 'F4':
|
||||
return '\uE034';
|
||||
case 'F5':
|
||||
return '\uE035';
|
||||
case 'F6':
|
||||
return '\uE036';
|
||||
case 'F7':
|
||||
return '\uE037';
|
||||
case 'F8':
|
||||
return '\uE038';
|
||||
case 'F9':
|
||||
return '\uE039';
|
||||
case 'F10':
|
||||
return '\uE03A';
|
||||
case 'F11':
|
||||
return '\uE03B';
|
||||
case 'F12':
|
||||
return '\uE03C';
|
||||
case 'Meta':
|
||||
case 'MetaLeft':
|
||||
return '\uE03D';
|
||||
case 'ShiftRight':
|
||||
return '\uE050';
|
||||
case 'ControlRight':
|
||||
return '\uE051';
|
||||
case 'AltRight':
|
||||
return '\uE052';
|
||||
case 'MetaRight':
|
||||
return '\uE053';
|
||||
case 'Digit0':
|
||||
return '0';
|
||||
case 'Digit1':
|
||||
return '1';
|
||||
case 'Digit2':
|
||||
return '2';
|
||||
case 'Digit3':
|
||||
return '3';
|
||||
case 'Digit4':
|
||||
return '4';
|
||||
case 'Digit5':
|
||||
return '5';
|
||||
case 'Digit6':
|
||||
return '6';
|
||||
case 'Digit7':
|
||||
return '7';
|
||||
case 'Digit8':
|
||||
return '8';
|
||||
case 'Digit9':
|
||||
return '9';
|
||||
case 'KeyA':
|
||||
return 'a';
|
||||
case 'KeyB':
|
||||
return 'b';
|
||||
case 'KeyC':
|
||||
return 'c';
|
||||
case 'KeyD':
|
||||
return 'd';
|
||||
case 'KeyE':
|
||||
return 'e';
|
||||
case 'KeyF':
|
||||
return 'f';
|
||||
case 'KeyG':
|
||||
return 'g';
|
||||
case 'KeyH':
|
||||
return 'h';
|
||||
case 'KeyI':
|
||||
return 'i';
|
||||
case 'KeyJ':
|
||||
return 'j';
|
||||
case 'KeyK':
|
||||
return 'k';
|
||||
case 'KeyL':
|
||||
return 'l';
|
||||
case 'KeyM':
|
||||
return 'm';
|
||||
case 'KeyN':
|
||||
return 'n';
|
||||
case 'KeyO':
|
||||
return 'o';
|
||||
case 'KeyP':
|
||||
return 'p';
|
||||
case 'KeyQ':
|
||||
return 'q';
|
||||
case 'KeyR':
|
||||
return 'r';
|
||||
case 'KeyS':
|
||||
return 's';
|
||||
case 'KeyT':
|
||||
return 't';
|
||||
case 'KeyU':
|
||||
return 'u';
|
||||
case 'KeyV':
|
||||
return 'v';
|
||||
case 'KeyW':
|
||||
return 'w';
|
||||
case 'KeyX':
|
||||
return 'x';
|
||||
case 'KeyY':
|
||||
return 'y';
|
||||
case 'KeyZ':
|
||||
return 'z';
|
||||
case 'Semicolon':
|
||||
return ';';
|
||||
case 'Equal':
|
||||
return '=';
|
||||
case 'Comma':
|
||||
return ',';
|
||||
case 'Minus':
|
||||
return '-';
|
||||
case 'Period':
|
||||
return '.';
|
||||
case 'Slash':
|
||||
return '/';
|
||||
case 'Backquote':
|
||||
return '`';
|
||||
case 'BracketLeft':
|
||||
return '[';
|
||||
case 'Backslash':
|
||||
return '\\';
|
||||
case 'BracketRight':
|
||||
return ']';
|
||||
case 'Quote':
|
||||
return '"';
|
||||
default:
|
||||
throw new Error(`Unknown key: "${key}"`);
|
||||
}
|
||||
};
|
||||
exports.getBidiKeyValue = getBidiKeyValue;
|
||||
139
node_modules/playwright-core/lib/server/bidi/third_party/bidiProtocol.js
generated
vendored
Normal file
139
node_modules/playwright-core/lib/server/bidi/third_party/bidiProtocol.js
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Storage = exports.Session = exports.Script = exports.Network = exports.Log = exports.Input = exports.ErrorCode = exports.BrowsingContext = exports.Browser = void 0;
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/**
|
||||
* THIS FILE IS AUTOGENERATED by cddlconv 0.1.5.
|
||||
* Run `node tools/generate-bidi-types.mjs` to regenerate.
|
||||
* @see https://github.com/w3c/webdriver-bidi/blob/master/index.bs
|
||||
*/
|
||||
/**
|
||||
* Must be between `-9007199254740991` and `9007199254740991`, inclusive.
|
||||
*/
|
||||
/**
|
||||
* Must be between `0` and `9007199254740991`, inclusive.
|
||||
*/
|
||||
let ErrorCode = exports.ErrorCode = /*#__PURE__*/function (ErrorCode) {
|
||||
ErrorCode["InvalidArgument"] = "invalid argument";
|
||||
ErrorCode["InvalidSelector"] = "invalid selector";
|
||||
ErrorCode["InvalidSessionId"] = "invalid session id";
|
||||
ErrorCode["MoveTargetOutOfBounds"] = "move target out of bounds";
|
||||
ErrorCode["NoSuchAlert"] = "no such alert";
|
||||
ErrorCode["NoSuchElement"] = "no such element";
|
||||
ErrorCode["NoSuchFrame"] = "no such frame";
|
||||
ErrorCode["NoSuchHandle"] = "no such handle";
|
||||
ErrorCode["NoSuchHistoryEntry"] = "no such history entry";
|
||||
ErrorCode["NoSuchIntercept"] = "no such intercept";
|
||||
ErrorCode["NoSuchNode"] = "no such node";
|
||||
ErrorCode["NoSuchRequest"] = "no such request";
|
||||
ErrorCode["NoSuchScript"] = "no such script";
|
||||
ErrorCode["NoSuchStoragePartition"] = "no such storage partition";
|
||||
ErrorCode["NoSuchUserContext"] = "no such user context";
|
||||
ErrorCode["SessionNotCreated"] = "session not created";
|
||||
ErrorCode["UnableToCaptureScreen"] = "unable to capture screen";
|
||||
ErrorCode["UnableToCloseBrowser"] = "unable to close browser";
|
||||
ErrorCode["UnableToSetCookie"] = "unable to set cookie";
|
||||
ErrorCode["UnableToSetFileInput"] = "unable to set file input";
|
||||
ErrorCode["UnderspecifiedStoragePartition"] = "underspecified storage partition";
|
||||
ErrorCode["UnknownCommand"] = "unknown command";
|
||||
ErrorCode["UnknownError"] = "unknown error";
|
||||
ErrorCode["UnsupportedOperation"] = "unsupported operation";
|
||||
return ErrorCode;
|
||||
}({});
|
||||
let Session = exports.Session = void 0;
|
||||
(function (_Session10) {
|
||||
let UserPromptHandlerType = /*#__PURE__*/function (UserPromptHandlerType) {
|
||||
UserPromptHandlerType["Accept"] = "accept";
|
||||
UserPromptHandlerType["Dismiss"] = "dismiss";
|
||||
UserPromptHandlerType["Ignore"] = "ignore";
|
||||
return UserPromptHandlerType;
|
||||
}({});
|
||||
_Session10.UserPromptHandlerType = UserPromptHandlerType;
|
||||
})(Session || (exports.Session = Session = {}));
|
||||
let Browser = exports.Browser = void 0;
|
||||
let BrowsingContext = exports.BrowsingContext = void 0;
|
||||
(function (_BrowsingContext10) {
|
||||
let ReadinessState = /*#__PURE__*/function (ReadinessState) {
|
||||
ReadinessState["None"] = "none";
|
||||
ReadinessState["Interactive"] = "interactive";
|
||||
ReadinessState["Complete"] = "complete";
|
||||
return ReadinessState;
|
||||
}({});
|
||||
_BrowsingContext10.ReadinessState = ReadinessState;
|
||||
})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
|
||||
(function (_BrowsingContext11) {
|
||||
let UserPromptType = /*#__PURE__*/function (UserPromptType) {
|
||||
UserPromptType["Alert"] = "alert";
|
||||
UserPromptType["Beforeunload"] = "beforeunload";
|
||||
UserPromptType["Confirm"] = "confirm";
|
||||
UserPromptType["Prompt"] = "prompt";
|
||||
return UserPromptType;
|
||||
}({});
|
||||
_BrowsingContext11.UserPromptType = UserPromptType;
|
||||
})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
|
||||
(function (_BrowsingContext24) {
|
||||
let CreateType = /*#__PURE__*/function (CreateType) {
|
||||
CreateType["Tab"] = "tab";
|
||||
CreateType["Window"] = "window";
|
||||
return CreateType;
|
||||
}({});
|
||||
_BrowsingContext24.CreateType = CreateType;
|
||||
})(BrowsingContext || (exports.BrowsingContext = BrowsingContext = {}));
|
||||
let Network = exports.Network = void 0;
|
||||
(function (_Network6) {
|
||||
let SameSite = /*#__PURE__*/function (SameSite) {
|
||||
SameSite["Strict"] = "strict";
|
||||
SameSite["Lax"] = "lax";
|
||||
SameSite["None"] = "none";
|
||||
return SameSite;
|
||||
}({});
|
||||
_Network6.SameSite = SameSite;
|
||||
})(Network || (exports.Network = Network = {}));
|
||||
(function (_Network23) {
|
||||
let InterceptPhase = /*#__PURE__*/function (InterceptPhase) {
|
||||
InterceptPhase["BeforeRequestSent"] = "beforeRequestSent";
|
||||
InterceptPhase["ResponseStarted"] = "responseStarted";
|
||||
InterceptPhase["AuthRequired"] = "authRequired";
|
||||
return InterceptPhase;
|
||||
}({});
|
||||
_Network23.InterceptPhase = InterceptPhase;
|
||||
})(Network || (exports.Network = Network = {}));
|
||||
let Script = exports.Script = void 0;
|
||||
(function (_Script68) {
|
||||
let ResultOwnership = /*#__PURE__*/function (ResultOwnership) {
|
||||
ResultOwnership["Root"] = "root";
|
||||
ResultOwnership["None"] = "none";
|
||||
return ResultOwnership;
|
||||
}({});
|
||||
_Script68.ResultOwnership = ResultOwnership;
|
||||
})(Script || (exports.Script = Script = {}));
|
||||
let Storage = exports.Storage = void 0;
|
||||
let Log = exports.Log = void 0;
|
||||
(function (_Log7) {
|
||||
let Level = /*#__PURE__*/function (Level) {
|
||||
Level["Debug"] = "debug";
|
||||
Level["Info"] = "info";
|
||||
Level["Warn"] = "warn";
|
||||
Level["Error"] = "error";
|
||||
return Level;
|
||||
}({});
|
||||
_Log7.Level = Level;
|
||||
})(Log || (exports.Log = Log = {}));
|
||||
let Input = exports.Input = void 0;
|
||||
(function (_Input9) {
|
||||
let PointerType = /*#__PURE__*/function (PointerType) {
|
||||
PointerType["Mouse"] = "mouse";
|
||||
PointerType["Pen"] = "pen";
|
||||
PointerType["Touch"] = "touch";
|
||||
return PointerType;
|
||||
}({});
|
||||
_Input9.PointerType = PointerType;
|
||||
})(Input || (exports.Input = Input = {}));
|
||||
144
node_modules/playwright-core/lib/server/bidi/third_party/bidiSerializer.js
generated
vendored
Normal file
144
node_modules/playwright-core/lib/server/bidi/third_party/bidiSerializer.js
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.isRegExp = exports.isPlainObject = exports.isDate = exports.BidiSerializer = void 0;
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable curly, indent */
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class UnserializableError extends Error {}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class BidiSerializer {
|
||||
static serialize(arg) {
|
||||
switch (typeof arg) {
|
||||
case 'symbol':
|
||||
case 'function':
|
||||
throw new UnserializableError(`Unable to serializable ${typeof arg}`);
|
||||
case 'object':
|
||||
return BidiSerializer._serializeObject(arg);
|
||||
case 'undefined':
|
||||
return {
|
||||
type: 'undefined'
|
||||
};
|
||||
case 'number':
|
||||
return BidiSerializer._serializeNumber(arg);
|
||||
case 'bigint':
|
||||
return {
|
||||
type: 'bigint',
|
||||
value: arg.toString()
|
||||
};
|
||||
case 'string':
|
||||
return {
|
||||
type: 'string',
|
||||
value: arg
|
||||
};
|
||||
case 'boolean':
|
||||
return {
|
||||
type: 'boolean',
|
||||
value: arg
|
||||
};
|
||||
}
|
||||
}
|
||||
static _serializeNumber(arg) {
|
||||
let value;
|
||||
if (Object.is(arg, -0)) {
|
||||
value = '-0';
|
||||
} else if (Object.is(arg, Infinity)) {
|
||||
value = 'Infinity';
|
||||
} else if (Object.is(arg, -Infinity)) {
|
||||
value = '-Infinity';
|
||||
} else if (Object.is(arg, NaN)) {
|
||||
value = 'NaN';
|
||||
} else {
|
||||
value = arg;
|
||||
}
|
||||
return {
|
||||
type: 'number',
|
||||
value
|
||||
};
|
||||
}
|
||||
static _serializeObject(arg) {
|
||||
if (arg === null) {
|
||||
return {
|
||||
type: 'null'
|
||||
};
|
||||
} else if (Array.isArray(arg)) {
|
||||
const parsedArray = arg.map(subArg => {
|
||||
return BidiSerializer.serialize(subArg);
|
||||
});
|
||||
return {
|
||||
type: 'array',
|
||||
value: parsedArray
|
||||
};
|
||||
} else if (isPlainObject(arg)) {
|
||||
try {
|
||||
JSON.stringify(arg);
|
||||
} catch (error) {
|
||||
if (error instanceof TypeError && error.message.startsWith('Converting circular structure to JSON')) {
|
||||
error.message += ' Recursive objects are not allowed.';
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const parsedObject = [];
|
||||
for (const key in arg) {
|
||||
parsedObject.push([BidiSerializer.serialize(key), BidiSerializer.serialize(arg[key])]);
|
||||
}
|
||||
return {
|
||||
type: 'object',
|
||||
value: parsedObject
|
||||
};
|
||||
} else if (isRegExp(arg)) {
|
||||
return {
|
||||
type: 'regexp',
|
||||
value: {
|
||||
pattern: arg.source,
|
||||
flags: arg.flags
|
||||
}
|
||||
};
|
||||
} else if (isDate(arg)) {
|
||||
return {
|
||||
type: 'date',
|
||||
value: arg.toISOString()
|
||||
};
|
||||
}
|
||||
throw new UnserializableError('Custom object serialization not possible. Use plain objects instead.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
exports.BidiSerializer = BidiSerializer;
|
||||
const isPlainObject = obj => {
|
||||
return typeof obj === 'object' && (obj === null || obj === void 0 ? void 0 : obj.constructor) === Object;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
exports.isPlainObject = isPlainObject;
|
||||
const isRegExp = obj => {
|
||||
return typeof obj === 'object' && (obj === null || obj === void 0 ? void 0 : obj.constructor) === RegExp;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
exports.isRegExp = isRegExp;
|
||||
const isDate = obj => {
|
||||
return typeof obj === 'object' && (obj === null || obj === void 0 ? void 0 : obj.constructor) === Date;
|
||||
};
|
||||
exports.isDate = isDate;
|
||||
221
node_modules/playwright-core/lib/server/bidi/third_party/firefoxPrefs.js
generated
vendored
Normal file
221
node_modules/playwright-core/lib/server/bidi/third_party/firefoxPrefs.js
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.createProfile = createProfile;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2023 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable curly, indent */
|
||||
|
||||
async function createProfile(options) {
|
||||
if (!_fs.default.existsSync(options.path)) {
|
||||
await _fs.default.promises.mkdir(options.path, {
|
||||
recursive: true
|
||||
});
|
||||
}
|
||||
await writePreferences({
|
||||
preferences: {
|
||||
...defaultProfilePreferences(options.preferences),
|
||||
...options.preferences
|
||||
},
|
||||
path: options.path
|
||||
});
|
||||
}
|
||||
function defaultProfilePreferences(extraPrefs) {
|
||||
const server = 'dummy.test';
|
||||
const defaultPrefs = {
|
||||
// Make sure Shield doesn't hit the network.
|
||||
'app.normandy.api_url': '',
|
||||
// Disable Firefox old build background check
|
||||
'app.update.checkInstallTime': false,
|
||||
// Disable automatically upgrading Firefox
|
||||
'app.update.disabledForTesting': true,
|
||||
// Increase the APZ content response timeout to 1 minute
|
||||
'apz.content_response_timeout': 60000,
|
||||
// Prevent various error message on the console
|
||||
// jest-puppeteer asserts that no error message is emitted by the console
|
||||
'browser.contentblocking.features.standard': '-tp,tpPrivate,cookieBehavior0,-cm,-fp',
|
||||
// Enable the dump function: which sends messages to the system
|
||||
// console
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1543115
|
||||
'browser.dom.window.dump.enabled': true,
|
||||
// Disable topstories
|
||||
'browser.newtabpage.activity-stream.feeds.system.topstories': false,
|
||||
// Always display a blank page
|
||||
'browser.newtabpage.enabled': false,
|
||||
// Background thumbnails in particular cause grief: and disabling
|
||||
// thumbnails in general cannot hurt
|
||||
'browser.pagethumbnails.capturing_disabled': true,
|
||||
// Disable safebrowsing components.
|
||||
'browser.safebrowsing.blockedURIs.enabled': false,
|
||||
'browser.safebrowsing.downloads.enabled': false,
|
||||
'browser.safebrowsing.malware.enabled': false,
|
||||
'browser.safebrowsing.phishing.enabled': false,
|
||||
// Disable updates to search engines.
|
||||
'browser.search.update': false,
|
||||
// Do not restore the last open set of tabs if the browser has crashed
|
||||
'browser.sessionstore.resume_from_crash': false,
|
||||
// Skip check for default browser on startup
|
||||
'browser.shell.checkDefaultBrowser': false,
|
||||
// Disable newtabpage
|
||||
'browser.startup.homepage': 'about:blank',
|
||||
// Do not redirect user when a milstone upgrade of Firefox is detected
|
||||
'browser.startup.homepage_override.mstone': 'ignore',
|
||||
// Start with a blank page about:blank
|
||||
'browser.startup.page': 0,
|
||||
// Do not allow background tabs to be zombified on Android: otherwise for
|
||||
// tests that open additional tabs: the test harness tab itself might get
|
||||
// unloaded
|
||||
'browser.tabs.disableBackgroundZombification': false,
|
||||
// Do not warn when closing all other open tabs
|
||||
'browser.tabs.warnOnCloseOtherTabs': false,
|
||||
// Do not warn when multiple tabs will be opened
|
||||
'browser.tabs.warnOnOpen': false,
|
||||
// Do not automatically offer translations, as tests do not expect this.
|
||||
'browser.translations.automaticallyPopup': false,
|
||||
// Disable the UI tour.
|
||||
'browser.uitour.enabled': false,
|
||||
// Turn off search suggestions in the location bar so as not to trigger
|
||||
// network connections.
|
||||
'browser.urlbar.suggest.searches': false,
|
||||
// Disable first run splash page on Windows 10
|
||||
'browser.usedOnWindows10.introURL': '',
|
||||
// Do not warn on quitting Firefox
|
||||
'browser.warnOnQuit': false,
|
||||
// Defensively disable data reporting systems
|
||||
'datareporting.healthreport.documentServerURI': `http://${server}/dummy/healthreport/`,
|
||||
'datareporting.healthreport.logging.consoleEnabled': false,
|
||||
'datareporting.healthreport.service.enabled': false,
|
||||
'datareporting.healthreport.service.firstRun': false,
|
||||
'datareporting.healthreport.uploadEnabled': false,
|
||||
// Do not show datareporting policy notifications which can interfere with tests
|
||||
'datareporting.policy.dataSubmissionEnabled': false,
|
||||
'datareporting.policy.dataSubmissionPolicyBypassNotification': true,
|
||||
// DevTools JSONViewer sometimes fails to load dependencies with its require.js.
|
||||
// This doesn't affect Puppeteer but spams console (Bug 1424372)
|
||||
'devtools.jsonview.enabled': false,
|
||||
// Disable popup-blocker
|
||||
'dom.disable_open_during_load': false,
|
||||
// Enable the support for File object creation in the content process
|
||||
// Required for |Page.setFileInputFiles| protocol method.
|
||||
'dom.file.createInChild': true,
|
||||
// Disable the ProcessHangMonitor
|
||||
'dom.ipc.reportProcessHangs': false,
|
||||
// Disable slow script dialogues
|
||||
'dom.max_chrome_script_run_time': 0,
|
||||
'dom.max_script_run_time': 0,
|
||||
// Only load extensions from the application and user profile
|
||||
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
|
||||
'extensions.autoDisableScopes': 0,
|
||||
'extensions.enabledScopes': 5,
|
||||
// Disable metadata caching for installed add-ons by default
|
||||
'extensions.getAddons.cache.enabled': false,
|
||||
// Disable installing any distribution extensions or add-ons.
|
||||
'extensions.installDistroAddons': false,
|
||||
// Disabled screenshots extension
|
||||
'extensions.screenshots.disabled': true,
|
||||
// Turn off extension updates so they do not bother tests
|
||||
'extensions.update.enabled': false,
|
||||
// Turn off extension updates so they do not bother tests
|
||||
'extensions.update.notifyUser': false,
|
||||
// Make sure opening about:addons will not hit the network
|
||||
'extensions.webservice.discoverURL': `http://${server}/dummy/discoveryURL`,
|
||||
// Allow the application to have focus even it runs in the background
|
||||
'focusmanager.testmode': true,
|
||||
// Disable useragent updates
|
||||
'general.useragent.updates.enabled': false,
|
||||
// Always use network provider for geolocation tests so we bypass the
|
||||
// macOS dialog raised by the corelocation provider
|
||||
'geo.provider.testing': true,
|
||||
// Do not scan Wifi
|
||||
'geo.wifi.scan': false,
|
||||
// No hang monitor
|
||||
'hangmonitor.timeout': 0,
|
||||
// Show chrome errors and warnings in the error console
|
||||
'javascript.options.showInConsole': true,
|
||||
// Disable download and usage of OpenH264: and Widevine plugins
|
||||
'media.gmp-manager.updateEnabled': false,
|
||||
// Disable the GFX sanity window
|
||||
'media.sanity-test.disabled': true,
|
||||
// Disable experimental feature that is only available in Nightly
|
||||
'network.cookie.sameSite.laxByDefault': false,
|
||||
// Do not prompt for temporary redirects
|
||||
'network.http.prompt-temp-redirect': false,
|
||||
// Disable speculative connections so they are not reported as leaking
|
||||
// when they are hanging around
|
||||
'network.http.speculative-parallel-limit': 0,
|
||||
// Do not automatically switch between offline and online
|
||||
'network.manage-offline-status': false,
|
||||
// Make sure SNTP requests do not hit the network
|
||||
'network.sntp.pools': server,
|
||||
// Disable Flash.
|
||||
'plugin.state.flash': 0,
|
||||
'privacy.trackingprotection.enabled': false,
|
||||
// Can be removed once Firefox 89 is no longer supported
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1710839
|
||||
'remote.enabled': true,
|
||||
// Don't do network connections for mitm priming
|
||||
'security.certerrors.mitm.priming.enabled': false,
|
||||
// Local documents have access to all other local documents,
|
||||
// including directory listings
|
||||
'security.fileuri.strict_origin_policy': false,
|
||||
// Do not wait for the notification button security delay
|
||||
'security.notification_enable_delay': 0,
|
||||
// Ensure blocklist updates do not hit the network
|
||||
'services.settings.server': `http://${server}/dummy/blocklist/`,
|
||||
// Do not automatically fill sign-in forms with known usernames and
|
||||
// passwords
|
||||
'signon.autofillForms': false,
|
||||
// Disable password capture, so that tests that include forms are not
|
||||
// influenced by the presence of the persistent doorhanger notification
|
||||
'signon.rememberSignons': false,
|
||||
// Disable first-run welcome page
|
||||
'startup.homepage_welcome_url': 'about:blank',
|
||||
// Disable first-run welcome page
|
||||
'startup.homepage_welcome_url.additional': '',
|
||||
// Disable browser animations (tabs, fullscreen, sliding alerts)
|
||||
'toolkit.cosmeticAnimations.enabled': false,
|
||||
// Prevent starting into safe mode after application crashes
|
||||
'toolkit.startup.max_resumed_crashes': -1
|
||||
};
|
||||
return Object.assign(defaultPrefs, extraPrefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the user.js file with custom preferences as needed to allow
|
||||
* Firefox's CDP support to properly function. These preferences will be
|
||||
* automatically copied over to prefs.js during startup of Firefox. To be
|
||||
* able to restore the original values of preferences a backup of prefs.js
|
||||
* will be created.
|
||||
*
|
||||
* @param prefs - List of preferences to add.
|
||||
* @param profilePath - Firefox profile to write the preferences to.
|
||||
*/
|
||||
async function writePreferences(options) {
|
||||
const prefsPath = _path.default.join(options.path, 'prefs.js');
|
||||
const lines = Object.entries(options.preferences).map(([key, value]) => {
|
||||
return `user_pref(${JSON.stringify(key)}, ${JSON.stringify(value)});`;
|
||||
});
|
||||
|
||||
// Use allSettled to prevent corruption
|
||||
const result = await Promise.allSettled([_fs.default.promises.writeFile(_path.default.join(options.path, 'user.js'), lines.join('\n')),
|
||||
// Create a backup of the preferences file if it already exitsts.
|
||||
_fs.default.promises.access(prefsPath, _fs.default.constants.F_OK).then(async () => {
|
||||
await _fs.default.promises.copyFile(prefsPath, _path.default.join(options.path, 'prefs.js.playwright'));
|
||||
},
|
||||
// Swallow only if file does not exist
|
||||
() => {})]);
|
||||
for (const command of result) {
|
||||
if (command.status === 'rejected') {
|
||||
throw command.reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
148
node_modules/playwright-core/lib/server/browser.js
generated
vendored
Normal file
148
node_modules/playwright-core/lib/server/browser.js
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Browser = void 0;
|
||||
var _browserContext = require("./browserContext");
|
||||
var _page = require("./page");
|
||||
var _download = require("./download");
|
||||
var _instrumentation = require("./instrumentation");
|
||||
var _artifact = require("./artifact");
|
||||
var _socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Browser extends _instrumentation.SdkObject {
|
||||
constructor(parent, options) {
|
||||
super(parent, 'browser');
|
||||
this.options = void 0;
|
||||
this._downloads = new Map();
|
||||
this._defaultContext = null;
|
||||
this._startedClosing = false;
|
||||
this._idToVideo = new Map();
|
||||
this._contextForReuse = void 0;
|
||||
this._closeReason = void 0;
|
||||
this._isCollocatedWithServer = true;
|
||||
this.attribution.browser = this;
|
||||
this.options = options;
|
||||
this.instrumentation.onBrowserOpen(this);
|
||||
}
|
||||
async newContext(metadata, options) {
|
||||
var _options$clientCertif;
|
||||
(0, _browserContext.validateBrowserContextOptions)(options, this.options);
|
||||
let clientCertificatesProxy;
|
||||
if ((_options$clientCertif = options.clientCertificates) !== null && _options$clientCertif !== void 0 && _options$clientCertif.length) {
|
||||
clientCertificatesProxy = new _socksClientCertificatesInterceptor.ClientCertificatesProxy(options);
|
||||
options = {
|
||||
...options
|
||||
};
|
||||
options.proxyOverride = await clientCertificatesProxy.listen();
|
||||
options.internalIgnoreHTTPSErrors = true;
|
||||
}
|
||||
let context;
|
||||
try {
|
||||
context = await this.doCreateNewContext(options);
|
||||
} catch (error) {
|
||||
var _clientCertificatesPr;
|
||||
await ((_clientCertificatesPr = clientCertificatesProxy) === null || _clientCertificatesPr === void 0 ? void 0 : _clientCertificatesPr.close());
|
||||
throw error;
|
||||
}
|
||||
context._clientCertificatesProxy = clientCertificatesProxy;
|
||||
if (options.storageState) await context.setStorageState(metadata, options.storageState);
|
||||
return context;
|
||||
}
|
||||
async newContextForReuse(params, metadata) {
|
||||
const hash = _browserContext.BrowserContext.reusableContextHash(params);
|
||||
if (!this._contextForReuse || hash !== this._contextForReuse.hash || !this._contextForReuse.context.canResetForReuse()) {
|
||||
if (this._contextForReuse) await this._contextForReuse.context.close({
|
||||
reason: 'Context reused'
|
||||
});
|
||||
this._contextForReuse = {
|
||||
context: await this.newContext(metadata, params),
|
||||
hash
|
||||
};
|
||||
return {
|
||||
context: this._contextForReuse.context,
|
||||
needsReset: false
|
||||
};
|
||||
}
|
||||
await this._contextForReuse.context.stopPendingOperations('Context recreated');
|
||||
return {
|
||||
context: this._contextForReuse.context,
|
||||
needsReset: true
|
||||
};
|
||||
}
|
||||
async stopPendingOperations(reason) {
|
||||
var _this$_contextForReus;
|
||||
await ((_this$_contextForReus = this._contextForReuse) === null || _this$_contextForReus === void 0 || (_this$_contextForReus = _this$_contextForReus.context) === null || _this$_contextForReus === void 0 ? void 0 : _this$_contextForReus.stopPendingOperations(reason));
|
||||
}
|
||||
_downloadCreated(page, uuid, url, suggestedFilename) {
|
||||
const download = new _download.Download(page, this.options.downloadsPath || '', uuid, url, suggestedFilename);
|
||||
this._downloads.set(uuid, download);
|
||||
}
|
||||
_downloadFilenameSuggested(uuid, suggestedFilename) {
|
||||
const download = this._downloads.get(uuid);
|
||||
if (!download) return;
|
||||
download._filenameSuggested(suggestedFilename);
|
||||
}
|
||||
_downloadFinished(uuid, error) {
|
||||
const download = this._downloads.get(uuid);
|
||||
if (!download) return;
|
||||
download.artifact.reportFinished(error ? new Error(error) : undefined);
|
||||
this._downloads.delete(uuid);
|
||||
}
|
||||
_videoStarted(context, videoId, path, pageOrError) {
|
||||
const artifact = new _artifact.Artifact(context, path);
|
||||
this._idToVideo.set(videoId, {
|
||||
context,
|
||||
artifact
|
||||
});
|
||||
pageOrError.then(page => {
|
||||
if (page instanceof _page.Page) {
|
||||
page._video = artifact;
|
||||
page.emitOnContext(_browserContext.BrowserContext.Events.VideoStarted, artifact);
|
||||
page.emit(_page.Page.Events.Video, artifact);
|
||||
}
|
||||
});
|
||||
}
|
||||
_takeVideo(videoId) {
|
||||
const video = this._idToVideo.get(videoId);
|
||||
this._idToVideo.delete(videoId);
|
||||
return video === null || video === void 0 ? void 0 : video.artifact;
|
||||
}
|
||||
_didClose() {
|
||||
for (const context of this.contexts()) context._browserClosed();
|
||||
if (this._defaultContext) this._defaultContext._browserClosed();
|
||||
this.emit(Browser.Events.Disconnected);
|
||||
this.instrumentation.onBrowserClose(this);
|
||||
}
|
||||
async close(options) {
|
||||
if (!this._startedClosing) {
|
||||
if (options.reason) this._closeReason = options.reason;
|
||||
this._startedClosing = true;
|
||||
await this.options.browserProcess.close();
|
||||
}
|
||||
if (this.isConnected()) await new Promise(x => this.once(Browser.Events.Disconnected, x));
|
||||
}
|
||||
async killForTests() {
|
||||
await this.options.browserProcess.kill();
|
||||
}
|
||||
}
|
||||
exports.Browser = Browser;
|
||||
Browser.Events = {
|
||||
Disconnected: 'disconnected'
|
||||
};
|
||||
667
node_modules/playwright-core/lib/server/browserContext.js
generated
vendored
Normal file
667
node_modules/playwright-core/lib/server/browserContext.js
generated
vendored
Normal file
@@ -0,0 +1,667 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BrowserContext = void 0;
|
||||
exports.assertBrowserContextIsNotOwned = assertBrowserContextIsNotOwned;
|
||||
exports.normalizeProxySettings = normalizeProxySettings;
|
||||
exports.validateBrowserContextOptions = validateBrowserContextOptions;
|
||||
exports.verifyClientCertificates = verifyClientCertificates;
|
||||
exports.verifyGeolocation = verifyGeolocation;
|
||||
var _timeoutSettings = require("../common/timeoutSettings");
|
||||
var _utils = require("../utils");
|
||||
var _fileUtils = require("../utils/fileUtils");
|
||||
var _helper = require("./helper");
|
||||
var network = _interopRequireWildcard(require("./network"));
|
||||
var _page6 = require("./page");
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _instrumentation = require("./instrumentation");
|
||||
var _debugger = require("./debugger");
|
||||
var _tracing = require("./trace/recorder/tracing");
|
||||
var _harRecorder = require("./har/harRecorder");
|
||||
var _recorder = require("./recorder");
|
||||
var consoleApiSource = _interopRequireWildcard(require("../generated/consoleApiSource"));
|
||||
var _fetch = require("./fetch");
|
||||
var _clock = require("./clock");
|
||||
var _recorderApp = require("./recorder/recorderApp");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BrowserContext extends _instrumentation.SdkObject {
|
||||
constructor(browser, options, browserContextId) {
|
||||
super(browser, 'browser-context');
|
||||
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||
this._pageBindings = new Map();
|
||||
this._activeProgressControllers = new Set();
|
||||
this._options = void 0;
|
||||
this._requestInterceptor = void 0;
|
||||
this._isPersistentContext = void 0;
|
||||
this._closedStatus = 'open';
|
||||
this._closePromise = void 0;
|
||||
this._closePromiseFulfill = void 0;
|
||||
this._permissions = new Map();
|
||||
this._downloads = new Set();
|
||||
this._browser = void 0;
|
||||
this._browserContextId = void 0;
|
||||
this._selectors = void 0;
|
||||
this._origins = new Set();
|
||||
this._harRecorders = new Map();
|
||||
this.tracing = void 0;
|
||||
this.fetchRequest = void 0;
|
||||
this._customCloseHandler = void 0;
|
||||
this._tempDirs = [];
|
||||
this._settingStorageState = false;
|
||||
this.initScripts = [];
|
||||
this._routesInFlight = new Set();
|
||||
this._debugger = void 0;
|
||||
this._closeReason = void 0;
|
||||
this.clock = void 0;
|
||||
this._clientCertificatesProxy = void 0;
|
||||
this.attribution.context = this;
|
||||
this._browser = browser;
|
||||
this._options = options;
|
||||
this._browserContextId = browserContextId;
|
||||
this._isPersistentContext = !browserContextId;
|
||||
this._closePromise = new Promise(fulfill => this._closePromiseFulfill = fulfill);
|
||||
this.fetchRequest = new _fetch.BrowserContextAPIRequestContext(this);
|
||||
if (this._options.recordHar) this._harRecorders.set('', new _harRecorder.HarRecorder(this, null, this._options.recordHar));
|
||||
this.tracing = new _tracing.Tracing(this, browser.options.tracesDir);
|
||||
this.clock = new _clock.Clock(this);
|
||||
}
|
||||
isPersistentContext() {
|
||||
return this._isPersistentContext;
|
||||
}
|
||||
setSelectors(selectors) {
|
||||
this._selectors = selectors;
|
||||
}
|
||||
selectors() {
|
||||
return this._selectors || this.attribution.playwright.selectors;
|
||||
}
|
||||
async _initialize() {
|
||||
if (this.attribution.playwright.options.isInternalPlaywright) return;
|
||||
// Debugger will pause execution upon page.pause in headed mode.
|
||||
this._debugger = new _debugger.Debugger(this);
|
||||
|
||||
// When PWDEBUG=1, show inspector for each context.
|
||||
if ((0, _utils.debugMode)() === 'inspector') await _recorder.Recorder.show('actions', this, _recorderApp.RecorderApp.factory(this), {
|
||||
pauseOnNextStatement: true
|
||||
});
|
||||
|
||||
// When paused, show inspector.
|
||||
if (this._debugger.isPaused()) _recorder.Recorder.showInspectorNoReply(this, _recorderApp.RecorderApp.factory(this));
|
||||
this._debugger.on(_debugger.Debugger.Events.PausedStateChanged, () => {
|
||||
if (this._debugger.isPaused()) _recorder.Recorder.showInspectorNoReply(this, _recorderApp.RecorderApp.factory(this));
|
||||
});
|
||||
if ((0, _utils.debugMode)() === 'console') await this.extendInjectedScript(consoleApiSource.source);
|
||||
if (this._options.serviceWorkers === 'block') await this.addInitScript(`\nif (navigator.serviceWorker) navigator.serviceWorker.register = async () => { console.warn('Service Worker registration blocked by Playwright'); };\n`);
|
||||
if (this._options.permissions) await this.grantPermissions(this._options.permissions);
|
||||
}
|
||||
debugger() {
|
||||
return this._debugger;
|
||||
}
|
||||
async _ensureVideosPath() {
|
||||
if (this._options.recordVideo) await (0, _fileUtils.mkdirIfNeeded)(_path.default.join(this._options.recordVideo.dir, 'dummy'));
|
||||
}
|
||||
canResetForReuse() {
|
||||
if (this._closedStatus !== 'open') return false;
|
||||
return true;
|
||||
}
|
||||
async stopPendingOperations(reason) {
|
||||
// When using context reuse, stop pending operations to gracefully terminate all the actions
|
||||
// with a user-friendly error message containing operation log.
|
||||
for (const controller of this._activeProgressControllers) controller.abort(new Error(reason));
|
||||
// Let rejections in microtask generate events before returning.
|
||||
await new Promise(f => setTimeout(f, 0));
|
||||
}
|
||||
static reusableContextHash(params) {
|
||||
const paramsCopy = {
|
||||
...params
|
||||
};
|
||||
for (const k of Object.keys(paramsCopy)) {
|
||||
const key = k;
|
||||
if (paramsCopy[key] === defaultNewContextParamValues[key]) delete paramsCopy[key];
|
||||
}
|
||||
for (const key of paramsThatAllowContextReuse) delete paramsCopy[key];
|
||||
return JSON.stringify(paramsCopy);
|
||||
}
|
||||
async resetForReuse(metadata, params) {
|
||||
var _page, _page2, _page3, _page4, _page5;
|
||||
this.setDefaultNavigationTimeout(undefined);
|
||||
this.setDefaultTimeout(undefined);
|
||||
this.tracing.resetForReuse();
|
||||
if (params) {
|
||||
for (const key of paramsThatAllowContextReuse) this._options[key] = params[key];
|
||||
}
|
||||
await this._cancelAllRoutesInFlight();
|
||||
|
||||
// Close extra pages early.
|
||||
let page = this.pages()[0];
|
||||
const [, ...otherPages] = this.pages();
|
||||
for (const p of otherPages) await p.close(metadata);
|
||||
if (page && page.hasCrashed()) {
|
||||
await page.close(metadata);
|
||||
page = undefined;
|
||||
}
|
||||
|
||||
// Unless dialogs are dismissed, setting extra http headers below does not respond.
|
||||
(_page = page) === null || _page === void 0 || _page._frameManager.setCloseAllOpeningDialogs(true);
|
||||
await ((_page2 = page) === null || _page2 === void 0 ? void 0 : _page2._frameManager.closeOpenDialogs());
|
||||
// Navigate to about:blank first to ensure no page scripts are running after this point.
|
||||
await ((_page3 = page) === null || _page3 === void 0 ? void 0 : _page3.mainFrame().goto(metadata, 'about:blank', {
|
||||
timeout: 0
|
||||
}));
|
||||
(_page4 = page) === null || _page4 === void 0 || _page4._frameManager.setCloseAllOpeningDialogs(false);
|
||||
await this._resetStorage();
|
||||
await this._removeExposedBindings();
|
||||
await this._removeInitScripts();
|
||||
this.clock.markAsUninstalled();
|
||||
// TODO: following can be optimized to not perform noops.
|
||||
if (this._options.permissions) await this.grantPermissions(this._options.permissions);else await this.clearPermissions();
|
||||
await this.setExtraHTTPHeaders(this._options.extraHTTPHeaders || []);
|
||||
await this.setGeolocation(this._options.geolocation);
|
||||
await this.setOffline(!!this._options.offline);
|
||||
await this.setUserAgent(this._options.userAgent);
|
||||
await this.clearCache();
|
||||
await this._resetCookies();
|
||||
await ((_page5 = page) === null || _page5 === void 0 ? void 0 : _page5.resetForReuse(metadata));
|
||||
}
|
||||
_browserClosed() {
|
||||
for (const page of this.pages()) page._didClose();
|
||||
this._didCloseInternal();
|
||||
}
|
||||
_didCloseInternal() {
|
||||
var _this$_clientCertific;
|
||||
if (this._closedStatus === 'closed') {
|
||||
// We can come here twice if we close browser context and browser
|
||||
// at the same time.
|
||||
return;
|
||||
}
|
||||
(_this$_clientCertific = this._clientCertificatesProxy) === null || _this$_clientCertific === void 0 || _this$_clientCertific.close().catch(() => {});
|
||||
this.tracing.abort();
|
||||
if (this._isPersistentContext) this.onClosePersistent();
|
||||
this._closePromiseFulfill(new Error('Context closed'));
|
||||
this.emit(BrowserContext.Events.Close);
|
||||
}
|
||||
|
||||
// BrowserContext methods.
|
||||
|
||||
async cookies(urls = []) {
|
||||
if (urls && !Array.isArray(urls)) urls = [urls];
|
||||
return await this.doGetCookies(urls);
|
||||
}
|
||||
async clearCookies(options) {
|
||||
const currentCookies = await this.cookies();
|
||||
await this.doClearCookies();
|
||||
const matches = (cookie, prop, value) => {
|
||||
if (!value) return true;
|
||||
if (value instanceof RegExp) {
|
||||
value.lastIndex = 0;
|
||||
return value.test(cookie[prop]);
|
||||
}
|
||||
return cookie[prop] === value;
|
||||
};
|
||||
const cookiesToReadd = currentCookies.filter(cookie => {
|
||||
return !matches(cookie, 'name', options.name) || !matches(cookie, 'domain', options.domain) || !matches(cookie, 'path', options.path);
|
||||
});
|
||||
await this.addCookies(cookiesToReadd);
|
||||
}
|
||||
setHTTPCredentials(httpCredentials) {
|
||||
return this.doSetHTTPCredentials(httpCredentials);
|
||||
}
|
||||
async exposeBinding(name, needsHandle, playwrightBinding) {
|
||||
if (this._pageBindings.has(name)) throw new Error(`Function "${name}" has been already registered`);
|
||||
for (const page of this.pages()) {
|
||||
if (page.getBinding(name)) throw new Error(`Function "${name}" has been already registered in one of the pages`);
|
||||
}
|
||||
const binding = new _page6.PageBinding(name, playwrightBinding, needsHandle);
|
||||
this._pageBindings.set(name, binding);
|
||||
await this.doAddInitScript(binding.initScript);
|
||||
const frames = this.pages().map(page => page.frames()).flat();
|
||||
await Promise.all(frames.map(frame => frame.evaluateExpression(binding.initScript.source).catch(e => {})));
|
||||
}
|
||||
async _removeExposedBindings() {
|
||||
for (const [key, binding] of this._pageBindings) {
|
||||
if (!binding.internal) this._pageBindings.delete(key);
|
||||
}
|
||||
}
|
||||
async grantPermissions(permissions, origin) {
|
||||
let resolvedOrigin = '*';
|
||||
if (origin) {
|
||||
const url = new URL(origin);
|
||||
resolvedOrigin = url.origin;
|
||||
}
|
||||
const existing = new Set(this._permissions.get(resolvedOrigin) || []);
|
||||
permissions.forEach(p => existing.add(p));
|
||||
const list = [...existing.values()];
|
||||
this._permissions.set(resolvedOrigin, list);
|
||||
await this.doGrantPermissions(resolvedOrigin, list);
|
||||
}
|
||||
async clearPermissions() {
|
||||
this._permissions.clear();
|
||||
await this.doClearPermissions();
|
||||
}
|
||||
setDefaultNavigationTimeout(timeout) {
|
||||
this._timeoutSettings.setDefaultNavigationTimeout(timeout);
|
||||
}
|
||||
setDefaultTimeout(timeout) {
|
||||
this._timeoutSettings.setDefaultTimeout(timeout);
|
||||
}
|
||||
async _loadDefaultContextAsIs(progress) {
|
||||
if (!this.pages().length) {
|
||||
const waitForEvent = _helper.helper.waitForEvent(progress, this, BrowserContext.Events.Page);
|
||||
progress.cleanupWhenAborted(() => waitForEvent.dispose);
|
||||
const page = await waitForEvent.promise;
|
||||
if (page._pageIsError) throw page._pageIsError;
|
||||
}
|
||||
const pages = this.pages();
|
||||
if (pages[0]._pageIsError) throw pages[0]._pageIsError;
|
||||
await pages[0].mainFrame()._waitForLoadState(progress, 'load');
|
||||
return pages;
|
||||
}
|
||||
async _loadDefaultContext(progress) {
|
||||
const pages = await this._loadDefaultContextAsIs(progress);
|
||||
const browserName = this._browser.options.name;
|
||||
if (this._options.isMobile && browserName === 'chromium' || this._options.locale && browserName === 'webkit') {
|
||||
// Workaround for:
|
||||
// - chromium fails to change isMobile for existing page;
|
||||
// - webkit fails to change locale for existing page.
|
||||
const oldPage = pages[0];
|
||||
await this.newPage(progress.metadata);
|
||||
await oldPage.close(progress.metadata);
|
||||
}
|
||||
}
|
||||
_authenticateProxyViaHeader() {
|
||||
const proxy = this._options.proxy || this._browser.options.proxy || {
|
||||
username: undefined,
|
||||
password: undefined
|
||||
};
|
||||
const {
|
||||
username,
|
||||
password
|
||||
} = proxy;
|
||||
if (username) {
|
||||
this._options.httpCredentials = {
|
||||
username,
|
||||
password: password
|
||||
};
|
||||
const token = Buffer.from(`${username}:${password}`).toString('base64');
|
||||
this._options.extraHTTPHeaders = network.mergeHeaders([this._options.extraHTTPHeaders, network.singleHeader('Proxy-Authorization', `Basic ${token}`)]);
|
||||
}
|
||||
}
|
||||
_authenticateProxyViaCredentials() {
|
||||
const proxy = this._options.proxy || this._browser.options.proxy;
|
||||
if (!proxy) return;
|
||||
const {
|
||||
username,
|
||||
password
|
||||
} = proxy;
|
||||
if (username) this._options.httpCredentials = {
|
||||
username,
|
||||
password: password || ''
|
||||
};
|
||||
}
|
||||
async addInitScript(source) {
|
||||
const initScript = new _page6.InitScript(source);
|
||||
this.initScripts.push(initScript);
|
||||
await this.doAddInitScript(initScript);
|
||||
}
|
||||
async _removeInitScripts() {
|
||||
this.initScripts = this.initScripts.filter(script => script.internal);
|
||||
await this.doRemoveNonInternalInitScripts();
|
||||
}
|
||||
async setRequestInterceptor(handler) {
|
||||
this._requestInterceptor = handler;
|
||||
await this.doUpdateRequestInterception();
|
||||
}
|
||||
isClosingOrClosed() {
|
||||
return this._closedStatus !== 'open';
|
||||
}
|
||||
async _deleteAllDownloads() {
|
||||
await Promise.all(Array.from(this._downloads).map(download => download.artifact.deleteOnContextClose()));
|
||||
}
|
||||
async _deleteAllTempDirs() {
|
||||
await Promise.all(this._tempDirs.map(async dir => await _fs.default.promises.unlink(dir).catch(e => {})));
|
||||
}
|
||||
setCustomCloseHandler(handler) {
|
||||
this._customCloseHandler = handler;
|
||||
}
|
||||
async close(options) {
|
||||
if (this._closedStatus === 'open') {
|
||||
if (options.reason) this._closeReason = options.reason;
|
||||
this.emit(BrowserContext.Events.BeforeClose);
|
||||
this._closedStatus = 'closing';
|
||||
for (const harRecorder of this._harRecorders.values()) await harRecorder.flush();
|
||||
await this.tracing.flush();
|
||||
|
||||
// Cleanup.
|
||||
const promises = [];
|
||||
for (const {
|
||||
context,
|
||||
artifact
|
||||
} of this._browser._idToVideo.values()) {
|
||||
// Wait for the videos to finish.
|
||||
if (context === this) promises.push(artifact.finishedPromise());
|
||||
}
|
||||
if (this._customCloseHandler) {
|
||||
await this._customCloseHandler();
|
||||
} else {
|
||||
// Close the context.
|
||||
await this.doClose(options.reason);
|
||||
}
|
||||
|
||||
// We delete downloads after context closure
|
||||
// so that browser does not write to the download file anymore.
|
||||
promises.push(this._deleteAllDownloads());
|
||||
promises.push(this._deleteAllTempDirs());
|
||||
await Promise.all(promises);
|
||||
|
||||
// Custom handler should trigger didCloseInternal itself.
|
||||
if (!this._customCloseHandler) this._didCloseInternal();
|
||||
}
|
||||
await this._closePromise;
|
||||
}
|
||||
async newPage(metadata) {
|
||||
const pageDelegate = await this.newPageDelegate();
|
||||
if (metadata.isServerSide) pageDelegate.potentiallyUninitializedPage().markAsServerSideOnly();
|
||||
const pageOrError = await pageDelegate.pageOrError();
|
||||
if (pageOrError instanceof _page6.Page) {
|
||||
if (pageOrError.isClosed()) throw new Error('Page has been closed.');
|
||||
return pageOrError;
|
||||
}
|
||||
throw pageOrError;
|
||||
}
|
||||
addVisitedOrigin(origin) {
|
||||
this._origins.add(origin);
|
||||
}
|
||||
async storageState() {
|
||||
const result = {
|
||||
cookies: await this.cookies(),
|
||||
origins: []
|
||||
};
|
||||
const originsToSave = new Set(this._origins);
|
||||
|
||||
// First try collecting storage stage from existing pages.
|
||||
for (const page of this.pages()) {
|
||||
const origin = page.mainFrame().origin();
|
||||
if (!origin || !originsToSave.has(origin)) continue;
|
||||
try {
|
||||
const storage = await page.mainFrame().nonStallingEvaluateInExistingContext(`({
|
||||
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
|
||||
})`, 'utility');
|
||||
if (storage.localStorage.length) result.origins.push({
|
||||
origin,
|
||||
localStorage: storage.localStorage
|
||||
});
|
||||
originsToSave.delete(origin);
|
||||
} catch {
|
||||
// When failed on the live page, we'll retry on the blank page below.
|
||||
}
|
||||
}
|
||||
|
||||
// If there are still origins to save, create a blank page to iterate over origins.
|
||||
if (originsToSave.size) {
|
||||
const internalMetadata = (0, _instrumentation.serverSideCallMetadata)();
|
||||
const page = await this.newPage(internalMetadata);
|
||||
await page._setServerRequestInterceptor(handler => {
|
||||
handler.fulfill({
|
||||
body: '<html></html>'
|
||||
}).catch(() => {});
|
||||
return true;
|
||||
});
|
||||
for (const origin of originsToSave) {
|
||||
const originStorage = {
|
||||
origin,
|
||||
localStorage: []
|
||||
};
|
||||
const frame = page.mainFrame();
|
||||
await frame.goto(internalMetadata, origin);
|
||||
const storage = await frame.evaluateExpression(`({
|
||||
localStorage: Object.keys(localStorage).map(name => ({ name, value: localStorage.getItem(name) })),
|
||||
})`, {
|
||||
world: 'utility'
|
||||
});
|
||||
originStorage.localStorage = storage.localStorage;
|
||||
if (storage.localStorage.length) result.origins.push(originStorage);
|
||||
}
|
||||
await page.close(internalMetadata);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async _resetStorage() {
|
||||
var _this$_options$storag;
|
||||
const oldOrigins = this._origins;
|
||||
const newOrigins = new Map(((_this$_options$storag = this._options.storageState) === null || _this$_options$storag === void 0 || (_this$_options$storag = _this$_options$storag.origins) === null || _this$_options$storag === void 0 ? void 0 : _this$_options$storag.map(p => [p.origin, p])) || []);
|
||||
if (!oldOrigins.size && !newOrigins.size) return;
|
||||
let page = this.pages()[0];
|
||||
const internalMetadata = (0, _instrumentation.serverSideCallMetadata)();
|
||||
page = page || (await this.newPage({
|
||||
...internalMetadata,
|
||||
// Do not mark this page as internal, because we will leave it for later reuse
|
||||
// as a user-visible page.
|
||||
isServerSide: false
|
||||
}));
|
||||
await page._setServerRequestInterceptor(handler => {
|
||||
handler.fulfill({
|
||||
body: '<html></html>'
|
||||
}).catch(() => {});
|
||||
return true;
|
||||
});
|
||||
for (const origin of new Set([...oldOrigins, ...newOrigins.keys()])) {
|
||||
const frame = page.mainFrame();
|
||||
await frame.goto(internalMetadata, origin);
|
||||
await frame.resetStorageForCurrentOriginBestEffort(newOrigins.get(origin));
|
||||
}
|
||||
await page._setServerRequestInterceptor(undefined);
|
||||
this._origins = new Set([...newOrigins.keys()]);
|
||||
// It is safe to not restore the URL to about:blank since we are doing it in Page::resetForReuse.
|
||||
}
|
||||
async _resetCookies() {
|
||||
var _this$_options$storag2, _this$_options$storag3;
|
||||
await this.doClearCookies();
|
||||
if ((_this$_options$storag2 = this._options.storageState) !== null && _this$_options$storag2 !== void 0 && _this$_options$storag2.cookies) await this.addCookies((_this$_options$storag3 = this._options.storageState) === null || _this$_options$storag3 === void 0 ? void 0 : _this$_options$storag3.cookies);
|
||||
}
|
||||
isSettingStorageState() {
|
||||
return this._settingStorageState;
|
||||
}
|
||||
async setStorageState(metadata, state) {
|
||||
this._settingStorageState = true;
|
||||
try {
|
||||
if (state.cookies) await this.addCookies(state.cookies);
|
||||
if (state.origins && state.origins.length) {
|
||||
const internalMetadata = (0, _instrumentation.serverSideCallMetadata)();
|
||||
const page = await this.newPage(internalMetadata);
|
||||
await page._setServerRequestInterceptor(handler => {
|
||||
handler.fulfill({
|
||||
body: '<html></html>'
|
||||
}).catch(() => {});
|
||||
return true;
|
||||
});
|
||||
for (const originState of state.origins) {
|
||||
const frame = page.mainFrame();
|
||||
await frame.goto(metadata, originState.origin);
|
||||
await frame.evaluateExpression(`
|
||||
originState => {
|
||||
for (const { name, value } of (originState.localStorage || []))
|
||||
localStorage.setItem(name, value);
|
||||
}`, {
|
||||
isFunction: true,
|
||||
world: 'utility'
|
||||
}, originState);
|
||||
}
|
||||
await page.close(internalMetadata);
|
||||
}
|
||||
} finally {
|
||||
this._settingStorageState = false;
|
||||
}
|
||||
}
|
||||
async extendInjectedScript(source, arg) {
|
||||
const installInFrame = frame => frame.extendInjectedScript(source, arg).catch(() => {});
|
||||
const installInPage = page => {
|
||||
page.on(_page6.Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
|
||||
return Promise.all(page.frames().map(installInFrame));
|
||||
};
|
||||
this.on(BrowserContext.Events.Page, installInPage);
|
||||
return Promise.all(this.pages().map(installInPage));
|
||||
}
|
||||
async safeNonStallingEvaluateInAllFrames(expression, world, options = {}) {
|
||||
await Promise.all(this.pages().map(page => page.safeNonStallingEvaluateInAllFrames(expression, world, options)));
|
||||
}
|
||||
async _harStart(page, options) {
|
||||
const harId = (0, _utils.createGuid)();
|
||||
this._harRecorders.set(harId, new _harRecorder.HarRecorder(this, page, options));
|
||||
return harId;
|
||||
}
|
||||
async _harExport(harId) {
|
||||
const recorder = this._harRecorders.get(harId || '');
|
||||
return recorder.export();
|
||||
}
|
||||
addRouteInFlight(route) {
|
||||
this._routesInFlight.add(route);
|
||||
}
|
||||
removeRouteInFlight(route) {
|
||||
this._routesInFlight.delete(route);
|
||||
}
|
||||
async _cancelAllRoutesInFlight() {
|
||||
await Promise.all([...this._routesInFlight].map(r => r.abort())).catch(() => {});
|
||||
this._routesInFlight.clear();
|
||||
}
|
||||
}
|
||||
exports.BrowserContext = BrowserContext;
|
||||
BrowserContext.Events = {
|
||||
Console: 'console',
|
||||
Close: 'close',
|
||||
Dialog: 'dialog',
|
||||
Page: 'page',
|
||||
// Can't use just 'error' due to node.js special treatment of error events.
|
||||
// @see https://nodejs.org/api/events.html#events_error_events
|
||||
PageError: 'pageerror',
|
||||
Request: 'request',
|
||||
Response: 'response',
|
||||
RequestFailed: 'requestfailed',
|
||||
RequestFinished: 'requestfinished',
|
||||
RequestAborted: 'requestaborted',
|
||||
RequestFulfilled: 'requestfulfilled',
|
||||
RequestContinued: 'requestcontinued',
|
||||
BeforeClose: 'beforeclose',
|
||||
VideoStarted: 'videostarted'
|
||||
};
|
||||
function assertBrowserContextIsNotOwned(context) {
|
||||
for (const page of context.pages()) {
|
||||
if (page._ownedContext) throw new Error('Please use browser.newContext() for multi-page scripts that share the context.');
|
||||
}
|
||||
}
|
||||
function validateBrowserContextOptions(options, browserOptions) {
|
||||
if (options.noDefaultViewport && options.deviceScaleFactor !== undefined) throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
|
||||
if (options.noDefaultViewport && !!options.isMobile) throw new Error(`"isMobile" option is not supported with null "viewport"`);
|
||||
if (options.acceptDownloads === undefined && browserOptions.name !== 'electron') options.acceptDownloads = 'accept';
|
||||
// Electron requires explicit acceptDownloads: true since we wait for
|
||||
// https://github.com/electron/electron/pull/41718 to be widely shipped.
|
||||
// In 6-12 months, we can remove this check.
|
||||
else if (options.acceptDownloads === undefined && browserOptions.name === 'electron') options.acceptDownloads = 'internal-browser-default';
|
||||
if (!options.viewport && !options.noDefaultViewport) options.viewport = {
|
||||
width: 1280,
|
||||
height: 720
|
||||
};
|
||||
if (options.recordVideo) {
|
||||
if (!options.recordVideo.size) {
|
||||
if (options.noDefaultViewport) {
|
||||
options.recordVideo.size = {
|
||||
width: 800,
|
||||
height: 600
|
||||
};
|
||||
} else {
|
||||
const size = options.viewport;
|
||||
const scale = Math.min(1, 800 / Math.max(size.width, size.height));
|
||||
options.recordVideo.size = {
|
||||
width: Math.floor(size.width * scale),
|
||||
height: Math.floor(size.height * scale)
|
||||
};
|
||||
}
|
||||
}
|
||||
// Make sure both dimensions are odd, this is required for vp8
|
||||
options.recordVideo.size.width &= ~1;
|
||||
options.recordVideo.size.height &= ~1;
|
||||
}
|
||||
if (options.proxy) options.proxy = normalizeProxySettings(options.proxy);
|
||||
verifyGeolocation(options.geolocation);
|
||||
}
|
||||
function verifyGeolocation(geolocation) {
|
||||
if (!geolocation) return;
|
||||
geolocation.accuracy = geolocation.accuracy || 0;
|
||||
const {
|
||||
longitude,
|
||||
latitude,
|
||||
accuracy
|
||||
} = geolocation;
|
||||
if (longitude < -180 || longitude > 180) throw new Error(`geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||
if (latitude < -90 || latitude > 90) throw new Error(`geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed.`);
|
||||
if (accuracy < 0) throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`);
|
||||
}
|
||||
function verifyClientCertificates(clientCertificates) {
|
||||
if (!clientCertificates) return;
|
||||
for (const cert of clientCertificates) {
|
||||
if (!cert.origin) throw new Error(`clientCertificates.origin is required`);
|
||||
if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx) throw new Error('None of cert, key, passphrase or pfx is specified');
|
||||
if (cert.cert && !cert.key) throw new Error('cert is specified without key');
|
||||
if (!cert.cert && cert.key) throw new Error('key is specified without cert');
|
||||
if (cert.pfx && (cert.cert || cert.key)) throw new Error('pfx is specified together with cert, key or passphrase');
|
||||
}
|
||||
}
|
||||
function normalizeProxySettings(proxy) {
|
||||
let {
|
||||
server,
|
||||
bypass
|
||||
} = proxy;
|
||||
let url;
|
||||
try {
|
||||
// new URL('127.0.0.1:8080') throws
|
||||
// new URL('localhost:8080') fails to parse host or protocol
|
||||
// In both of these cases, we need to try re-parse URL with `http://` prefix.
|
||||
url = new URL(server);
|
||||
if (!url.host || !url.protocol) url = new URL('http://' + server);
|
||||
} catch (e) {
|
||||
url = new URL('http://' + server);
|
||||
}
|
||||
if (url.protocol === 'socks4:' && (proxy.username || proxy.password)) throw new Error(`Socks4 proxy protocol does not support authentication`);
|
||||
if (url.protocol === 'socks5:' && (proxy.username || proxy.password)) throw new Error(`Browser does not support socks5 proxy authentication`);
|
||||
server = url.protocol + '//' + url.host;
|
||||
if (bypass) bypass = bypass.split(',').map(t => t.trim()).join(',');
|
||||
return {
|
||||
...proxy,
|
||||
server,
|
||||
bypass
|
||||
};
|
||||
}
|
||||
const paramsThatAllowContextReuse = ['colorScheme', 'forcedColors', 'reducedMotion', 'screen', 'userAgent', 'viewport'];
|
||||
const defaultNewContextParamValues = {
|
||||
noDefaultViewport: false,
|
||||
ignoreHTTPSErrors: false,
|
||||
javaScriptEnabled: true,
|
||||
bypassCSP: false,
|
||||
offline: false,
|
||||
isMobile: false,
|
||||
hasTouch: false,
|
||||
acceptDownloads: 'accept',
|
||||
strictSelectors: false,
|
||||
serviceWorkers: 'allow',
|
||||
locale: 'en-US'
|
||||
};
|
||||
338
node_modules/playwright-core/lib/server/browserType.js
generated
vendored
Normal file
338
node_modules/playwright-core/lib/server/browserType.js
generated
vendored
Normal file
@@ -0,0 +1,338 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.kNoXServerRunningError = exports.BrowserType = exports.BrowserReadyState = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var os = _interopRequireWildcard(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _browserContext = require("./browserContext");
|
||||
var _registry = require("./registry");
|
||||
var _transport = require("./transport");
|
||||
var _processLauncher = require("../utils/processLauncher");
|
||||
var _pipeTransport = require("./pipeTransport");
|
||||
var _progress = require("./progress");
|
||||
var _timeoutSettings = require("../common/timeoutSettings");
|
||||
var _utils = require("../utils");
|
||||
var _fileUtils = require("../utils/fileUtils");
|
||||
var _helper = require("./helper");
|
||||
var _debugLogger = require("../utils/debugLogger");
|
||||
var _instrumentation = require("./instrumentation");
|
||||
var _protocolError = require("./protocolError");
|
||||
var _socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kNoXServerRunningError = exports.kNoXServerRunningError = 'Looks like you launched a headed browser without having a XServer running.\n' + 'Set either \'headless: true\' or use \'xvfb-run <your-playwright-app>\' before running Playwright.\n\n<3 Playwright Team';
|
||||
class BrowserReadyState {
|
||||
constructor() {
|
||||
this._wsEndpoint = new _utils.ManualPromise();
|
||||
}
|
||||
onBrowserExit() {
|
||||
// Unblock launch when browser prematurely exits.
|
||||
this._wsEndpoint.resolve(undefined);
|
||||
}
|
||||
async waitUntilReady() {
|
||||
const wsEndpoint = await this._wsEndpoint;
|
||||
return {
|
||||
wsEndpoint
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.BrowserReadyState = BrowserReadyState;
|
||||
class BrowserType extends _instrumentation.SdkObject {
|
||||
constructor(parent, browserName) {
|
||||
super(parent, 'browser-type');
|
||||
this._name = void 0;
|
||||
this._useBidi = false;
|
||||
this.attribution.browserType = this;
|
||||
this._name = browserName;
|
||||
}
|
||||
executablePath() {
|
||||
return _registry.registry.findExecutable(this._name).executablePath(this.attribution.playwright.options.sdkLanguage) || '';
|
||||
}
|
||||
name() {
|
||||
return this._name;
|
||||
}
|
||||
async launch(metadata, options, protocolLogger) {
|
||||
options = this._validateLaunchOptions(options);
|
||||
if (this._useBidi) options.useWebSocket = true;
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
controller.setLogName('browser');
|
||||
const browser = await controller.run(progress => {
|
||||
const seleniumHubUrl = options.__testHookSeleniumRemoteURL || process.env.SELENIUM_REMOTE_URL;
|
||||
if (seleniumHubUrl) return this._launchWithSeleniumHub(progress, seleniumHubUrl, options);
|
||||
return this._innerLaunchWithRetries(progress, options, undefined, _helper.helper.debugProtocolLogger(protocolLogger)).catch(e => {
|
||||
throw this._rewriteStartupLog(e);
|
||||
});
|
||||
}, _timeoutSettings.TimeoutSettings.launchTimeout(options));
|
||||
return browser;
|
||||
}
|
||||
async launchPersistentContext(metadata, userDataDir, options) {
|
||||
const launchOptions = this._validateLaunchOptions(options);
|
||||
if (this._useBidi) launchOptions.useWebSocket = true;
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
controller.setLogName('browser');
|
||||
const browser = await controller.run(async progress => {
|
||||
var _options$clientCertif;
|
||||
// Note: Any initial TLS requests will fail since we rely on the Page/Frames initialize which sets ignoreHTTPSErrors.
|
||||
let clientCertificatesProxy;
|
||||
if ((_options$clientCertif = options.clientCertificates) !== null && _options$clientCertif !== void 0 && _options$clientCertif.length) {
|
||||
var _clientCertificatesPr;
|
||||
clientCertificatesProxy = new _socksClientCertificatesInterceptor.ClientCertificatesProxy(options);
|
||||
launchOptions.proxyOverride = await ((_clientCertificatesPr = clientCertificatesProxy) === null || _clientCertificatesPr === void 0 ? void 0 : _clientCertificatesPr.listen());
|
||||
options = {
|
||||
...options
|
||||
};
|
||||
options.internalIgnoreHTTPSErrors = true;
|
||||
}
|
||||
progress.cleanupWhenAborted(() => {
|
||||
var _clientCertificatesPr2;
|
||||
return (_clientCertificatesPr2 = clientCertificatesProxy) === null || _clientCertificatesPr2 === void 0 ? void 0 : _clientCertificatesPr2.close();
|
||||
});
|
||||
const browser = await this._innerLaunchWithRetries(progress, launchOptions, options, _helper.helper.debugProtocolLogger(), userDataDir).catch(e => {
|
||||
throw this._rewriteStartupLog(e);
|
||||
});
|
||||
browser._defaultContext._clientCertificatesProxy = clientCertificatesProxy;
|
||||
return browser;
|
||||
}, _timeoutSettings.TimeoutSettings.launchTimeout(launchOptions));
|
||||
return browser._defaultContext;
|
||||
}
|
||||
async _innerLaunchWithRetries(progress, options, persistent, protocolLogger, userDataDir) {
|
||||
try {
|
||||
return await this._innerLaunch(progress, options, persistent, protocolLogger, userDataDir);
|
||||
} catch (error) {
|
||||
// @see https://github.com/microsoft/playwright/issues/5214
|
||||
const errorMessage = typeof error === 'object' && typeof error.message === 'string' ? error.message : '';
|
||||
if (errorMessage.includes('Inconsistency detected by ld.so')) {
|
||||
progress.log(`<restarting browser due to hitting race condition in glibc>`);
|
||||
return this._innerLaunch(progress, options, persistent, protocolLogger, userDataDir);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async _innerLaunch(progress, options, persistent, protocolLogger, maybeUserDataDir) {
|
||||
options.proxy = options.proxy ? (0, _browserContext.normalizeProxySettings)(options.proxy) : undefined;
|
||||
const browserLogsCollector = new _debugLogger.RecentLogsCollector();
|
||||
const {
|
||||
browserProcess,
|
||||
userDataDir,
|
||||
artifactsDir,
|
||||
transport
|
||||
} = await this._launchProcess(progress, options, !!persistent, browserLogsCollector, maybeUserDataDir);
|
||||
if (options.__testHookBeforeCreateBrowser) await options.__testHookBeforeCreateBrowser();
|
||||
const browserOptions = {
|
||||
name: this._name,
|
||||
isChromium: this._name === 'chromium',
|
||||
channel: options.channel,
|
||||
slowMo: options.slowMo,
|
||||
persistent,
|
||||
headful: !options.headless,
|
||||
artifactsDir,
|
||||
downloadsPath: options.downloadsPath || artifactsDir,
|
||||
tracesDir: options.tracesDir || artifactsDir,
|
||||
browserProcess,
|
||||
customExecutablePath: options.executablePath,
|
||||
proxy: options.proxy,
|
||||
protocolLogger,
|
||||
browserLogsCollector,
|
||||
wsEndpoint: options.useWebSocket ? transport.wsEndpoint : undefined,
|
||||
originalLaunchOptions: options
|
||||
};
|
||||
if (persistent) (0, _browserContext.validateBrowserContextOptions)(persistent, browserOptions);
|
||||
copyTestHooks(options, browserOptions);
|
||||
const browser = await this.connectToTransport(transport, browserOptions);
|
||||
browser._userDataDirForTest = userDataDir;
|
||||
// We assume no control when using custom arguments, and do not prepare the default context in that case.
|
||||
if (persistent && !options.ignoreAllDefaultArgs) await browser._defaultContext._loadDefaultContext(progress);
|
||||
return browser;
|
||||
}
|
||||
async _launchProcess(progress, options, isPersistent, browserLogsCollector, userDataDir) {
|
||||
var _await$readyState$wai;
|
||||
const {
|
||||
ignoreDefaultArgs,
|
||||
ignoreAllDefaultArgs,
|
||||
args = [],
|
||||
executablePath = null,
|
||||
handleSIGINT = true,
|
||||
handleSIGTERM = true,
|
||||
handleSIGHUP = true
|
||||
} = options;
|
||||
const env = options.env ? (0, _processLauncher.envArrayToObject)(options.env) : process.env;
|
||||
await this._createArtifactDirs(options);
|
||||
const tempDirectories = [];
|
||||
const artifactsDir = await _fs.default.promises.mkdtemp(_path.default.join(os.tmpdir(), 'playwright-artifacts-'));
|
||||
tempDirectories.push(artifactsDir);
|
||||
if (userDataDir) {
|
||||
// Firefox bails if the profile directory does not exist, Chrome creates it. We ensure consistent behavior here.
|
||||
if (!(await (0, _fileUtils.existsAsync)(userDataDir))) await _fs.default.promises.mkdir(userDataDir, {
|
||||
recursive: true,
|
||||
mode: 0o700
|
||||
});
|
||||
} else {
|
||||
userDataDir = await _fs.default.promises.mkdtemp(_path.default.join(os.tmpdir(), `playwright_${this._name}dev_profile-`));
|
||||
tempDirectories.push(userDataDir);
|
||||
}
|
||||
await this.prepareUserDataDir(options, userDataDir);
|
||||
const browserArguments = [];
|
||||
if (ignoreAllDefaultArgs) browserArguments.push(...args);else if (ignoreDefaultArgs) browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1));else browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir));
|
||||
let executable;
|
||||
if (executablePath) {
|
||||
if (!(await (0, _fileUtils.existsAsync)(executablePath))) throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`);
|
||||
executable = executablePath;
|
||||
} else {
|
||||
const registryExecutable = _registry.registry.findExecutable(this.getExecutableName(options));
|
||||
if (!registryExecutable || registryExecutable.browserName !== this._name) throw new Error(`Unsupported ${this._name} channel "${options.channel}"`);
|
||||
executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage);
|
||||
await _registry.registry.validateHostRequirementsForExecutablesIfNeeded([registryExecutable], this.attribution.playwright.options.sdkLanguage);
|
||||
}
|
||||
const readyState = this.readyState(options);
|
||||
// Note: it is important to define these variables before launchProcess, so that we don't get
|
||||
// "Cannot access 'browserServer' before initialization" if something went wrong.
|
||||
let transport = undefined;
|
||||
let browserProcess = undefined;
|
||||
const {
|
||||
launchedProcess,
|
||||
gracefullyClose,
|
||||
kill
|
||||
} = await (0, _processLauncher.launchProcess)({
|
||||
command: executable,
|
||||
args: browserArguments,
|
||||
env: this.amendEnvironment(env, userDataDir, executable, browserArguments),
|
||||
handleSIGINT,
|
||||
handleSIGTERM,
|
||||
handleSIGHUP,
|
||||
log: message => {
|
||||
readyState === null || readyState === void 0 || readyState.onBrowserOutput(message);
|
||||
progress.log(message);
|
||||
browserLogsCollector.log(message);
|
||||
},
|
||||
stdio: 'pipe',
|
||||
tempDirectories,
|
||||
attemptToGracefullyClose: async () => {
|
||||
if (options.__testHookGracefullyClose) await options.__testHookGracefullyClose();
|
||||
// We try to gracefully close to prevent crash reporting and core dumps.
|
||||
// Note that it's fine to reuse the pipe transport, since
|
||||
// our connection ignores kBrowserCloseMessageId.
|
||||
this.attemptToGracefullyCloseBrowser(transport);
|
||||
},
|
||||
onExit: (exitCode, signal) => {
|
||||
// Unblock launch when browser prematurely exits.
|
||||
readyState === null || readyState === void 0 || readyState.onBrowserExit();
|
||||
if (browserProcess && browserProcess.onclose) browserProcess.onclose(exitCode, signal);
|
||||
}
|
||||
});
|
||||
async function closeOrKill(timeout) {
|
||||
let timer;
|
||||
try {
|
||||
await Promise.race([gracefullyClose(), new Promise((resolve, reject) => timer = setTimeout(reject, timeout))]);
|
||||
} catch (ignored) {
|
||||
await kill().catch(ignored => {}); // Make sure to await actual process exit.
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
browserProcess = {
|
||||
onclose: undefined,
|
||||
process: launchedProcess,
|
||||
close: () => closeOrKill(options.__testHookBrowserCloseTimeout || _timeoutSettings.DEFAULT_TIMEOUT),
|
||||
kill
|
||||
};
|
||||
progress.cleanupWhenAborted(() => closeOrKill(progress.timeUntilDeadline()));
|
||||
const wsEndpoint = (_await$readyState$wai = await (readyState === null || readyState === void 0 ? void 0 : readyState.waitUntilReady())) === null || _await$readyState$wai === void 0 ? void 0 : _await$readyState$wai.wsEndpoint;
|
||||
if (options.useWebSocket) {
|
||||
transport = await _transport.WebSocketTransport.connect(progress, wsEndpoint);
|
||||
} else {
|
||||
const stdio = launchedProcess.stdio;
|
||||
transport = new _pipeTransport.PipeTransport(stdio[3], stdio[4]);
|
||||
}
|
||||
return {
|
||||
browserProcess,
|
||||
artifactsDir,
|
||||
userDataDir,
|
||||
transport
|
||||
};
|
||||
}
|
||||
async _createArtifactDirs(options) {
|
||||
if (options.downloadsPath) await _fs.default.promises.mkdir(options.downloadsPath, {
|
||||
recursive: true
|
||||
});
|
||||
if (options.tracesDir) await _fs.default.promises.mkdir(options.tracesDir, {
|
||||
recursive: true
|
||||
});
|
||||
}
|
||||
async connectOverCDP(metadata, endpointURL, options, timeout) {
|
||||
throw new Error('CDP connections are only supported by Chromium');
|
||||
}
|
||||
async _launchWithSeleniumHub(progress, hubUrl, options) {
|
||||
throw new Error('Connecting to SELENIUM_REMOTE_URL is only supported by Chromium');
|
||||
}
|
||||
_validateLaunchOptions(options) {
|
||||
const {
|
||||
devtools = false
|
||||
} = options;
|
||||
let {
|
||||
headless = !devtools,
|
||||
downloadsPath,
|
||||
proxy
|
||||
} = options;
|
||||
if ((0, _utils.debugMode)()) headless = false;
|
||||
if (downloadsPath && !_path.default.isAbsolute(downloadsPath)) downloadsPath = _path.default.join(process.cwd(), downloadsPath);
|
||||
if (this.attribution.playwright.options.socksProxyPort) proxy = {
|
||||
server: `socks5://127.0.0.1:${this.attribution.playwright.options.socksProxyPort}`
|
||||
};
|
||||
return {
|
||||
...options,
|
||||
devtools,
|
||||
headless,
|
||||
downloadsPath,
|
||||
proxy
|
||||
};
|
||||
}
|
||||
_createUserDataDirArgMisuseError(userDataDirArg) {
|
||||
switch (this.attribution.playwright.options.sdkLanguage) {
|
||||
case 'java':
|
||||
return new Error(`Pass userDataDir parameter to 'BrowserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`);
|
||||
case 'python':
|
||||
return new Error(`Pass user_data_dir parameter to 'browser_type.launch_persistent_context(user_data_dir, **kwargs)' instead of specifying '${userDataDirArg}' argument`);
|
||||
case 'csharp':
|
||||
return new Error(`Pass userDataDir parameter to 'BrowserType.LaunchPersistentContextAsync(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`);
|
||||
default:
|
||||
return new Error(`Pass userDataDir parameter to 'browserType.launchPersistentContext(userDataDir, options)' instead of specifying '${userDataDirArg}' argument`);
|
||||
}
|
||||
}
|
||||
_rewriteStartupLog(error) {
|
||||
if (!(0, _protocolError.isProtocolError)(error)) return error;
|
||||
return this.doRewriteStartupLog(error);
|
||||
}
|
||||
readyState(options) {
|
||||
return undefined;
|
||||
}
|
||||
async prepareUserDataDir(options, userDataDir) {}
|
||||
getExecutableName(options) {
|
||||
return options.channel || this._name;
|
||||
}
|
||||
}
|
||||
exports.BrowserType = BrowserType;
|
||||
function copyTestHooks(from, to) {
|
||||
for (const [key, value] of Object.entries(from)) {
|
||||
if (key.startsWith('__testHook')) to[key] = value;
|
||||
}
|
||||
}
|
||||
BIN
node_modules/playwright-core/lib/server/chromium/appIcon.png
generated
vendored
Normal file
BIN
node_modules/playwright-core/lib/server/chromium/appIcon.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
354
node_modules/playwright-core/lib/server/chromium/chromium.js
generated
vendored
Normal file
354
node_modules/playwright-core/lib/server/chromium/chromium.js
generated
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Chromium = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _crBrowser = require("./crBrowser");
|
||||
var _processLauncher = require("../../utils/processLauncher");
|
||||
var _crConnection = require("./crConnection");
|
||||
var _browserType = require("../browserType");
|
||||
var _transport = require("../transport");
|
||||
var _crDevTools = require("./crDevTools");
|
||||
var _browser = require("../browser");
|
||||
var _network = require("../../utils/network");
|
||||
var _userAgent = require("../../utils/userAgent");
|
||||
var _ascii = require("../../utils/ascii");
|
||||
var _utils = require("../../utils");
|
||||
var _fileUtils = require("../../utils/fileUtils");
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _progress = require("../progress");
|
||||
var _timeoutSettings = require("../../common/timeoutSettings");
|
||||
var _helper = require("../helper");
|
||||
var _registry = require("../registry");
|
||||
var _manualPromise = require("../../utils/manualPromise");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _chromiumSwitches = require("./chromiumSwitches");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const ARTIFACTS_FOLDER = _path.default.join(_os.default.tmpdir(), 'playwright-artifacts-');
|
||||
class Chromium extends _browserType.BrowserType {
|
||||
constructor(parent) {
|
||||
super(parent, 'chromium');
|
||||
this._devtools = void 0;
|
||||
if ((0, _utils.debugMode)()) this._devtools = this._createDevTools();
|
||||
}
|
||||
async connectOverCDP(metadata, endpointURL, options, timeout) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
controller.setLogName('browser');
|
||||
return controller.run(async progress => {
|
||||
return await this._connectOverCDPInternal(progress, endpointURL, options);
|
||||
}, _timeoutSettings.TimeoutSettings.timeout({
|
||||
timeout
|
||||
}));
|
||||
}
|
||||
async _connectOverCDPInternal(progress, endpointURL, options, onClose) {
|
||||
let headersMap;
|
||||
if (options.headers) headersMap = (0, _utils.headersArrayToObject)(options.headers, false);
|
||||
if (!headersMap) headersMap = {
|
||||
'User-Agent': (0, _userAgent.getUserAgent)()
|
||||
};else if (headersMap && !Object.keys(headersMap).some(key => key.toLowerCase() === 'user-agent')) headersMap['User-Agent'] = (0, _userAgent.getUserAgent)();
|
||||
const artifactsDir = await _fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
const wsEndpoint = await urlToWSEndpoint(progress, endpointURL, headersMap);
|
||||
progress.throwIfAborted();
|
||||
const chromeTransport = await _transport.WebSocketTransport.connect(progress, wsEndpoint, headersMap);
|
||||
const cleanedUp = new _manualPromise.ManualPromise();
|
||||
const doCleanup = async () => {
|
||||
await (0, _fileUtils.removeFolders)([artifactsDir]);
|
||||
await (onClose === null || onClose === void 0 ? void 0 : onClose());
|
||||
cleanedUp.resolve();
|
||||
};
|
||||
const doClose = async () => {
|
||||
await chromeTransport.closeAndWait();
|
||||
await cleanedUp;
|
||||
};
|
||||
const browserProcess = {
|
||||
close: doClose,
|
||||
kill: doClose
|
||||
};
|
||||
const persistent = {
|
||||
noDefaultViewport: true
|
||||
};
|
||||
const browserOptions = {
|
||||
slowMo: options.slowMo,
|
||||
name: 'chromium',
|
||||
isChromium: true,
|
||||
persistent,
|
||||
browserProcess,
|
||||
protocolLogger: _helper.helper.debugProtocolLogger(),
|
||||
browserLogsCollector: new _debugLogger.RecentLogsCollector(),
|
||||
artifactsDir,
|
||||
downloadsPath: options.downloadsPath || artifactsDir,
|
||||
tracesDir: options.tracesDir || artifactsDir,
|
||||
originalLaunchOptions: {}
|
||||
};
|
||||
(0, _browserContext.validateBrowserContextOptions)(persistent, browserOptions);
|
||||
progress.throwIfAborted();
|
||||
const browser = await _crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
|
||||
browser._isCollocatedWithServer = false;
|
||||
browser.on(_browser.Browser.Events.Disconnected, doCleanup);
|
||||
return browser;
|
||||
}
|
||||
_createDevTools() {
|
||||
// TODO: this is totally wrong when using channels.
|
||||
const directory = _registry.registry.findExecutable('chromium').directory;
|
||||
return directory ? new _crDevTools.CRDevTools(_path.default.join(directory, 'devtools-preferences.json')) : undefined;
|
||||
}
|
||||
async connectToTransport(transport, options) {
|
||||
let devtools = this._devtools;
|
||||
if (options.__testHookForDevTools) {
|
||||
devtools = this._createDevTools();
|
||||
await options.__testHookForDevTools(devtools);
|
||||
}
|
||||
return _crBrowser.CRBrowser.connect(this.attribution.playwright, transport, options, devtools);
|
||||
}
|
||||
doRewriteStartupLog(error) {
|
||||
if (!error.logs) return error;
|
||||
if (error.logs.includes('Missing X server')) error.logs = '\n' + (0, _ascii.wrapInASCIIBox)(_browserType.kNoXServerRunningError, 1);
|
||||
// These error messages are taken from Chromium source code as of July, 2020:
|
||||
// https://github.com/chromium/chromium/blob/70565f67e79f79e17663ad1337dc6e63ee207ce9/content/browser/zygote_host/zygote_host_impl_linux.cc
|
||||
if (!error.logs.includes('crbug.com/357670') && !error.logs.includes('No usable sandbox!') && !error.logs.includes('crbug.com/638180')) return error;
|
||||
error.logs = [`Chromium sandboxing failed!`, `================================`, `To avoid the sandboxing issue, do either of the following:`, ` - (preferred): Configure your environment to support sandboxing`, ` - (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option`, `================================`, ``].join('\n');
|
||||
return error;
|
||||
}
|
||||
amendEnvironment(env, userDataDir, executable, browserArguments) {
|
||||
return env;
|
||||
}
|
||||
attemptToGracefullyCloseBrowser(transport) {
|
||||
const message = {
|
||||
method: 'Browser.close',
|
||||
id: _crConnection.kBrowserCloseMessageId,
|
||||
params: {}
|
||||
};
|
||||
transport.send(message);
|
||||
}
|
||||
async _launchWithSeleniumHub(progress, hubUrl, options) {
|
||||
await this._createArtifactDirs(options);
|
||||
if (!hubUrl.endsWith('/')) hubUrl = hubUrl + '/';
|
||||
const args = this._innerDefaultArgs(options);
|
||||
args.push('--remote-debugging-port=0');
|
||||
const isEdge = options.channel && options.channel.startsWith('msedge');
|
||||
let desiredCapabilities = {
|
||||
'browserName': isEdge ? 'MicrosoftEdge' : 'chrome',
|
||||
[isEdge ? 'ms:edgeOptions' : 'goog:chromeOptions']: {
|
||||
args
|
||||
}
|
||||
};
|
||||
if (process.env.SELENIUM_REMOTE_CAPABILITIES) {
|
||||
const remoteCapabilities = parseSeleniumRemoteParams({
|
||||
name: 'capabilities',
|
||||
value: process.env.SELENIUM_REMOTE_CAPABILITIES
|
||||
}, progress);
|
||||
if (remoteCapabilities) desiredCapabilities = {
|
||||
...desiredCapabilities,
|
||||
...remoteCapabilities
|
||||
};
|
||||
}
|
||||
let headers = {};
|
||||
if (process.env.SELENIUM_REMOTE_HEADERS) {
|
||||
const remoteHeaders = parseSeleniumRemoteParams({
|
||||
name: 'headers',
|
||||
value: process.env.SELENIUM_REMOTE_HEADERS
|
||||
}, progress);
|
||||
if (remoteHeaders) headers = remoteHeaders;
|
||||
}
|
||||
progress.log(`<selenium> connecting to ${hubUrl}`);
|
||||
const response = await (0, _network.fetchData)({
|
||||
url: hubUrl + 'session',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
...headers
|
||||
},
|
||||
data: JSON.stringify({
|
||||
capabilities: {
|
||||
alwaysMatch: desiredCapabilities
|
||||
}
|
||||
}),
|
||||
timeout: progress.timeUntilDeadline()
|
||||
}, seleniumErrorHandler);
|
||||
const value = JSON.parse(response).value;
|
||||
const sessionId = value.sessionId;
|
||||
progress.log(`<selenium> connected to sessionId=${sessionId}`);
|
||||
const disconnectFromSelenium = async () => {
|
||||
progress.log(`<selenium> disconnecting from sessionId=${sessionId}`);
|
||||
await (0, _network.fetchData)({
|
||||
url: hubUrl + 'session/' + sessionId,
|
||||
method: 'DELETE',
|
||||
headers
|
||||
}).catch(error => progress.log(`<error disconnecting from selenium>: ${error}`));
|
||||
progress.log(`<selenium> disconnected from sessionId=${sessionId}`);
|
||||
_processLauncher.gracefullyCloseSet.delete(disconnectFromSelenium);
|
||||
};
|
||||
_processLauncher.gracefullyCloseSet.add(disconnectFromSelenium);
|
||||
try {
|
||||
const capabilities = value.capabilities;
|
||||
let endpointURL;
|
||||
if (capabilities['se:cdp']) {
|
||||
// Selenium 4 - use built-in CDP websocket proxy.
|
||||
progress.log(`<selenium> using selenium v4`);
|
||||
const endpointURLString = addProtocol(capabilities['se:cdp']);
|
||||
endpointURL = new URL(endpointURLString);
|
||||
if (endpointURL.hostname === 'localhost' || endpointURL.hostname === '127.0.0.1') endpointURL.hostname = new URL(hubUrl).hostname;
|
||||
progress.log(`<selenium> retrieved endpoint ${endpointURL.toString()} for sessionId=${sessionId}`);
|
||||
} else {
|
||||
// Selenium 3 - resolve target node IP to use instead of localhost ws url.
|
||||
progress.log(`<selenium> using selenium v3`);
|
||||
const maybeChromeOptions = capabilities['goog:chromeOptions'];
|
||||
const chromeOptions = maybeChromeOptions && typeof maybeChromeOptions === 'object' ? maybeChromeOptions : undefined;
|
||||
const debuggerAddress = chromeOptions && typeof chromeOptions.debuggerAddress === 'string' ? chromeOptions.debuggerAddress : undefined;
|
||||
const chromeOptionsURL = typeof maybeChromeOptions === 'string' ? maybeChromeOptions : undefined;
|
||||
// TODO(dgozman): figure out if we can make ChromeDriver to return 127.0.0.1 instead of localhost.
|
||||
const endpointURLString = addProtocol(debuggerAddress || chromeOptionsURL).replace('localhost', '127.0.0.1');
|
||||
progress.log(`<selenium> retrieved endpoint ${endpointURLString} for sessionId=${sessionId}`);
|
||||
endpointURL = new URL(endpointURLString);
|
||||
if (endpointURL.hostname === 'localhost' || endpointURL.hostname === '127.0.0.1') {
|
||||
const sessionInfoUrl = new URL(hubUrl).origin + '/grid/api/testsession?session=' + sessionId;
|
||||
try {
|
||||
const sessionResponse = await (0, _network.fetchData)({
|
||||
url: sessionInfoUrl,
|
||||
method: 'GET',
|
||||
timeout: progress.timeUntilDeadline(),
|
||||
headers
|
||||
}, seleniumErrorHandler);
|
||||
const proxyId = JSON.parse(sessionResponse).proxyId;
|
||||
endpointURL.hostname = new URL(proxyId).hostname;
|
||||
progress.log(`<selenium> resolved endpoint ip ${endpointURL.toString()} for sessionId=${sessionId}`);
|
||||
} catch (e) {
|
||||
progress.log(`<selenium> unable to resolve endpoint ip for sessionId=${sessionId}, running in standalone?`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return await this._connectOverCDPInternal(progress, endpointURL.toString(), {
|
||||
...options,
|
||||
headers: (0, _utils.headersObjectToArray)(headers)
|
||||
}, disconnectFromSelenium);
|
||||
} catch (e) {
|
||||
await disconnectFromSelenium();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
defaultArgs(options, isPersistent, userDataDir) {
|
||||
const chromeArguments = this._innerDefaultArgs(options);
|
||||
chromeArguments.push(`--user-data-dir=${userDataDir}`);
|
||||
if (options.useWebSocket) chromeArguments.push('--remote-debugging-port=0');else chromeArguments.push('--remote-debugging-pipe');
|
||||
if (isPersistent) chromeArguments.push('about:blank');else chromeArguments.push('--no-startup-window');
|
||||
return chromeArguments;
|
||||
}
|
||||
_innerDefaultArgs(options) {
|
||||
const {
|
||||
args = []
|
||||
} = options;
|
||||
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
|
||||
if (userDataDirArg) throw this._createUserDataDirArgMisuseError('--user-data-dir');
|
||||
if (args.find(arg => arg.startsWith('--remote-debugging-pipe'))) throw new Error('Playwright manages remote debugging connection itself.');
|
||||
if (args.find(arg => !arg.startsWith('-'))) throw new Error('Arguments can not specify page to be opened');
|
||||
const chromeArguments = [..._chromiumSwitches.chromiumSwitches];
|
||||
if (_os.default.platform() === 'darwin') {
|
||||
// See https://github.com/microsoft/playwright/issues/7362
|
||||
chromeArguments.push('--enable-use-zoom-for-dsf=false');
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1407025.
|
||||
if (options.headless && (!options.channel || options.channel === 'chromium-headless-shell')) chromeArguments.push('--use-angle');
|
||||
}
|
||||
if (options.devtools) chromeArguments.push('--auto-open-devtools-for-tabs');
|
||||
if (options.headless) {
|
||||
chromeArguments.push('--headless');
|
||||
chromeArguments.push('--hide-scrollbars', '--mute-audio', '--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4');
|
||||
}
|
||||
if (options.chromiumSandbox !== true) chromeArguments.push('--no-sandbox');
|
||||
const proxy = options.proxyOverride || options.proxy;
|
||||
if (proxy) {
|
||||
const proxyURL = new URL(proxy.server);
|
||||
const isSocks = proxyURL.protocol === 'socks5:';
|
||||
// https://www.chromium.org/developers/design-documents/network-settings
|
||||
if (isSocks && !this.attribution.playwright.options.socksProxyPort) {
|
||||
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
|
||||
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
|
||||
}
|
||||
chromeArguments.push(`--proxy-server=${proxy.server}`);
|
||||
const proxyBypassRules = [];
|
||||
// https://source.chromium.org/chromium/chromium/src/+/master:net/docs/proxy.md;l=548;drc=71698e610121078e0d1a811054dcf9fd89b49578
|
||||
if (this.attribution.playwright.options.socksProxyPort) proxyBypassRules.push('<-loopback>');
|
||||
if (proxy.bypass) proxyBypassRules.push(...proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t));
|
||||
if (!process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK && !proxyBypassRules.includes('<-loopback>')) proxyBypassRules.push('<-loopback>');
|
||||
if (proxyBypassRules.length > 0) chromeArguments.push(`--proxy-bypass-list=${proxyBypassRules.join(';')}`);
|
||||
}
|
||||
chromeArguments.push(...args);
|
||||
return chromeArguments;
|
||||
}
|
||||
readyState(options) {
|
||||
var _options$args;
|
||||
if (options.useWebSocket || (_options$args = options.args) !== null && _options$args !== void 0 && _options$args.some(a => a.startsWith('--remote-debugging-port'))) return new ChromiumReadyState();
|
||||
return undefined;
|
||||
}
|
||||
getExecutableName(options) {
|
||||
if (options.channel) return options.channel;
|
||||
return options.headless ? 'chromium-headless-shell' : 'chromium';
|
||||
}
|
||||
}
|
||||
exports.Chromium = Chromium;
|
||||
class ChromiumReadyState extends _browserType.BrowserReadyState {
|
||||
onBrowserOutput(message) {
|
||||
const match = message.match(/DevTools listening on (.*)/);
|
||||
if (match) this._wsEndpoint.resolve(match[1]);
|
||||
}
|
||||
}
|
||||
async function urlToWSEndpoint(progress, endpointURL, headers) {
|
||||
if (endpointURL.startsWith('ws')) return endpointURL;
|
||||
progress.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
|
||||
const httpURL = endpointURL.endsWith('/') ? `${endpointURL}json/version/` : `${endpointURL}/json/version/`;
|
||||
const json = await (0, _network.fetchData)({
|
||||
url: httpURL,
|
||||
headers
|
||||
}, async (_, resp) => new Error(`Unexpected status ${resp.statusCode} when connecting to ${httpURL}.\n` + `This does not look like a DevTools server, try connecting via ws://.`));
|
||||
return JSON.parse(json).webSocketDebuggerUrl;
|
||||
}
|
||||
async function seleniumErrorHandler(params, response) {
|
||||
const body = await streamToString(response);
|
||||
let message = body;
|
||||
try {
|
||||
const json = JSON.parse(body);
|
||||
message = json.value.localizedMessage || json.value.message;
|
||||
} catch (e) {}
|
||||
return new Error(`Error connecting to Selenium at ${params.url}: ${message}`);
|
||||
}
|
||||
function addProtocol(url) {
|
||||
if (!['ws://', 'wss://', 'http://', 'https://'].some(protocol => url.startsWith(protocol))) return 'http://' + url;
|
||||
return url;
|
||||
}
|
||||
function streamToString(stream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
stream.on('data', chunk => chunks.push(Buffer.from(chunk)));
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
||||
});
|
||||
}
|
||||
function parseSeleniumRemoteParams(env, progress) {
|
||||
try {
|
||||
const parsed = JSON.parse(env.value);
|
||||
progress.log(`<selenium> using additional ${env.name} "${env.value}"`);
|
||||
return parsed;
|
||||
} catch (e) {
|
||||
progress.log(`<selenium> ignoring additional ${env.name} "${env.value}": ${e}`);
|
||||
}
|
||||
}
|
||||
46
node_modules/playwright-core/lib/server/chromium/chromiumSwitches.js
generated
vendored
Normal file
46
node_modules/playwright-core/lib/server/chromium/chromiumSwitches.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.chromiumSwitches = void 0;
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// No dependencies as it is used from the Electron loader.
|
||||
|
||||
const chromiumSwitches = exports.chromiumSwitches = ['--disable-field-trial-config',
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md
|
||||
'--disable-background-networking', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-back-forward-cache',
|
||||
// Avoids surprises like main request not being intercepted during page.goBack().
|
||||
'--disable-breakpad', '--disable-client-side-phishing-detection', '--disable-component-extensions-with-background-pages', '--disable-component-update',
|
||||
// Avoids unneeded network activity after startup.
|
||||
'--no-default-browser-check', '--disable-default-apps', '--disable-dev-shm-usage', '--disable-extensions',
|
||||
// AvoidUnnecessaryBeforeUnloadCheckSync - https://github.com/microsoft/playwright/issues/14047
|
||||
// Translate - https://github.com/microsoft/playwright/issues/16126
|
||||
// HttpsUpgrades - https://github.com/microsoft/playwright/pull/27605
|
||||
// PaintHolding - https://github.com/microsoft/playwright/issues/28023
|
||||
// ThirdPartyStoragePartitioning - https://github.com/microsoft/playwright/issues/32230
|
||||
// LensOverlay - Hides the Lens feature in the URL address bar. Its not working in unofficial builds.
|
||||
// PlzDedicatedWorker - https://github.com/microsoft/playwright/issues/31747
|
||||
'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker', '--allow-pre-commit-input', '--disable-hang-monitor', '--disable-ipc-flooding-protection', '--disable-popup-blocking', '--disable-prompt-on-repost', '--disable-renderer-backgrounding', '--force-color-profile=srgb', '--metrics-recording-only', '--no-first-run', '--enable-automation', '--password-store=basic', '--use-mock-keychain',
|
||||
// See https://chromium-review.googlesource.com/c/chromium/src/+/2436773
|
||||
'--no-service-autorun', '--export-tagged-pdf',
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/4853540
|
||||
'--disable-search-engine-choice-screen',
|
||||
// https://issues.chromium.org/41491762
|
||||
'--unsafely-disable-devtools-self-xss-warnings'];
|
||||
237
node_modules/playwright-core/lib/server/chromium/crAccessibility.js
generated
vendored
Normal file
237
node_modules/playwright-core/lib/server/chromium/crAccessibility.js
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getAccessibilityTree = getAccessibilityTree;
|
||||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function getAccessibilityTree(client, needle) {
|
||||
const {
|
||||
nodes
|
||||
} = await client.send('Accessibility.getFullAXTree');
|
||||
const tree = CRAXNode.createTree(client, nodes);
|
||||
return {
|
||||
tree,
|
||||
needle: needle ? await tree._findElement(needle) : null
|
||||
};
|
||||
}
|
||||
class CRAXNode {
|
||||
constructor(client, payload) {
|
||||
this._payload = void 0;
|
||||
this._children = [];
|
||||
this._richlyEditable = false;
|
||||
this._editable = false;
|
||||
this._focusable = false;
|
||||
this._expanded = false;
|
||||
this._hidden = false;
|
||||
this._name = void 0;
|
||||
this._role = void 0;
|
||||
this._cachedHasFocusableChild = void 0;
|
||||
this._client = void 0;
|
||||
this._client = client;
|
||||
this._payload = payload;
|
||||
this._name = this._payload.name ? this._payload.name.value : '';
|
||||
this._role = this._payload.role ? this._payload.role.value : 'Unknown';
|
||||
for (const property of this._payload.properties || []) {
|
||||
if (property.name === 'editable') {
|
||||
this._richlyEditable = property.value.value === 'richtext';
|
||||
this._editable = true;
|
||||
}
|
||||
if (property.name === 'focusable') this._focusable = property.value.value;
|
||||
if (property.name === 'expanded') this._expanded = property.value.value;
|
||||
if (property.name === 'hidden') this._hidden = property.value.value;
|
||||
}
|
||||
}
|
||||
_isPlainTextField() {
|
||||
if (this._richlyEditable) return false;
|
||||
if (this._editable) return true;
|
||||
return this._role === 'textbox' || this._role === 'ComboBox' || this._role === 'searchbox';
|
||||
}
|
||||
_isTextOnlyObject() {
|
||||
const role = this._role;
|
||||
return role === 'LineBreak' || role === 'text' || role === 'InlineTextBox' || role === 'StaticText';
|
||||
}
|
||||
_hasFocusableChild() {
|
||||
if (this._cachedHasFocusableChild === undefined) {
|
||||
this._cachedHasFocusableChild = false;
|
||||
for (const child of this._children) {
|
||||
if (child._focusable || child._hasFocusableChild()) {
|
||||
this._cachedHasFocusableChild = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._cachedHasFocusableChild;
|
||||
}
|
||||
children() {
|
||||
return this._children;
|
||||
}
|
||||
async _findElement(element) {
|
||||
const objectId = element._objectId;
|
||||
const {
|
||||
node: {
|
||||
backendNodeId
|
||||
}
|
||||
} = await this._client.send('DOM.describeNode', {
|
||||
objectId
|
||||
});
|
||||
const needle = this.find(node => node._payload.backendDOMNodeId === backendNodeId);
|
||||
return needle || null;
|
||||
}
|
||||
find(predicate) {
|
||||
if (predicate(this)) return this;
|
||||
for (const child of this._children) {
|
||||
const result = child.find(predicate);
|
||||
if (result) return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
isLeafNode() {
|
||||
if (!this._children.length) return true;
|
||||
|
||||
// These types of objects may have children that we use as internal
|
||||
// implementation details, but we want to expose them as leaves to platform
|
||||
// accessibility APIs because screen readers might be confused if they find
|
||||
// any children.
|
||||
if (this._isPlainTextField() || this._isTextOnlyObject()) return true;
|
||||
|
||||
// Roles whose children are only presentational according to the ARIA and
|
||||
// HTML5 Specs should be hidden from screen readers.
|
||||
// (Note that whilst ARIA buttons can have only presentational children, HTML5
|
||||
// buttons are allowed to have content.)
|
||||
switch (this._role) {
|
||||
case 'doc-cover':
|
||||
case 'graphics-symbol':
|
||||
case 'img':
|
||||
case 'Meter':
|
||||
case 'scrollbar':
|
||||
case 'slider':
|
||||
case 'separator':
|
||||
case 'progressbar':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Here and below: Android heuristics
|
||||
if (this._hasFocusableChild()) return false;
|
||||
if (this._focusable && this._role !== 'WebArea' && this._role !== 'RootWebArea' && this._name) return true;
|
||||
if (this._role === 'heading' && this._name) return true;
|
||||
return false;
|
||||
}
|
||||
isControl() {
|
||||
switch (this._role) {
|
||||
case 'button':
|
||||
case 'checkbox':
|
||||
case 'ColorWell':
|
||||
case 'combobox':
|
||||
case 'DisclosureTriangle':
|
||||
case 'listbox':
|
||||
case 'menu':
|
||||
case 'menubar':
|
||||
case 'menuitem':
|
||||
case 'menuitemcheckbox':
|
||||
case 'menuitemradio':
|
||||
case 'radio':
|
||||
case 'scrollbar':
|
||||
case 'searchbox':
|
||||
case 'slider':
|
||||
case 'spinbutton':
|
||||
case 'switch':
|
||||
case 'tab':
|
||||
case 'textbox':
|
||||
case 'tree':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
isInteresting(insideControl) {
|
||||
const role = this._role;
|
||||
if (role === 'Ignored' || this._hidden) return false;
|
||||
if (this._focusable || this._richlyEditable) return true;
|
||||
|
||||
// If it's not focusable but has a control role, then it's interesting.
|
||||
if (this.isControl()) return true;
|
||||
|
||||
// A non focusable child of a control is not interesting
|
||||
if (insideControl) return false;
|
||||
return this.isLeafNode() && !!this._name;
|
||||
}
|
||||
normalizedRole() {
|
||||
switch (this._role) {
|
||||
case 'RootWebArea':
|
||||
return 'WebArea';
|
||||
case 'StaticText':
|
||||
return 'text';
|
||||
default:
|
||||
return this._role;
|
||||
}
|
||||
}
|
||||
serialize() {
|
||||
const properties = new Map();
|
||||
for (const property of this._payload.properties || []) properties.set(property.name.toLowerCase(), property.value.value);
|
||||
if (this._payload.description) properties.set('description', this._payload.description.value);
|
||||
const node = {
|
||||
role: this.normalizedRole(),
|
||||
name: this._payload.name ? this._payload.name.value || '' : ''
|
||||
};
|
||||
const userStringProperties = ['description', 'keyshortcuts', 'roledescription', 'valuetext'];
|
||||
for (const userStringProperty of userStringProperties) {
|
||||
if (!properties.has(userStringProperty)) continue;
|
||||
node[userStringProperty] = properties.get(userStringProperty);
|
||||
}
|
||||
const booleanProperties = ['disabled', 'expanded', 'focused', 'modal', 'multiline', 'multiselectable', 'readonly', 'required', 'selected'];
|
||||
for (const booleanProperty of booleanProperties) {
|
||||
// WebArea's treat focus differently than other nodes. They report whether their frame has focus,
|
||||
// not whether focus is specifically on the root node.
|
||||
if (booleanProperty === 'focused' && (this._role === 'WebArea' || this._role === 'RootWebArea')) continue;
|
||||
const value = properties.get(booleanProperty);
|
||||
if (!value) continue;
|
||||
node[booleanProperty] = value;
|
||||
}
|
||||
const numericalProperties = ['level', 'valuemax', 'valuemin'];
|
||||
for (const numericalProperty of numericalProperties) {
|
||||
if (!properties.has(numericalProperty)) continue;
|
||||
node[numericalProperty] = properties.get(numericalProperty);
|
||||
}
|
||||
const tokenProperties = ['autocomplete', 'haspopup', 'invalid', 'orientation'];
|
||||
for (const tokenProperty of tokenProperties) {
|
||||
const value = properties.get(tokenProperty);
|
||||
if (!value || value === 'false') continue;
|
||||
node[tokenProperty] = value;
|
||||
}
|
||||
const axNode = node;
|
||||
if (this._payload.value) {
|
||||
if (typeof this._payload.value.value === 'string') axNode.valueString = this._payload.value.value;
|
||||
if (typeof this._payload.value.value === 'number') axNode.valueNumber = this._payload.value.value;
|
||||
}
|
||||
if (properties.has('checked')) axNode.checked = properties.get('checked') === 'true' ? 'checked' : properties.get('checked') === 'false' ? 'unchecked' : 'mixed';
|
||||
if (properties.has('pressed')) axNode.pressed = properties.get('pressed') === 'true' ? 'pressed' : properties.get('pressed') === 'false' ? 'released' : 'mixed';
|
||||
return axNode;
|
||||
}
|
||||
static createTree(client, payloads) {
|
||||
const nodeById = new Map();
|
||||
for (const payload of payloads) nodeById.set(payload.nodeId, new CRAXNode(client, payload));
|
||||
for (const node of nodeById.values()) {
|
||||
for (const childId of node._payload.childIds || []) node._children.push(nodeById.get(childId));
|
||||
}
|
||||
return nodeById.values().next().value;
|
||||
}
|
||||
}
|
||||
516
node_modules/playwright-core/lib/server/chromium/crBrowser.js
generated
vendored
Normal file
516
node_modules/playwright-core/lib/server/chromium/crBrowser.js
generated
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRBrowserContext = exports.CRBrowser = void 0;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _browser = require("../browser");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _utils = require("../../utils");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
var _page = require("../page");
|
||||
var _frames = require("../frames");
|
||||
var _crConnection = require("./crConnection");
|
||||
var _crPage = require("./crPage");
|
||||
var _crProtocolHelper = require("./crProtocolHelper");
|
||||
var _crServiceWorker = require("./crServiceWorker");
|
||||
var _artifact = require("../artifact");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CRBrowser extends _browser.Browser {
|
||||
static async connect(parent, transport, options, devtools) {
|
||||
// Make a copy in case we need to update `headful` property below.
|
||||
options = {
|
||||
...options
|
||||
};
|
||||
const connection = new _crConnection.CRConnection(transport, options.protocolLogger, options.browserLogsCollector);
|
||||
const browser = new CRBrowser(parent, connection, options);
|
||||
browser._devtools = devtools;
|
||||
if (browser.isClank()) browser._isCollocatedWithServer = false;
|
||||
const session = connection.rootSession;
|
||||
if (options.__testHookOnConnectToBrowser) await options.__testHookOnConnectToBrowser();
|
||||
const version = await session.send('Browser.getVersion');
|
||||
browser._version = version.product.substring(version.product.indexOf('/') + 1);
|
||||
browser._userAgent = version.userAgent;
|
||||
// We don't trust the option as it may lie in case of connectOverCDP where remote browser
|
||||
// may have been launched with different options.
|
||||
browser.options.headful = !version.userAgent.includes('Headless');
|
||||
if (!options.persistent) {
|
||||
await session.send('Target.setAutoAttach', {
|
||||
autoAttach: true,
|
||||
waitForDebuggerOnStart: true,
|
||||
flatten: true
|
||||
});
|
||||
return browser;
|
||||
}
|
||||
browser._defaultContext = new CRBrowserContext(browser, undefined, options.persistent);
|
||||
await Promise.all([session.send('Target.setAutoAttach', {
|
||||
autoAttach: true,
|
||||
waitForDebuggerOnStart: true,
|
||||
flatten: true
|
||||
}).then(async () => {
|
||||
// Target.setAutoAttach has a bug where it does not wait for new Targets being attached.
|
||||
// However making a dummy call afterwards fixes this.
|
||||
// This can be removed after https://chromium-review.googlesource.com/c/chromium/src/+/2885888 lands in stable.
|
||||
await session.send('Target.getTargetInfo');
|
||||
}), browser._defaultContext._initialize()]);
|
||||
await browser._waitForAllPagesToBeInitialized();
|
||||
return browser;
|
||||
}
|
||||
constructor(parent, connection, options) {
|
||||
super(parent, options);
|
||||
this._connection = void 0;
|
||||
this._session = void 0;
|
||||
this._clientRootSessionPromise = null;
|
||||
this._contexts = new Map();
|
||||
this._crPages = new Map();
|
||||
this._backgroundPages = new Map();
|
||||
this._serviceWorkers = new Map();
|
||||
this._devtools = void 0;
|
||||
this._version = '';
|
||||
this._tracingRecording = false;
|
||||
this._tracingClient = void 0;
|
||||
this._userAgent = '';
|
||||
this._connection = connection;
|
||||
this._session = this._connection.rootSession;
|
||||
this._connection.on(_crConnection.ConnectionEvents.Disconnected, () => this._didDisconnect());
|
||||
this._session.on('Target.attachedToTarget', this._onAttachedToTarget.bind(this));
|
||||
this._session.on('Target.detachedFromTarget', this._onDetachedFromTarget.bind(this));
|
||||
this._session.on('Browser.downloadWillBegin', this._onDownloadWillBegin.bind(this));
|
||||
this._session.on('Browser.downloadProgress', this._onDownloadProgress.bind(this));
|
||||
}
|
||||
async doCreateNewContext(options) {
|
||||
const proxy = options.proxyOverride || options.proxy;
|
||||
let proxyBypassList = undefined;
|
||||
if (proxy) {
|
||||
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK) proxyBypassList = proxy.bypass;else proxyBypassList = '<-loopback>' + (proxy.bypass ? `,${proxy.bypass}` : '');
|
||||
}
|
||||
const {
|
||||
browserContextId
|
||||
} = await this._session.send('Target.createBrowserContext', {
|
||||
disposeOnDetach: true,
|
||||
proxyServer: proxy ? proxy.server : undefined,
|
||||
proxyBypassList
|
||||
});
|
||||
const context = new CRBrowserContext(this, browserContextId, options);
|
||||
await context._initialize();
|
||||
this._contexts.set(browserContextId, context);
|
||||
return context;
|
||||
}
|
||||
contexts() {
|
||||
return Array.from(this._contexts.values());
|
||||
}
|
||||
version() {
|
||||
return this._version;
|
||||
}
|
||||
userAgent() {
|
||||
return this._userAgent;
|
||||
}
|
||||
_platform() {
|
||||
if (this._userAgent.includes('Windows')) return 'win';
|
||||
if (this._userAgent.includes('Macintosh')) return 'mac';
|
||||
return 'linux';
|
||||
}
|
||||
isClank() {
|
||||
return this.options.name === 'clank';
|
||||
}
|
||||
async _waitForAllPagesToBeInitialized() {
|
||||
await Promise.all([...this._crPages.values()].map(page => page.pageOrError()));
|
||||
}
|
||||
_onAttachedToTarget({
|
||||
targetInfo,
|
||||
sessionId,
|
||||
waitingForDebugger
|
||||
}) {
|
||||
if (targetInfo.type === 'browser') return;
|
||||
const session = this._session.createChildSession(sessionId);
|
||||
(0, _utils.assert)(targetInfo.browserContextId, 'targetInfo: ' + JSON.stringify(targetInfo, null, 2));
|
||||
let context = this._contexts.get(targetInfo.browserContextId) || null;
|
||||
if (!context) {
|
||||
// TODO: auto attach only to pages from our contexts.
|
||||
// assert(this._defaultContext);
|
||||
context = this._defaultContext;
|
||||
}
|
||||
if (targetInfo.type === 'other' && targetInfo.url.startsWith('devtools://devtools') && this._devtools) {
|
||||
this._devtools.install(session);
|
||||
return;
|
||||
}
|
||||
const treatOtherAsPage = targetInfo.type === 'other' && process.env.PW_CHROMIUM_ATTACH_TO_OTHER;
|
||||
if (!context || targetInfo.type === 'other' && !treatOtherAsPage) {
|
||||
session.detach().catch(() => {});
|
||||
return;
|
||||
}
|
||||
(0, _utils.assert)(!this._crPages.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
|
||||
(0, _utils.assert)(!this._backgroundPages.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
|
||||
(0, _utils.assert)(!this._serviceWorkers.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
|
||||
if (targetInfo.type === 'background_page') {
|
||||
const backgroundPage = new _crPage.CRPage(session, targetInfo.targetId, context, null, {
|
||||
hasUIWindow: false,
|
||||
isBackgroundPage: true
|
||||
});
|
||||
this._backgroundPages.set(targetInfo.targetId, backgroundPage);
|
||||
return;
|
||||
}
|
||||
if (targetInfo.type === 'page' || treatOtherAsPage) {
|
||||
const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null;
|
||||
const crPage = new _crPage.CRPage(session, targetInfo.targetId, context, opener, {
|
||||
hasUIWindow: targetInfo.type === 'page',
|
||||
isBackgroundPage: false
|
||||
});
|
||||
this._crPages.set(targetInfo.targetId, crPage);
|
||||
return;
|
||||
}
|
||||
if (targetInfo.type === 'service_worker') {
|
||||
const serviceWorker = new _crServiceWorker.CRServiceWorker(context, session, targetInfo.url);
|
||||
this._serviceWorkers.set(targetInfo.targetId, serviceWorker);
|
||||
context.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker);
|
||||
return;
|
||||
}
|
||||
|
||||
// Detach from any targets we are not interested in, to avoid side-effects.
|
||||
//
|
||||
// One example of a side effect: upon shared worker restart, we receive
|
||||
// Inspector.targetReloadedAfterCrash and backend waits for Runtime.runIfWaitingForDebugger
|
||||
// from any attached client. If we do not resume, shared worker will stall.
|
||||
session.detach().catch(() => {});
|
||||
}
|
||||
_onDetachedFromTarget(payload) {
|
||||
const targetId = payload.targetId;
|
||||
const crPage = this._crPages.get(targetId);
|
||||
if (crPage) {
|
||||
this._crPages.delete(targetId);
|
||||
crPage.didClose();
|
||||
return;
|
||||
}
|
||||
const backgroundPage = this._backgroundPages.get(targetId);
|
||||
if (backgroundPage) {
|
||||
this._backgroundPages.delete(targetId);
|
||||
backgroundPage.didClose();
|
||||
return;
|
||||
}
|
||||
const serviceWorker = this._serviceWorkers.get(targetId);
|
||||
if (serviceWorker) {
|
||||
this._serviceWorkers.delete(targetId);
|
||||
serviceWorker.didClose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
_didDisconnect() {
|
||||
for (const crPage of this._crPages.values()) crPage.didClose();
|
||||
this._crPages.clear();
|
||||
for (const backgroundPage of this._backgroundPages.values()) backgroundPage.didClose();
|
||||
this._backgroundPages.clear();
|
||||
for (const serviceWorker of this._serviceWorkers.values()) serviceWorker.didClose();
|
||||
this._serviceWorkers.clear();
|
||||
this._didClose();
|
||||
}
|
||||
_findOwningPage(frameId) {
|
||||
for (const crPage of this._crPages.values()) {
|
||||
const frame = crPage._page._frameManager.frame(frameId);
|
||||
if (frame) return crPage;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
_onDownloadWillBegin(payload) {
|
||||
const page = this._findOwningPage(payload.frameId);
|
||||
if (!page) {
|
||||
// There might be no page when download originates from something unusual, like
|
||||
// a DevTools window or maybe an extension page.
|
||||
// See https://github.com/microsoft/playwright/issues/22551.
|
||||
return;
|
||||
}
|
||||
page.willBeginDownload();
|
||||
let originPage = page._initializedPage;
|
||||
// If it's a new window download, report it on the opener page.
|
||||
if (!originPage && page._opener) originPage = page._opener._initializedPage;
|
||||
if (!originPage) return;
|
||||
this._downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename);
|
||||
}
|
||||
_onDownloadProgress(payload) {
|
||||
if (payload.state === 'completed') this._downloadFinished(payload.guid, '');
|
||||
if (payload.state === 'canceled') this._downloadFinished(payload.guid, this._closeReason || 'canceled');
|
||||
}
|
||||
async _closePage(crPage) {
|
||||
await this._session.send('Target.closeTarget', {
|
||||
targetId: crPage._targetId
|
||||
});
|
||||
}
|
||||
async newBrowserCDPSession() {
|
||||
return await this._connection.createBrowserSession();
|
||||
}
|
||||
async startTracing(page, options = {}) {
|
||||
(0, _utils.assert)(!this._tracingRecording, 'Cannot start recording trace while already recording trace.');
|
||||
this._tracingClient = page ? page._delegate._mainFrameSession._client : this._session;
|
||||
const defaultCategories = ['-*', 'devtools.timeline', 'v8.execute', 'disabled-by-default-devtools.timeline', 'disabled-by-default-devtools.timeline.frame', 'toplevel', 'blink.console', 'blink.user_timing', 'latencyInfo', 'disabled-by-default-devtools.timeline.stack', 'disabled-by-default-v8.cpu_profiler', 'disabled-by-default-v8.cpu_profiler.hires'];
|
||||
const {
|
||||
screenshots = false,
|
||||
categories = defaultCategories
|
||||
} = options;
|
||||
if (screenshots) categories.push('disabled-by-default-devtools.screenshot');
|
||||
this._tracingRecording = true;
|
||||
await this._tracingClient.send('Tracing.start', {
|
||||
transferMode: 'ReturnAsStream',
|
||||
categories: categories.join(',')
|
||||
});
|
||||
}
|
||||
async stopTracing() {
|
||||
(0, _utils.assert)(this._tracingClient, 'Tracing was not started.');
|
||||
const [event] = await Promise.all([new Promise(f => this._tracingClient.once('Tracing.tracingComplete', f)), this._tracingClient.send('Tracing.end')]);
|
||||
const tracingPath = _path.default.join(this.options.artifactsDir, (0, _utils.createGuid)() + '.crtrace');
|
||||
await (0, _crProtocolHelper.saveProtocolStream)(this._tracingClient, event.stream, tracingPath);
|
||||
this._tracingRecording = false;
|
||||
const artifact = new _artifact.Artifact(this, tracingPath);
|
||||
artifact.reportFinished();
|
||||
return artifact;
|
||||
}
|
||||
isConnected() {
|
||||
return !this._connection._closed;
|
||||
}
|
||||
async _clientRootSession() {
|
||||
if (!this._clientRootSessionPromise) this._clientRootSessionPromise = this._connection.createBrowserSession();
|
||||
return this._clientRootSessionPromise;
|
||||
}
|
||||
}
|
||||
exports.CRBrowser = CRBrowser;
|
||||
class CRBrowserContext extends _browserContext.BrowserContext {
|
||||
constructor(browser, browserContextId, options) {
|
||||
super(browser, options, browserContextId);
|
||||
this._authenticateProxyViaCredentials();
|
||||
}
|
||||
async _initialize() {
|
||||
(0, _utils.assert)(!Array.from(this._browser._crPages.values()).some(page => page._browserContext === this));
|
||||
const promises = [super._initialize()];
|
||||
if (this._browser.options.name !== 'clank' && this._options.acceptDownloads !== 'internal-browser-default') {
|
||||
promises.push(this._browser._session.send('Browser.setDownloadBehavior', {
|
||||
behavior: this._options.acceptDownloads === 'accept' ? 'allowAndName' : 'deny',
|
||||
browserContextId: this._browserContextId,
|
||||
downloadPath: this._browser.options.downloadsPath,
|
||||
eventsEnabled: true
|
||||
}));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
_crPages() {
|
||||
return [...this._browser._crPages.values()].filter(crPage => crPage._browserContext === this);
|
||||
}
|
||||
pages() {
|
||||
return this._crPages().map(crPage => crPage._initializedPage).filter(Boolean);
|
||||
}
|
||||
async newPageDelegate() {
|
||||
(0, _browserContext.assertBrowserContextIsNotOwned)(this);
|
||||
const oldKeys = this._browser.isClank() ? new Set(this._browser._crPages.keys()) : undefined;
|
||||
let {
|
||||
targetId
|
||||
} = await this._browser._session.send('Target.createTarget', {
|
||||
url: 'about:blank',
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
if (oldKeys) {
|
||||
// Chrome for Android returns tab ids (1, 2, 3, 4, 5) instead of content target ids here, work around it via the
|
||||
// heuristic assuming that there is only one page created at a time.
|
||||
const newKeys = new Set(this._browser._crPages.keys());
|
||||
// Remove old keys.
|
||||
for (const key of oldKeys) newKeys.delete(key);
|
||||
// Remove potential concurrent popups.
|
||||
for (const key of newKeys) {
|
||||
const page = this._browser._crPages.get(key);
|
||||
if (page._opener) newKeys.delete(key);
|
||||
}
|
||||
(0, _utils.assert)(newKeys.size === 1);
|
||||
[targetId] = [...newKeys];
|
||||
}
|
||||
return this._browser._crPages.get(targetId);
|
||||
}
|
||||
async doGetCookies(urls) {
|
||||
const {
|
||||
cookies
|
||||
} = await this._browser._session.send('Storage.getCookies', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
return network.filterCookies(cookies.map(c => {
|
||||
const copy = {
|
||||
sameSite: 'Lax',
|
||||
...c
|
||||
};
|
||||
delete copy.size;
|
||||
delete copy.priority;
|
||||
delete copy.session;
|
||||
delete copy.sameParty;
|
||||
delete copy.sourceScheme;
|
||||
delete copy.sourcePort;
|
||||
return copy;
|
||||
}), urls);
|
||||
}
|
||||
async addCookies(cookies) {
|
||||
await this._browser._session.send('Storage.setCookies', {
|
||||
cookies: network.rewriteCookies(cookies),
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
async doClearCookies() {
|
||||
await this._browser._session.send('Storage.clearCookies', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
async doGrantPermissions(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'], ['storage-access', 'storageAccess']]);
|
||||
const filtered = permissions.map(permission => {
|
||||
const protocolPermission = webPermissionToProtocol.get(permission);
|
||||
if (!protocolPermission) throw new Error('Unknown permission: ' + permission);
|
||||
return protocolPermission;
|
||||
});
|
||||
await this._browser._session.send('Browser.grantPermissions', {
|
||||
origin: origin === '*' ? undefined : origin,
|
||||
browserContextId: this._browserContextId,
|
||||
permissions: filtered
|
||||
});
|
||||
}
|
||||
async doClearPermissions() {
|
||||
await this._browser._session.send('Browser.resetPermissions', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
async setGeolocation(geolocation) {
|
||||
(0, _browserContext.verifyGeolocation)(geolocation);
|
||||
this._options.geolocation = geolocation;
|
||||
for (const page of this.pages()) await page._delegate.updateGeolocation();
|
||||
}
|
||||
async setExtraHTTPHeaders(headers) {
|
||||
this._options.extraHTTPHeaders = headers;
|
||||
for (const page of this.pages()) await page._delegate.updateExtraHTTPHeaders();
|
||||
for (const sw of this.serviceWorkers()) await sw.updateExtraHTTPHeaders();
|
||||
}
|
||||
async setUserAgent(userAgent) {
|
||||
this._options.userAgent = userAgent;
|
||||
for (const page of this.pages()) await page._delegate.updateUserAgent();
|
||||
// TODO: service workers don't have Emulation domain?
|
||||
}
|
||||
async setOffline(offline) {
|
||||
this._options.offline = offline;
|
||||
for (const page of this.pages()) await page._delegate.updateOffline();
|
||||
for (const sw of this.serviceWorkers()) await sw.updateOffline();
|
||||
}
|
||||
async doSetHTTPCredentials(httpCredentials) {
|
||||
this._options.httpCredentials = httpCredentials;
|
||||
for (const page of this.pages()) await page._delegate.updateHttpCredentials();
|
||||
for (const sw of this.serviceWorkers()) await sw.updateHttpCredentials();
|
||||
}
|
||||
async doAddInitScript(initScript) {
|
||||
for (const page of this.pages()) await page._delegate.addInitScript(initScript);
|
||||
}
|
||||
async doRemoveNonInternalInitScripts() {
|
||||
for (const page of this.pages()) await page._delegate.removeNonInternalInitScripts();
|
||||
}
|
||||
async doUpdateRequestInterception() {
|
||||
for (const page of this.pages()) await page._delegate.updateRequestInterception();
|
||||
for (const sw of this.serviceWorkers()) await sw.updateRequestInterception();
|
||||
}
|
||||
async doClose(reason) {
|
||||
// Headful chrome cannot dispose browser context with opened 'beforeunload'
|
||||
// dialogs, so we should close all that are currently opened.
|
||||
// We also won't get new ones since `Target.disposeBrowserContext` does not trigger
|
||||
// beforeunload.
|
||||
const openedBeforeUnloadDialogs = [];
|
||||
for (const crPage of this._crPages()) {
|
||||
const dialogs = [...crPage._page._frameManager._openedDialogs].filter(dialog => dialog.type() === 'beforeunload');
|
||||
openedBeforeUnloadDialogs.push(...dialogs);
|
||||
}
|
||||
await Promise.all(openedBeforeUnloadDialogs.map(dialog => dialog.dismiss()));
|
||||
if (!this._browserContextId) {
|
||||
await this.stopVideoRecording();
|
||||
// Closing persistent context should close the browser.
|
||||
await this._browser.close({
|
||||
reason
|
||||
});
|
||||
return;
|
||||
}
|
||||
await this._browser._session.send('Target.disposeBrowserContext', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
this._browser._contexts.delete(this._browserContextId);
|
||||
for (const [targetId, serviceWorker] of this._browser._serviceWorkers) {
|
||||
if (serviceWorker._browserContext !== this) continue;
|
||||
// When closing a browser context, service workers are shutdown
|
||||
// asynchronously and we get detached from them later.
|
||||
// To avoid the wrong order of notifications, we manually fire
|
||||
// "close" event here and forget about the service worker.
|
||||
serviceWorker.didClose();
|
||||
this._browser._serviceWorkers.delete(targetId);
|
||||
}
|
||||
}
|
||||
async stopVideoRecording() {
|
||||
await Promise.all(this._crPages().map(crPage => crPage._mainFrameSession._stopVideoRecording()));
|
||||
}
|
||||
onClosePersistent() {
|
||||
// When persistent context is closed, we do not necessary get Target.detachedFromTarget
|
||||
// for all the background pages.
|
||||
for (const [targetId, backgroundPage] of this._browser._backgroundPages.entries()) {
|
||||
if (backgroundPage._browserContext === this && backgroundPage._initializedPage) {
|
||||
backgroundPage.didClose();
|
||||
this._browser._backgroundPages.delete(targetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
async clearCache() {
|
||||
for (const page of this._crPages()) await page._networkManager.clearCache();
|
||||
}
|
||||
async cancelDownload(guid) {
|
||||
// The upstream CDP method is implemented in a way that no explicit error would be given
|
||||
// regarding the requested `guid`, even if the download is in a state not suitable for
|
||||
// cancellation (finished, cancelled, etc.) or the guid is invalid at all.
|
||||
await this._browser._session.send('Browser.cancelDownload', {
|
||||
guid: guid,
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
backgroundPages() {
|
||||
const result = [];
|
||||
for (const backgroundPage of this._browser._backgroundPages.values()) {
|
||||
if (backgroundPage._browserContext === this && backgroundPage._initializedPage) result.push(backgroundPage._initializedPage);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
serviceWorkers() {
|
||||
return Array.from(this._browser._serviceWorkers.values()).filter(serviceWorker => serviceWorker._browserContext === this);
|
||||
}
|
||||
async newCDPSession(page) {
|
||||
let targetId = null;
|
||||
if (page instanceof _page.Page) {
|
||||
targetId = page._delegate._targetId;
|
||||
} else if (page instanceof _frames.Frame) {
|
||||
const session = page._page._delegate._sessions.get(page._id);
|
||||
if (!session) throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`);
|
||||
targetId = session._targetId;
|
||||
} else {
|
||||
throw new Error('page: expected Page or Frame');
|
||||
}
|
||||
const rootSession = await this._browser._clientRootSession();
|
||||
return rootSession.attachToTarget(targetId);
|
||||
}
|
||||
}
|
||||
exports.CRBrowserContext = CRBrowserContext;
|
||||
CRBrowserContext.CREvents = {
|
||||
BackgroundPage: 'backgroundpage',
|
||||
ServiceWorker: 'serviceworker'
|
||||
};
|
||||
228
node_modules/playwright-core/lib/server/chromium/crConnection.js
generated
vendored
Normal file
228
node_modules/playwright-core/lib/server/chromium/crConnection.js
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.kBrowserCloseMessageId = exports.ConnectionEvents = exports.CRSession = exports.CRConnection = exports.CDPSession = void 0;
|
||||
var _utils = require("../../utils");
|
||||
var _events = require("events");
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _helper = require("../helper");
|
||||
var _protocolError = require("../protocolError");
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const ConnectionEvents = exports.ConnectionEvents = {
|
||||
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
||||
};
|
||||
|
||||
// CRPlaywright uses this special id to issue Browser.close command which we
|
||||
// should ignore.
|
||||
const kBrowserCloseMessageId = exports.kBrowserCloseMessageId = -9999;
|
||||
class CRConnection extends _events.EventEmitter {
|
||||
constructor(transport, protocolLogger, browserLogsCollector) {
|
||||
super();
|
||||
this._lastId = 0;
|
||||
this._transport = void 0;
|
||||
this._sessions = new Map();
|
||||
this._protocolLogger = void 0;
|
||||
this._browserLogsCollector = void 0;
|
||||
this._browserDisconnectedLogs = void 0;
|
||||
this.rootSession = void 0;
|
||||
this._closed = false;
|
||||
this.setMaxListeners(0);
|
||||
this._transport = transport;
|
||||
this._protocolLogger = protocolLogger;
|
||||
this._browserLogsCollector = browserLogsCollector;
|
||||
this.rootSession = new CRSession(this, null, '');
|
||||
this._sessions.set('', this.rootSession);
|
||||
this._transport.onmessage = this._onMessage.bind(this);
|
||||
// onclose should be set last, since it can be immediately called.
|
||||
this._transport.onclose = this._onClose.bind(this);
|
||||
}
|
||||
_rawSend(sessionId, method, params) {
|
||||
const id = ++this._lastId;
|
||||
const message = {
|
||||
id,
|
||||
method,
|
||||
params
|
||||
};
|
||||
if (sessionId) message.sessionId = sessionId;
|
||||
this._protocolLogger('send', message);
|
||||
this._transport.send(message);
|
||||
return id;
|
||||
}
|
||||
async _onMessage(message) {
|
||||
this._protocolLogger('receive', message);
|
||||
if (message.id === kBrowserCloseMessageId) return;
|
||||
const session = this._sessions.get(message.sessionId || '');
|
||||
if (session) session._onMessage(message);
|
||||
}
|
||||
_onClose(reason) {
|
||||
this._closed = true;
|
||||
this._transport.onmessage = undefined;
|
||||
this._transport.onclose = undefined;
|
||||
this._browserDisconnectedLogs = _helper.helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
|
||||
this.rootSession.dispose();
|
||||
Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
|
||||
}
|
||||
close() {
|
||||
if (!this._closed) this._transport.close();
|
||||
}
|
||||
async createBrowserSession() {
|
||||
const {
|
||||
sessionId
|
||||
} = await this.rootSession.send('Target.attachToBrowserTarget');
|
||||
return new CDPSession(this.rootSession, sessionId);
|
||||
}
|
||||
}
|
||||
exports.CRConnection = CRConnection;
|
||||
class CRSession extends _events.EventEmitter {
|
||||
constructor(connection, parentSession, sessionId, eventListener) {
|
||||
super();
|
||||
this._connection = void 0;
|
||||
this._eventListener = void 0;
|
||||
this._callbacks = new Map();
|
||||
this._sessionId = void 0;
|
||||
this._parentSession = void 0;
|
||||
this._crashed = false;
|
||||
this._closed = false;
|
||||
this.on = void 0;
|
||||
this.addListener = void 0;
|
||||
this.off = void 0;
|
||||
this.removeListener = void 0;
|
||||
this.once = void 0;
|
||||
this.setMaxListeners(0);
|
||||
this._connection = connection;
|
||||
this._parentSession = parentSession;
|
||||
this._sessionId = sessionId;
|
||||
this._eventListener = eventListener;
|
||||
this.on = super.on;
|
||||
this.addListener = super.addListener;
|
||||
this.off = super.removeListener;
|
||||
this.removeListener = super.removeListener;
|
||||
this.once = super.once;
|
||||
}
|
||||
_markAsCrashed() {
|
||||
this._crashed = true;
|
||||
}
|
||||
createChildSession(sessionId, eventListener) {
|
||||
const session = new CRSession(this._connection, this, sessionId, eventListener);
|
||||
this._connection._sessions.set(sessionId, session);
|
||||
return session;
|
||||
}
|
||||
async send(method, params) {
|
||||
if (this._crashed || this._closed || this._connection._closed || this._connection._browserDisconnectedLogs) throw new _protocolError.ProtocolError(this._crashed ? 'crashed' : 'closed', undefined, this._connection._browserDisconnectedLogs);
|
||||
const id = this._connection._rawSend(this._sessionId, method, params);
|
||||
return new Promise((resolve, reject) => {
|
||||
this._callbacks.set(id, {
|
||||
resolve,
|
||||
reject,
|
||||
error: new _protocolError.ProtocolError('error', method)
|
||||
});
|
||||
});
|
||||
}
|
||||
_sendMayFail(method, params) {
|
||||
return this.send(method, params).catch(error => _debugLogger.debugLogger.log('error', error));
|
||||
}
|
||||
_onMessage(object) {
|
||||
var _object$error;
|
||||
if (object.id && this._callbacks.has(object.id)) {
|
||||
const callback = this._callbacks.get(object.id);
|
||||
this._callbacks.delete(object.id);
|
||||
if (object.error) {
|
||||
callback.error.setMessage(object.error.message);
|
||||
callback.reject(callback.error);
|
||||
} else {
|
||||
callback.resolve(object.result);
|
||||
}
|
||||
} else if (object.id && ((_object$error = object.error) === null || _object$error === void 0 ? void 0 : _object$error.code) === -32001) {
|
||||
// Message to a closed session, just ignore it.
|
||||
} else {
|
||||
var _object$error2;
|
||||
(0, _utils.assert)(!object.id, (object === null || object === void 0 || (_object$error2 = object.error) === null || _object$error2 === void 0 ? void 0 : _object$error2.message) || undefined);
|
||||
Promise.resolve().then(() => {
|
||||
if (this._eventListener) this._eventListener(object.method, object.params);
|
||||
this.emit(object.method, object.params);
|
||||
});
|
||||
}
|
||||
}
|
||||
async detach() {
|
||||
if (this._closed) throw new Error(`Session already detached. Most likely the page has been closed.`);
|
||||
if (!this._parentSession) throw new Error('Root session cannot be closed');
|
||||
// Ideally, detaching should resume any target, but there is a bug in the backend,
|
||||
// so we must Runtime.runIfWaitingForDebugger first.
|
||||
await this._sendMayFail('Runtime.runIfWaitingForDebugger');
|
||||
await this._parentSession.send('Target.detachFromTarget', {
|
||||
sessionId: this._sessionId
|
||||
});
|
||||
this.dispose();
|
||||
}
|
||||
dispose() {
|
||||
this._closed = true;
|
||||
this._connection._sessions.delete(this._sessionId);
|
||||
for (const callback of this._callbacks.values()) {
|
||||
callback.error.setMessage(`Internal server error, session closed.`);
|
||||
callback.error.type = this._crashed ? 'crashed' : 'closed';
|
||||
callback.error.logs = this._connection._browserDisconnectedLogs;
|
||||
callback.reject(callback.error);
|
||||
}
|
||||
this._callbacks.clear();
|
||||
}
|
||||
}
|
||||
exports.CRSession = CRSession;
|
||||
class CDPSession extends _events.EventEmitter {
|
||||
constructor(parentSession, sessionId) {
|
||||
super();
|
||||
this.guid = void 0;
|
||||
this._session = void 0;
|
||||
this._listeners = [];
|
||||
this.guid = `cdp-session@${sessionId}`;
|
||||
this._session = parentSession.createChildSession(sessionId, (method, params) => this.emit(CDPSession.Events.Event, {
|
||||
method,
|
||||
params
|
||||
}));
|
||||
this._listeners = [_utils.eventsHelper.addEventListener(parentSession, 'Target.detachedFromTarget', event => {
|
||||
if (event.sessionId === sessionId) this._onClose();
|
||||
})];
|
||||
}
|
||||
async send(method, params) {
|
||||
return await this._session.send(method, params);
|
||||
}
|
||||
async detach() {
|
||||
return await this._session.detach();
|
||||
}
|
||||
async attachToTarget(targetId) {
|
||||
const {
|
||||
sessionId
|
||||
} = await this.send('Target.attachToTarget', {
|
||||
targetId,
|
||||
flatten: true
|
||||
});
|
||||
return new CDPSession(this._session, sessionId);
|
||||
}
|
||||
_onClose() {
|
||||
_utils.eventsHelper.removeEventListeners(this._listeners);
|
||||
this._session.dispose();
|
||||
this.emit(CDPSession.Events.Closed);
|
||||
}
|
||||
}
|
||||
exports.CDPSession = CDPSession;
|
||||
CDPSession.Events = {
|
||||
Event: 'event',
|
||||
Closed: 'close'
|
||||
};
|
||||
246
node_modules/playwright-core/lib/server/chromium/crCoverage.js
generated
vendored
Normal file
246
node_modules/playwright-core/lib/server/chromium/crCoverage.js
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRCoverage = void 0;
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CRCoverage {
|
||||
constructor(client) {
|
||||
this._jsCoverage = void 0;
|
||||
this._cssCoverage = void 0;
|
||||
this._jsCoverage = new JSCoverage(client);
|
||||
this._cssCoverage = new CSSCoverage(client);
|
||||
}
|
||||
async startJSCoverage(options) {
|
||||
return await this._jsCoverage.start(options);
|
||||
}
|
||||
async stopJSCoverage() {
|
||||
return await this._jsCoverage.stop();
|
||||
}
|
||||
async startCSSCoverage(options) {
|
||||
return await this._cssCoverage.start(options);
|
||||
}
|
||||
async stopCSSCoverage() {
|
||||
return await this._cssCoverage.stop();
|
||||
}
|
||||
}
|
||||
exports.CRCoverage = CRCoverage;
|
||||
class JSCoverage {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._enabled = void 0;
|
||||
this._scriptIds = void 0;
|
||||
this._scriptSources = void 0;
|
||||
this._eventListeners = void 0;
|
||||
this._resetOnNavigation = void 0;
|
||||
this._reportAnonymousScripts = false;
|
||||
this._client = client;
|
||||
this._enabled = false;
|
||||
this._scriptIds = new Set();
|
||||
this._scriptSources = new Map();
|
||||
this._eventListeners = [];
|
||||
this._resetOnNavigation = false;
|
||||
}
|
||||
async start(options) {
|
||||
(0, _utils.assert)(!this._enabled, 'JSCoverage is already enabled');
|
||||
const {
|
||||
resetOnNavigation = true,
|
||||
reportAnonymousScripts = false
|
||||
} = options;
|
||||
this._resetOnNavigation = resetOnNavigation;
|
||||
this._reportAnonymousScripts = reportAnonymousScripts;
|
||||
this._enabled = true;
|
||||
this._scriptIds.clear();
|
||||
this._scriptSources.clear();
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._client, 'Debugger.paused', this._onDebuggerPaused.bind(this))];
|
||||
await Promise.all([this._client.send('Profiler.enable'), this._client.send('Profiler.startPreciseCoverage', {
|
||||
callCount: true,
|
||||
detailed: true
|
||||
}), this._client.send('Debugger.enable'), this._client.send('Debugger.setSkipAllPauses', {
|
||||
skip: true
|
||||
})]);
|
||||
}
|
||||
_onDebuggerPaused() {
|
||||
this._client.send('Debugger.resume');
|
||||
}
|
||||
_onExecutionContextsCleared() {
|
||||
if (!this._resetOnNavigation) return;
|
||||
this._scriptIds.clear();
|
||||
this._scriptSources.clear();
|
||||
}
|
||||
async _onScriptParsed(event) {
|
||||
this._scriptIds.add(event.scriptId);
|
||||
// Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
|
||||
if (!event.url && !this._reportAnonymousScripts) return;
|
||||
// This might fail if the page has already navigated away.
|
||||
const response = await this._client._sendMayFail('Debugger.getScriptSource', {
|
||||
scriptId: event.scriptId
|
||||
});
|
||||
if (response) this._scriptSources.set(event.scriptId, response.scriptSource);
|
||||
}
|
||||
async stop() {
|
||||
(0, _utils.assert)(this._enabled, 'JSCoverage is not enabled');
|
||||
this._enabled = false;
|
||||
const [profileResponse] = await Promise.all([this._client.send('Profiler.takePreciseCoverage'), this._client.send('Profiler.stopPreciseCoverage'), this._client.send('Profiler.disable'), this._client.send('Debugger.disable')]);
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
const coverage = {
|
||||
entries: []
|
||||
};
|
||||
for (const entry of profileResponse.result) {
|
||||
if (!this._scriptIds.has(entry.scriptId)) continue;
|
||||
if (!entry.url && !this._reportAnonymousScripts) continue;
|
||||
const source = this._scriptSources.get(entry.scriptId);
|
||||
if (source) coverage.entries.push({
|
||||
...entry,
|
||||
source
|
||||
});else coverage.entries.push(entry);
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
}
|
||||
class CSSCoverage {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._enabled = void 0;
|
||||
this._stylesheetURLs = void 0;
|
||||
this._stylesheetSources = void 0;
|
||||
this._eventListeners = void 0;
|
||||
this._resetOnNavigation = void 0;
|
||||
this._client = client;
|
||||
this._enabled = false;
|
||||
this._stylesheetURLs = new Map();
|
||||
this._stylesheetSources = new Map();
|
||||
this._eventListeners = [];
|
||||
this._resetOnNavigation = false;
|
||||
}
|
||||
async start(options) {
|
||||
(0, _utils.assert)(!this._enabled, 'CSSCoverage is already enabled');
|
||||
const {
|
||||
resetOnNavigation = true
|
||||
} = options;
|
||||
this._resetOnNavigation = resetOnNavigation;
|
||||
this._enabled = true;
|
||||
this._stylesheetURLs.clear();
|
||||
this._stylesheetSources.clear();
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._client, 'CSS.styleSheetAdded', this._onStyleSheet.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this))];
|
||||
await Promise.all([this._client.send('DOM.enable'), this._client.send('CSS.enable'), this._client.send('CSS.startRuleUsageTracking')]);
|
||||
}
|
||||
_onExecutionContextsCleared() {
|
||||
if (!this._resetOnNavigation) return;
|
||||
this._stylesheetURLs.clear();
|
||||
this._stylesheetSources.clear();
|
||||
}
|
||||
async _onStyleSheet(event) {
|
||||
const header = event.header;
|
||||
// Ignore anonymous scripts
|
||||
if (!header.sourceURL) return;
|
||||
// This might fail if the page has already navigated away.
|
||||
const response = await this._client._sendMayFail('CSS.getStyleSheetText', {
|
||||
styleSheetId: header.styleSheetId
|
||||
});
|
||||
if (response) {
|
||||
this._stylesheetURLs.set(header.styleSheetId, header.sourceURL);
|
||||
this._stylesheetSources.set(header.styleSheetId, response.text);
|
||||
}
|
||||
}
|
||||
async stop() {
|
||||
(0, _utils.assert)(this._enabled, 'CSSCoverage is not enabled');
|
||||
this._enabled = false;
|
||||
const ruleTrackingResponse = await this._client.send('CSS.stopRuleUsageTracking');
|
||||
await Promise.all([this._client.send('CSS.disable'), this._client.send('DOM.disable')]);
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
|
||||
// aggregate by styleSheetId
|
||||
const styleSheetIdToCoverage = new Map();
|
||||
for (const entry of ruleTrackingResponse.ruleUsage) {
|
||||
let ranges = styleSheetIdToCoverage.get(entry.styleSheetId);
|
||||
if (!ranges) {
|
||||
ranges = [];
|
||||
styleSheetIdToCoverage.set(entry.styleSheetId, ranges);
|
||||
}
|
||||
ranges.push({
|
||||
startOffset: entry.startOffset,
|
||||
endOffset: entry.endOffset,
|
||||
count: entry.used ? 1 : 0
|
||||
});
|
||||
}
|
||||
const coverage = {
|
||||
entries: []
|
||||
};
|
||||
for (const styleSheetId of this._stylesheetURLs.keys()) {
|
||||
const url = this._stylesheetURLs.get(styleSheetId);
|
||||
const text = this._stylesheetSources.get(styleSheetId);
|
||||
const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []);
|
||||
coverage.entries.push({
|
||||
url,
|
||||
ranges,
|
||||
text
|
||||
});
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
}
|
||||
function convertToDisjointRanges(nestedRanges) {
|
||||
const points = [];
|
||||
for (const range of nestedRanges) {
|
||||
points.push({
|
||||
offset: range.startOffset,
|
||||
type: 0,
|
||||
range
|
||||
});
|
||||
points.push({
|
||||
offset: range.endOffset,
|
||||
type: 1,
|
||||
range
|
||||
});
|
||||
}
|
||||
// Sort points to form a valid parenthesis sequence.
|
||||
points.sort((a, b) => {
|
||||
// Sort with increasing offsets.
|
||||
if (a.offset !== b.offset) return a.offset - b.offset;
|
||||
// All "end" points should go before "start" points.
|
||||
if (a.type !== b.type) return b.type - a.type;
|
||||
const aLength = a.range.endOffset - a.range.startOffset;
|
||||
const bLength = b.range.endOffset - b.range.startOffset;
|
||||
// For two "start" points, the one with longer range goes first.
|
||||
if (a.type === 0) return bLength - aLength;
|
||||
// For two "end" points, the one with shorter range goes first.
|
||||
return aLength - bLength;
|
||||
});
|
||||
const hitCountStack = [];
|
||||
const results = [];
|
||||
let lastOffset = 0;
|
||||
// Run scanning line to intersect all ranges.
|
||||
for (const point of points) {
|
||||
if (hitCountStack.length && lastOffset < point.offset && hitCountStack[hitCountStack.length - 1] > 0) {
|
||||
const lastResult = results.length ? results[results.length - 1] : null;
|
||||
if (lastResult && lastResult.end === lastOffset) lastResult.end = point.offset;else results.push({
|
||||
start: lastOffset,
|
||||
end: point.offset
|
||||
});
|
||||
}
|
||||
lastOffset = point.offset;
|
||||
if (point.type === 0) hitCountStack.push(point.range.count);else hitCountStack.pop();
|
||||
}
|
||||
// Filter out empty ranges.
|
||||
return results.filter(range => range.end - range.start > 1);
|
||||
}
|
||||
104
node_modules/playwright-core/lib/server/chromium/crDevTools.js
generated
vendored
Normal file
104
node_modules/playwright-core/lib/server/chromium/crDevTools.js
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRDevTools = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const kBindingName = '__pw_devtools__';
|
||||
|
||||
// This class intercepts preferences-related DevTools embedder methods
|
||||
// and stores preferences as a json file in the browser installation directory.
|
||||
class CRDevTools {
|
||||
constructor(preferencesPath) {
|
||||
this._preferencesPath = void 0;
|
||||
this._prefs = void 0;
|
||||
this._savePromise = void 0;
|
||||
this.__testHookOnBinding = void 0;
|
||||
this._preferencesPath = preferencesPath;
|
||||
this._savePromise = Promise.resolve();
|
||||
}
|
||||
install(session) {
|
||||
session.on('Runtime.bindingCalled', async event => {
|
||||
if (event.name !== kBindingName) return;
|
||||
const parsed = JSON.parse(event.payload);
|
||||
let result = undefined;
|
||||
if (this.__testHookOnBinding) this.__testHookOnBinding(parsed);
|
||||
if (parsed.method === 'getPreferences') {
|
||||
if (this._prefs === undefined) {
|
||||
try {
|
||||
const json = await _fs.default.promises.readFile(this._preferencesPath, 'utf8');
|
||||
this._prefs = JSON.parse(json);
|
||||
} catch (e) {
|
||||
this._prefs = {};
|
||||
}
|
||||
}
|
||||
result = this._prefs;
|
||||
} else if (parsed.method === 'setPreference') {
|
||||
this._prefs[parsed.params[0]] = parsed.params[1];
|
||||
this._save();
|
||||
} else if (parsed.method === 'removePreference') {
|
||||
delete this._prefs[parsed.params[0]];
|
||||
this._save();
|
||||
} else if (parsed.method === 'clearPreferences') {
|
||||
this._prefs = {};
|
||||
this._save();
|
||||
}
|
||||
session.send('Runtime.evaluate', {
|
||||
expression: `window.DevToolsAPI.embedderMessageAck(${parsed.id}, ${JSON.stringify(result)})`,
|
||||
contextId: event.executionContextId
|
||||
}).catch(e => null);
|
||||
});
|
||||
Promise.all([session.send('Runtime.enable'), session.send('Runtime.addBinding', {
|
||||
name: kBindingName
|
||||
}), session.send('Page.enable'), session.send('Page.addScriptToEvaluateOnNewDocument', {
|
||||
source: `
|
||||
(() => {
|
||||
const init = () => {
|
||||
// Lazy init happens when InspectorFrontendHost is initialized.
|
||||
// At this point DevToolsHost is ready to be used.
|
||||
const host = window.DevToolsHost;
|
||||
const old = host.sendMessageToEmbedder.bind(host);
|
||||
host.sendMessageToEmbedder = message => {
|
||||
if (['getPreferences', 'setPreference', 'removePreference', 'clearPreferences'].includes(JSON.parse(message).method))
|
||||
window.${kBindingName}(message);
|
||||
else
|
||||
old(message);
|
||||
};
|
||||
};
|
||||
let value;
|
||||
Object.defineProperty(window, 'InspectorFrontendHost', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() { return value; },
|
||||
set(v) { value = v; init(); },
|
||||
});
|
||||
})()
|
||||
`
|
||||
}), session.send('Runtime.runIfWaitingForDebugger')]).catch(e => null);
|
||||
}
|
||||
_save() {
|
||||
// Serialize saves to avoid corruption.
|
||||
this._savePromise = this._savePromise.then(async () => {
|
||||
await _fs.default.promises.writeFile(this._preferencesPath, JSON.stringify(this._prefs)).catch(e => null);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.CRDevTools = CRDevTools;
|
||||
143
node_modules/playwright-core/lib/server/chromium/crDragDrop.js
generated
vendored
Normal file
143
node_modules/playwright-core/lib/server/chromium/crDragDrop.js
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.DragManager = void 0;
|
||||
var _utils = require("../../utils");
|
||||
var _crProtocolHelper = require("./crProtocolHelper");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DragManager {
|
||||
constructor(page) {
|
||||
this._crPage = void 0;
|
||||
this._dragState = null;
|
||||
this._lastPosition = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
this._crPage = page;
|
||||
}
|
||||
async cancelDrag() {
|
||||
if (!this._dragState) return false;
|
||||
await this._crPage._mainFrameSession._client.send('Input.dispatchDragEvent', {
|
||||
type: 'dragCancel',
|
||||
x: this._lastPosition.x,
|
||||
y: this._lastPosition.y,
|
||||
data: {
|
||||
items: [],
|
||||
dragOperationsMask: 0xFFFF
|
||||
}
|
||||
});
|
||||
this._dragState = null;
|
||||
return true;
|
||||
}
|
||||
async interceptDragCausedByMove(x, y, button, buttons, modifiers, moveCallback) {
|
||||
this._lastPosition = {
|
||||
x,
|
||||
y
|
||||
};
|
||||
if (this._dragState) {
|
||||
await this._crPage._mainFrameSession._client.send('Input.dispatchDragEvent', {
|
||||
type: 'dragOver',
|
||||
x,
|
||||
y,
|
||||
data: this._dragState,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers)
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (button !== 'left') return moveCallback();
|
||||
const client = this._crPage._mainFrameSession._client;
|
||||
let onDragIntercepted;
|
||||
const dragInterceptedPromise = new Promise(x => onDragIntercepted = x);
|
||||
function setupDragListeners() {
|
||||
let didStartDrag = Promise.resolve(false);
|
||||
let dragEvent = null;
|
||||
const dragListener = event => dragEvent = event;
|
||||
const mouseListener = () => {
|
||||
didStartDrag = new Promise(callback => {
|
||||
window.addEventListener('dragstart', dragListener, {
|
||||
once: true,
|
||||
capture: true
|
||||
});
|
||||
setTimeout(() => callback(dragEvent ? !dragEvent.defaultPrevented : false), 0);
|
||||
});
|
||||
};
|
||||
window.addEventListener('mousemove', mouseListener, {
|
||||
once: true,
|
||||
capture: true
|
||||
});
|
||||
window.__cleanupDrag = async () => {
|
||||
const val = await didStartDrag;
|
||||
window.removeEventListener('mousemove', mouseListener, {
|
||||
capture: true
|
||||
});
|
||||
window.removeEventListener('dragstart', dragListener, {
|
||||
capture: true
|
||||
});
|
||||
delete window.__cleanupDrag;
|
||||
return val;
|
||||
};
|
||||
}
|
||||
await this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, 'utility');
|
||||
client.on('Input.dragIntercepted', onDragIntercepted);
|
||||
try {
|
||||
await client.send('Input.setInterceptDrags', {
|
||||
enabled: true
|
||||
});
|
||||
} catch {
|
||||
// If Input.setInterceptDrags is not supported, just do a regular move.
|
||||
// This can be removed once we stop supporting old Electron.
|
||||
client.off('Input.dragIntercepted', onDragIntercepted);
|
||||
return moveCallback();
|
||||
}
|
||||
await moveCallback();
|
||||
const expectingDrag = (await Promise.all(this._crPage._page.frames().map(async frame => {
|
||||
return frame.nonStallingEvaluateInExistingContext('window.__cleanupDrag && window.__cleanupDrag()', 'utility').catch(() => false);
|
||||
}))).some(x => x);
|
||||
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
|
||||
client.off('Input.dragIntercepted', onDragIntercepted);
|
||||
await client.send('Input.setInterceptDrags', {
|
||||
enabled: false
|
||||
});
|
||||
if (this._dragState) {
|
||||
await this._crPage._mainFrameSession._client.send('Input.dispatchDragEvent', {
|
||||
type: 'dragEnter',
|
||||
x,
|
||||
y,
|
||||
data: this._dragState,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers)
|
||||
});
|
||||
}
|
||||
}
|
||||
isDragging() {
|
||||
return !!this._dragState;
|
||||
}
|
||||
async drop(x, y, modifiers) {
|
||||
(0, _utils.assert)(this._dragState, 'missing drag state');
|
||||
await this._crPage._mainFrameSession._client.send('Input.dispatchDragEvent', {
|
||||
type: 'drop',
|
||||
x,
|
||||
y,
|
||||
data: this._dragState,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers)
|
||||
});
|
||||
this._dragState = null;
|
||||
}
|
||||
}
|
||||
exports.DragManager = DragManager;
|
||||
136
node_modules/playwright-core/lib/server/chromium/crExecutionContext.js
generated
vendored
Normal file
136
node_modules/playwright-core/lib/server/chromium/crExecutionContext.js
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRExecutionContext = void 0;
|
||||
var _crProtocolHelper = require("./crProtocolHelper");
|
||||
var js = _interopRequireWildcard(require("../javascript"));
|
||||
var _stackTrace = require("../../utils/stackTrace");
|
||||
var _utilityScriptSerializers = require("../isomorphic/utilityScriptSerializers");
|
||||
var _protocolError = require("../protocolError");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CRExecutionContext {
|
||||
constructor(client, contextPayload) {
|
||||
this._client = void 0;
|
||||
this._contextId = void 0;
|
||||
this._client = client;
|
||||
this._contextId = contextPayload.id;
|
||||
}
|
||||
async rawEvaluateJSON(expression) {
|
||||
const {
|
||||
exceptionDetails,
|
||||
result: remoteObject
|
||||
} = await this._client.send('Runtime.evaluate', {
|
||||
expression,
|
||||
contextId: this._contextId,
|
||||
returnByValue: true
|
||||
}).catch(rewriteError);
|
||||
if (exceptionDetails) throw new js.JavaScriptErrorInEvaluate((0, _crProtocolHelper.getExceptionMessage)(exceptionDetails));
|
||||
return remoteObject.value;
|
||||
}
|
||||
async rawEvaluateHandle(expression) {
|
||||
const {
|
||||
exceptionDetails,
|
||||
result: remoteObject
|
||||
} = await this._client.send('Runtime.evaluate', {
|
||||
expression,
|
||||
contextId: this._contextId
|
||||
}).catch(rewriteError);
|
||||
if (exceptionDetails) throw new js.JavaScriptErrorInEvaluate((0, _crProtocolHelper.getExceptionMessage)(exceptionDetails));
|
||||
return remoteObject.objectId;
|
||||
}
|
||||
async evaluateWithArguments(expression, returnByValue, utilityScript, values, objectIds) {
|
||||
const {
|
||||
exceptionDetails,
|
||||
result: remoteObject
|
||||
} = await this._client.send('Runtime.callFunctionOn', {
|
||||
functionDeclaration: expression,
|
||||
objectId: utilityScript._objectId,
|
||||
arguments: [{
|
||||
objectId: utilityScript._objectId
|
||||
}, ...values.map(value => ({
|
||||
value
|
||||
})), ...objectIds.map(objectId => ({
|
||||
objectId
|
||||
}))],
|
||||
returnByValue,
|
||||
awaitPromise: true,
|
||||
userGesture: true
|
||||
}).catch(rewriteError);
|
||||
if (exceptionDetails) throw new js.JavaScriptErrorInEvaluate((0, _crProtocolHelper.getExceptionMessage)(exceptionDetails));
|
||||
return returnByValue ? (0, _utilityScriptSerializers.parseEvaluationResultValue)(remoteObject.value) : utilityScript._context.createHandle(remoteObject);
|
||||
}
|
||||
async getProperties(context, objectId) {
|
||||
const response = await this._client.send('Runtime.getProperties', {
|
||||
objectId,
|
||||
ownProperties: true
|
||||
});
|
||||
const result = new Map();
|
||||
for (const property of response.result) {
|
||||
if (!property.enumerable || !property.value) continue;
|
||||
result.set(property.name, context.createHandle(property.value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
createHandle(context, remoteObject) {
|
||||
return new js.JSHandle(context, remoteObject.subtype || remoteObject.type, renderPreview(remoteObject), remoteObject.objectId, potentiallyUnserializableValue(remoteObject));
|
||||
}
|
||||
async releaseHandle(objectId) {
|
||||
await (0, _crProtocolHelper.releaseObject)(this._client, objectId);
|
||||
}
|
||||
}
|
||||
exports.CRExecutionContext = CRExecutionContext;
|
||||
function rewriteError(error) {
|
||||
if (error.message.includes('Object reference chain is too long')) return {
|
||||
result: {
|
||||
type: 'undefined'
|
||||
}
|
||||
};
|
||||
if (error.message.includes('Object couldn\'t be returned by value')) return {
|
||||
result: {
|
||||
type: 'undefined'
|
||||
}
|
||||
};
|
||||
if (error instanceof TypeError && error.message.startsWith('Converting circular structure to JSON')) (0, _stackTrace.rewriteErrorMessage)(error, error.message + ' Are you passing a nested JSHandle?');
|
||||
if (!js.isJavaScriptErrorInEvaluate(error) && !(0, _protocolError.isSessionClosedError)(error)) throw new Error('Execution context was destroyed, most likely because of a navigation.');
|
||||
throw error;
|
||||
}
|
||||
function potentiallyUnserializableValue(remoteObject) {
|
||||
const value = remoteObject.value;
|
||||
const unserializableValue = remoteObject.unserializableValue;
|
||||
return unserializableValue ? js.parseUnserializableValue(unserializableValue) : value;
|
||||
}
|
||||
function renderPreview(object) {
|
||||
if (object.type === 'undefined') return 'undefined';
|
||||
if ('value' in object) return String(object.value);
|
||||
if (object.unserializableValue) return String(object.unserializableValue);
|
||||
if (object.description === 'Object' && object.preview) {
|
||||
const tokens = [];
|
||||
for (const {
|
||||
name,
|
||||
value
|
||||
} of object.preview.properties) tokens.push(`${name}: ${value}`);
|
||||
return `{${tokens.join(', ')}}`;
|
||||
}
|
||||
if (object.subtype === 'array' && object.preview) return js.sparseArrayToString(object.preview.properties);
|
||||
return object.description;
|
||||
}
|
||||
171
node_modules/playwright-core/lib/server/chromium/crInput.js
generated
vendored
Normal file
171
node_modules/playwright-core/lib/server/chromium/crInput.js
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.RawTouchscreenImpl = exports.RawMouseImpl = exports.RawKeyboardImpl = void 0;
|
||||
var input = _interopRequireWildcard(require("../input"));
|
||||
var _macEditingCommands = require("../macEditingCommands");
|
||||
var _utils = require("../../utils");
|
||||
var _crProtocolHelper = require("./crProtocolHelper");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class RawKeyboardImpl {
|
||||
constructor(_client, _isMac, _dragManger) {
|
||||
this._client = _client;
|
||||
this._isMac = _isMac;
|
||||
this._dragManger = _dragManger;
|
||||
}
|
||||
_commandsForCode(code, modifiers) {
|
||||
if (!this._isMac) return [];
|
||||
const parts = [];
|
||||
for (const modifier of ['Shift', 'Control', 'Alt', 'Meta']) {
|
||||
if (modifiers.has(modifier)) parts.push(modifier);
|
||||
}
|
||||
parts.push(code);
|
||||
const shortcut = parts.join('+');
|
||||
let commands = _macEditingCommands.macEditingCommands[shortcut] || [];
|
||||
if ((0, _utils.isString)(commands)) commands = [commands];
|
||||
// Commands that insert text are not supported
|
||||
commands = commands.filter(x => !x.startsWith('insert'));
|
||||
// remove the trailing : to match the Chromium command names.
|
||||
return commands.map(c => c.substring(0, c.length - 1));
|
||||
}
|
||||
async keydown(modifiers, code, keyCode, keyCodeWithoutLocation, key, location, autoRepeat, text) {
|
||||
if (code === 'Escape' && (await this._dragManger.cancelDrag())) return;
|
||||
const commands = this._commandsForCode(code, modifiers);
|
||||
await this._client.send('Input.dispatchKeyEvent', {
|
||||
type: text ? 'keyDown' : 'rawKeyDown',
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
windowsVirtualKeyCode: keyCodeWithoutLocation,
|
||||
code,
|
||||
commands,
|
||||
key,
|
||||
text,
|
||||
unmodifiedText: text,
|
||||
autoRepeat,
|
||||
location,
|
||||
isKeypad: location === input.keypadLocation
|
||||
});
|
||||
}
|
||||
async keyup(modifiers, code, keyCode, keyCodeWithoutLocation, key, location) {
|
||||
await this._client.send('Input.dispatchKeyEvent', {
|
||||
type: 'keyUp',
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
key,
|
||||
windowsVirtualKeyCode: keyCodeWithoutLocation,
|
||||
code,
|
||||
location
|
||||
});
|
||||
}
|
||||
async sendText(text) {
|
||||
await this._client.send('Input.insertText', {
|
||||
text
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RawKeyboardImpl = RawKeyboardImpl;
|
||||
class RawMouseImpl {
|
||||
constructor(page, client, dragManager) {
|
||||
this._client = void 0;
|
||||
this._page = void 0;
|
||||
this._dragManager = void 0;
|
||||
this._page = page;
|
||||
this._client = client;
|
||||
this._dragManager = dragManager;
|
||||
}
|
||||
async move(x, y, button, buttons, modifiers, forClick) {
|
||||
const actualMove = async () => {
|
||||
await this._client.send('Input.dispatchMouseEvent', {
|
||||
type: 'mouseMoved',
|
||||
button,
|
||||
buttons: (0, _crProtocolHelper.toButtonsMask)(buttons),
|
||||
x,
|
||||
y,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers)
|
||||
});
|
||||
};
|
||||
if (forClick) {
|
||||
// Avoid extra protocol calls related to drag and drop, because click relies on
|
||||
// move-down-up protocol commands being sent synchronously.
|
||||
return actualMove();
|
||||
}
|
||||
await this._dragManager.interceptDragCausedByMove(x, y, button, buttons, modifiers, actualMove);
|
||||
}
|
||||
async down(x, y, button, buttons, modifiers, clickCount) {
|
||||
if (this._dragManager.isDragging()) return;
|
||||
await this._client.send('Input.dispatchMouseEvent', {
|
||||
type: 'mousePressed',
|
||||
button,
|
||||
buttons: (0, _crProtocolHelper.toButtonsMask)(buttons),
|
||||
x,
|
||||
y,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
clickCount
|
||||
});
|
||||
}
|
||||
async up(x, y, button, buttons, modifiers, clickCount) {
|
||||
if (this._dragManager.isDragging()) {
|
||||
await this._dragManager.drop(x, y, modifiers);
|
||||
return;
|
||||
}
|
||||
await this._client.send('Input.dispatchMouseEvent', {
|
||||
type: 'mouseReleased',
|
||||
button,
|
||||
buttons: (0, _crProtocolHelper.toButtonsMask)(buttons),
|
||||
x,
|
||||
y,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
clickCount
|
||||
});
|
||||
}
|
||||
async wheel(x, y, buttons, modifiers, deltaX, deltaY) {
|
||||
await this._client.send('Input.dispatchMouseEvent', {
|
||||
type: 'mouseWheel',
|
||||
x,
|
||||
y,
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
deltaX,
|
||||
deltaY
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RawMouseImpl = RawMouseImpl;
|
||||
class RawTouchscreenImpl {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._client = client;
|
||||
}
|
||||
async tap(x, y, modifiers) {
|
||||
await Promise.all([this._client.send('Input.dispatchTouchEvent', {
|
||||
type: 'touchStart',
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
touchPoints: [{
|
||||
x,
|
||||
y
|
||||
}]
|
||||
}), this._client.send('Input.dispatchTouchEvent', {
|
||||
type: 'touchEnd',
|
||||
modifiers: (0, _crProtocolHelper.toModifiersMask)(modifiers),
|
||||
touchPoints: []
|
||||
})]);
|
||||
}
|
||||
}
|
||||
exports.RawTouchscreenImpl = RawTouchscreenImpl;
|
||||
767
node_modules/playwright-core/lib/server/chromium/crNetworkManager.js
generated
vendored
Normal file
767
node_modules/playwright-core/lib/server/chromium/crNetworkManager.js
generated
vendored
Normal file
@@ -0,0 +1,767 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRNetworkManager = void 0;
|
||||
var _helper = require("../helper");
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
var _utils = require("../../utils");
|
||||
var _protocolError = require("../protocolError");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CRNetworkManager {
|
||||
constructor(page, serviceWorker) {
|
||||
this._page = void 0;
|
||||
this._serviceWorker = void 0;
|
||||
this._requestIdToRequest = new Map();
|
||||
this._requestIdToRequestWillBeSentEvent = new Map();
|
||||
this._credentials = null;
|
||||
this._attemptedAuthentications = new Set();
|
||||
this._userRequestInterceptionEnabled = false;
|
||||
this._protocolRequestInterceptionEnabled = false;
|
||||
this._offline = false;
|
||||
this._extraHTTPHeaders = [];
|
||||
this._requestIdToRequestPausedEvent = new Map();
|
||||
this._responseExtraInfoTracker = new ResponseExtraInfoTracker();
|
||||
this._sessions = new Map();
|
||||
this._page = page;
|
||||
this._serviceWorker = serviceWorker;
|
||||
}
|
||||
async addSession(session, workerFrame, isMain) {
|
||||
const sessionInfo = {
|
||||
session,
|
||||
isMain,
|
||||
workerFrame,
|
||||
eventListeners: []
|
||||
};
|
||||
sessionInfo.eventListeners = [_eventsHelper.eventsHelper.addEventListener(session, 'Fetch.requestPaused', this._onRequestPaused.bind(this, sessionInfo)), _eventsHelper.eventsHelper.addEventListener(session, 'Fetch.authRequired', this._onAuthRequired.bind(this, sessionInfo)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this, sessionInfo)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.requestWillBeSentExtraInfo', this._onRequestWillBeSentExtraInfo.bind(this)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.requestServedFromCache', this._onRequestServedFromCache.bind(this)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this, sessionInfo)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.responseReceivedExtraInfo', this._onResponseReceivedExtraInfo.bind(this)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.loadingFinished', this._onLoadingFinished.bind(this, sessionInfo)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.loadingFailed', this._onLoadingFailed.bind(this, sessionInfo))];
|
||||
if (this._page) {
|
||||
sessionInfo.eventListeners.push(...[_eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketCreated', e => this._page._frameManager.onWebSocketCreated(e.requestId, e.url)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page._frameManager.onWebSocketRequest(e.requestId)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketClosed', e => this._page._frameManager.webSocketClosed(e.requestId)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.webSocketFrameError', e => this._page._frameManager.webSocketError(e.requestId, e.errorMessage))]);
|
||||
}
|
||||
this._sessions.set(session, sessionInfo);
|
||||
await Promise.all([session.send('Network.enable'), this._updateProtocolRequestInterceptionForSession(sessionInfo, true /* initial */), this._setOfflineForSession(sessionInfo, true /* initial */), this._setExtraHTTPHeadersForSession(sessionInfo, true /* initial */)]);
|
||||
}
|
||||
removeSession(session) {
|
||||
const info = this._sessions.get(session);
|
||||
if (info) _eventsHelper.eventsHelper.removeEventListeners(info.eventListeners);
|
||||
this._sessions.delete(session);
|
||||
}
|
||||
async _forEachSession(cb) {
|
||||
await Promise.all([...this._sessions.values()].map(info => {
|
||||
if (info.isMain) return cb(info);
|
||||
return cb(info).catch(e => {
|
||||
// Broadcasting a message to the closed target should be a noop.
|
||||
if ((0, _protocolError.isSessionClosedError)(e)) return;
|
||||
throw e;
|
||||
});
|
||||
}));
|
||||
}
|
||||
async authenticate(credentials) {
|
||||
this._credentials = credentials;
|
||||
await this._updateProtocolRequestInterception();
|
||||
}
|
||||
async setOffline(offline) {
|
||||
if (offline === this._offline) return;
|
||||
this._offline = offline;
|
||||
await this._forEachSession(info => this._setOfflineForSession(info));
|
||||
}
|
||||
async _setOfflineForSession(info, initial) {
|
||||
if (initial && !this._offline) return;
|
||||
// Workers are affected by the owner frame's Network.emulateNetworkConditions.
|
||||
if (info.workerFrame) return;
|
||||
await info.session.send('Network.emulateNetworkConditions', {
|
||||
offline: this._offline,
|
||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||
latency: 0,
|
||||
downloadThroughput: -1,
|
||||
uploadThroughput: -1
|
||||
});
|
||||
}
|
||||
async setRequestInterception(value) {
|
||||
this._userRequestInterceptionEnabled = value;
|
||||
await this._updateProtocolRequestInterception();
|
||||
}
|
||||
async _updateProtocolRequestInterception() {
|
||||
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
|
||||
if (enabled === this._protocolRequestInterceptionEnabled) return;
|
||||
this._protocolRequestInterceptionEnabled = enabled;
|
||||
await this._forEachSession(info => this._updateProtocolRequestInterceptionForSession(info));
|
||||
}
|
||||
async _updateProtocolRequestInterceptionForSession(info, initial) {
|
||||
const enabled = this._protocolRequestInterceptionEnabled;
|
||||
if (initial && !enabled) return;
|
||||
const cachePromise = info.session.send('Network.setCacheDisabled', {
|
||||
cacheDisabled: enabled
|
||||
});
|
||||
let fetchPromise = Promise.resolve(undefined);
|
||||
if (!info.workerFrame) {
|
||||
if (enabled) fetchPromise = info.session.send('Fetch.enable', {
|
||||
handleAuthRequests: true,
|
||||
patterns: [{
|
||||
urlPattern: '*',
|
||||
requestStage: 'Request'
|
||||
}]
|
||||
});else fetchPromise = info.session.send('Fetch.disable');
|
||||
}
|
||||
await Promise.all([cachePromise, fetchPromise]);
|
||||
}
|
||||
async setExtraHTTPHeaders(extraHTTPHeaders) {
|
||||
if (!this._extraHTTPHeaders.length && !extraHTTPHeaders.length) return;
|
||||
this._extraHTTPHeaders = extraHTTPHeaders;
|
||||
await this._forEachSession(info => this._setExtraHTTPHeadersForSession(info));
|
||||
}
|
||||
async _setExtraHTTPHeadersForSession(info, initial) {
|
||||
if (initial && !this._extraHTTPHeaders.length) return;
|
||||
await info.session.send('Network.setExtraHTTPHeaders', {
|
||||
headers: (0, _utils.headersArrayToObject)(this._extraHTTPHeaders, false /* lowerCase */)
|
||||
});
|
||||
}
|
||||
async clearCache() {
|
||||
await this._forEachSession(async info => {
|
||||
// Sending 'Network.setCacheDisabled' with 'cacheDisabled = true' will clear the MemoryCache.
|
||||
await info.session.send('Network.setCacheDisabled', {
|
||||
cacheDisabled: true
|
||||
});
|
||||
if (!this._protocolRequestInterceptionEnabled) await info.session.send('Network.setCacheDisabled', {
|
||||
cacheDisabled: false
|
||||
});
|
||||
if (!info.workerFrame) await info.session.send('Network.clearBrowserCache');
|
||||
});
|
||||
}
|
||||
_onRequestWillBeSent(sessionInfo, event) {
|
||||
// Request interception doesn't happen for data URLs with Network Service.
|
||||
if (this._protocolRequestInterceptionEnabled && !event.request.url.startsWith('data:')) {
|
||||
const requestId = event.requestId;
|
||||
const requestPausedEvent = this._requestIdToRequestPausedEvent.get(requestId);
|
||||
if (requestPausedEvent) {
|
||||
this._onRequest(sessionInfo, event, requestPausedEvent.sessionInfo, requestPausedEvent.event);
|
||||
this._requestIdToRequestPausedEvent.delete(requestId);
|
||||
} else {
|
||||
this._requestIdToRequestWillBeSentEvent.set(event.requestId, {
|
||||
sessionInfo,
|
||||
event
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this._onRequest(sessionInfo, event, undefined, undefined);
|
||||
}
|
||||
}
|
||||
_onRequestServedFromCache(event) {
|
||||
this._responseExtraInfoTracker.requestServedFromCache(event);
|
||||
}
|
||||
_onRequestWillBeSentExtraInfo(event) {
|
||||
this._responseExtraInfoTracker.requestWillBeSentExtraInfo(event);
|
||||
}
|
||||
_onAuthRequired(sessionInfo, event) {
|
||||
let response = 'Default';
|
||||
const shouldProvideCredentials = this._shouldProvideCredentials(event.request.url);
|
||||
if (this._attemptedAuthentications.has(event.requestId)) {
|
||||
response = 'CancelAuth';
|
||||
} else if (shouldProvideCredentials) {
|
||||
response = 'ProvideCredentials';
|
||||
this._attemptedAuthentications.add(event.requestId);
|
||||
}
|
||||
const {
|
||||
username,
|
||||
password
|
||||
} = shouldProvideCredentials && this._credentials ? this._credentials : {
|
||||
username: undefined,
|
||||
password: undefined
|
||||
};
|
||||
sessionInfo.session._sendMayFail('Fetch.continueWithAuth', {
|
||||
requestId: event.requestId,
|
||||
authChallengeResponse: {
|
||||
response,
|
||||
username,
|
||||
password
|
||||
}
|
||||
});
|
||||
}
|
||||
_shouldProvideCredentials(url) {
|
||||
if (!this._credentials) return false;
|
||||
return !this._credentials.origin || new URL(url).origin.toLowerCase() === this._credentials.origin.toLowerCase();
|
||||
}
|
||||
_onRequestPaused(sessionInfo, event) {
|
||||
if (!event.networkId) {
|
||||
// Fetch without networkId means that request was not recognized by inspector, and
|
||||
// it will never receive Network.requestWillBeSent. Continue the request to not affect it.
|
||||
sessionInfo.session._sendMayFail('Fetch.continueRequest', {
|
||||
requestId: event.requestId
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (event.request.url.startsWith('data:')) return;
|
||||
const requestId = event.networkId;
|
||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
||||
if (requestWillBeSentEvent) {
|
||||
this._onRequest(requestWillBeSentEvent.sessionInfo, requestWillBeSentEvent.event, sessionInfo, event);
|
||||
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
||||
} else {
|
||||
var _existingRequest$_rou;
|
||||
const existingRequest = this._requestIdToRequest.get(requestId);
|
||||
const alreadyContinuedParams = existingRequest === null || existingRequest === void 0 || (_existingRequest$_rou = existingRequest._route) === null || _existingRequest$_rou === void 0 ? void 0 : _existingRequest$_rou._alreadyContinuedParams;
|
||||
if (alreadyContinuedParams && !event.redirectedRequestId) {
|
||||
// Sometimes Chromium network stack restarts the request internally.
|
||||
// For example, when no-cors request hits a "less public address space", it should be resent with cors.
|
||||
// There are some more examples here: https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader.cc;l=1205-1234;drc=d5dd931e0ad3d9ffe74888ec62a3cc106efd7ea6
|
||||
// There are probably even more cases deep inside the network stack.
|
||||
//
|
||||
// Anyway, in this case, continue the request in the same way as before, and it should go through.
|
||||
//
|
||||
// Note: make sure not to prematurely continue the redirect, which shares the
|
||||
// `networkId` between the original request and the redirect.
|
||||
sessionInfo.session._sendMayFail('Fetch.continueRequest', {
|
||||
...alreadyContinuedParams,
|
||||
requestId: event.requestId
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._requestIdToRequestPausedEvent.set(requestId, {
|
||||
sessionInfo,
|
||||
event
|
||||
});
|
||||
}
|
||||
}
|
||||
_onRequest(requestWillBeSentSessionInfo, requestWillBeSentEvent, requestPausedSessionInfo, requestPausedEvent) {
|
||||
var _this$_page, _this$_page2, _this$_page3;
|
||||
if (requestWillBeSentEvent.request.url.startsWith('data:')) return;
|
||||
let redirectedFrom = null;
|
||||
if (requestWillBeSentEvent.redirectResponse) {
|
||||
const request = this._requestIdToRequest.get(requestWillBeSentEvent.requestId);
|
||||
// If we connect late to the target, we could have missed the requestWillBeSent event.
|
||||
if (request) {
|
||||
this._handleRequestRedirect(request, requestWillBeSentEvent.redirectResponse, requestWillBeSentEvent.timestamp, requestWillBeSentEvent.redirectHasExtraInfo);
|
||||
redirectedFrom = request;
|
||||
}
|
||||
}
|
||||
let frame = requestWillBeSentEvent.frameId ? (_this$_page = this._page) === null || _this$_page === void 0 ? void 0 : _this$_page._frameManager.frame(requestWillBeSentEvent.frameId) : requestWillBeSentSessionInfo.workerFrame;
|
||||
// Requests from workers lack frameId, because we receive Network.requestWillBeSent
|
||||
// on the worker target. However, we receive Fetch.requestPaused on the page target,
|
||||
// and lack workerFrame there. Luckily, Fetch.requestPaused provides a frameId.
|
||||
if (!frame && this._page && requestPausedEvent && requestPausedEvent.frameId) frame = this._page._frameManager.frame(requestPausedEvent.frameId);
|
||||
|
||||
// Check if it's main resource request interception (targetId === main frame id).
|
||||
if (!frame && this._page && requestWillBeSentEvent.frameId === ((_this$_page2 = this._page) === null || _this$_page2 === void 0 ? void 0 : _this$_page2._delegate)._targetId) {
|
||||
// Main resource request for the page is being intercepted so the Frame is not created
|
||||
// yet. Precreate it here for the purposes of request interception. It will be updated
|
||||
// later as soon as the request continues and we receive frame tree from the page.
|
||||
frame = this._page._frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
|
||||
}
|
||||
|
||||
// CORS options preflight request is generated by the network stack. If interception is enabled,
|
||||
// we accept all CORS options, assuming that this was intended when setting route.
|
||||
//
|
||||
// Note: it would be better to match the URL against interception patterns.
|
||||
const isInterceptedOptionsPreflight = !!requestPausedEvent && requestPausedEvent.request.method === 'OPTIONS' && requestWillBeSentEvent.initiator.type === 'preflight';
|
||||
if (isInterceptedOptionsPreflight && (this._page || this._serviceWorker).needsRequestInterception()) {
|
||||
const requestHeaders = requestPausedEvent.request.headers;
|
||||
const responseHeaders = [{
|
||||
name: 'Access-Control-Allow-Origin',
|
||||
value: requestHeaders['Origin'] || '*'
|
||||
}, {
|
||||
name: 'Access-Control-Allow-Methods',
|
||||
value: requestHeaders['Access-Control-Request-Method'] || 'GET, POST, OPTIONS, DELETE'
|
||||
}, {
|
||||
name: 'Access-Control-Allow-Credentials',
|
||||
value: 'true'
|
||||
}];
|
||||
if (requestHeaders['Access-Control-Request-Headers']) responseHeaders.push({
|
||||
name: 'Access-Control-Allow-Headers',
|
||||
value: requestHeaders['Access-Control-Request-Headers']
|
||||
});
|
||||
requestPausedSessionInfo.session._sendMayFail('Fetch.fulfillRequest', {
|
||||
requestId: requestPausedEvent.requestId,
|
||||
responseCode: 204,
|
||||
responsePhrase: network.statusText(204),
|
||||
responseHeaders,
|
||||
body: ''
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-service-worker requests MUST have a frame—if they don't, we pretend there was no request
|
||||
if (!frame && !this._serviceWorker) {
|
||||
if (requestPausedEvent) requestPausedSessionInfo.session._sendMayFail('Fetch.continueRequest', {
|
||||
requestId: requestPausedEvent.requestId
|
||||
});
|
||||
return;
|
||||
}
|
||||
let route = null;
|
||||
let headersOverride;
|
||||
if (requestPausedEvent) {
|
||||
// We do not support intercepting redirects.
|
||||
if (redirectedFrom || !this._userRequestInterceptionEnabled && this._protocolRequestInterceptionEnabled) {
|
||||
var _redirectedFrom;
|
||||
// Chromium does not preserve header overrides between redirects, so we have to do it ourselves.
|
||||
headersOverride = (_redirectedFrom = redirectedFrom) === null || _redirectedFrom === void 0 || (_redirectedFrom = _redirectedFrom._originalRequestRoute) === null || _redirectedFrom === void 0 || (_redirectedFrom = _redirectedFrom._alreadyContinuedParams) === null || _redirectedFrom === void 0 ? void 0 : _redirectedFrom.headers;
|
||||
requestPausedSessionInfo.session._sendMayFail('Fetch.continueRequest', {
|
||||
requestId: requestPausedEvent.requestId,
|
||||
headers: headersOverride
|
||||
});
|
||||
} else {
|
||||
route = new RouteImpl(requestPausedSessionInfo.session, requestPausedEvent.requestId);
|
||||
}
|
||||
}
|
||||
const isNavigationRequest = requestWillBeSentEvent.requestId === requestWillBeSentEvent.loaderId && requestWillBeSentEvent.type === 'Document';
|
||||
const documentId = isNavigationRequest ? requestWillBeSentEvent.loaderId : undefined;
|
||||
const request = new InterceptableRequest({
|
||||
session: requestWillBeSentSessionInfo.session,
|
||||
context: (this._page || this._serviceWorker)._browserContext,
|
||||
frame: frame || null,
|
||||
serviceWorker: this._serviceWorker || null,
|
||||
documentId,
|
||||
route,
|
||||
requestWillBeSentEvent,
|
||||
requestPausedEvent,
|
||||
redirectedFrom,
|
||||
headersOverride: headersOverride || null
|
||||
});
|
||||
this._requestIdToRequest.set(requestWillBeSentEvent.requestId, request);
|
||||
if (route) {
|
||||
// We may not receive extra info when intercepting the request.
|
||||
// Use the headers from the Fetch.requestPausedPayload and release the allHeaders()
|
||||
// right away, so that client can call it from the route handler.
|
||||
request.request.setRawRequestHeaders((0, _utils.headersObjectToArray)(requestPausedEvent.request.headers, '\n'));
|
||||
}
|
||||
(((_this$_page3 = this._page) === null || _this$_page3 === void 0 ? void 0 : _this$_page3._frameManager) || this._serviceWorker).requestStarted(request.request, route || undefined);
|
||||
}
|
||||
_createResponse(request, responsePayload, hasExtraInfo) {
|
||||
var _responsePayload$secu, _responsePayload$secu2, _responsePayload$secu3, _responsePayload$secu4, _responsePayload$secu5;
|
||||
const getResponseBody = async () => {
|
||||
var _request$_route;
|
||||
const contentLengthHeader = Object.entries(responsePayload.headers).find(header => header[0].toLowerCase() === 'content-length');
|
||||
const expectedLength = contentLengthHeader ? +contentLengthHeader[1] : undefined;
|
||||
const session = request.session;
|
||||
const response = await session.send('Network.getResponseBody', {
|
||||
requestId: request._requestId
|
||||
});
|
||||
if (response.body || !expectedLength) return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||
|
||||
// Make sure no network requests sent while reading the body for fulfilled requests.
|
||||
if ((_request$_route = request._route) !== null && _request$_route !== void 0 && _request$_route._fulfilled) return Buffer.from('');
|
||||
|
||||
// For <link prefetch we are going to receive empty body with non-empty content-length expectation. Reach out for the actual content.
|
||||
const resource = await session.send('Network.loadNetworkResource', {
|
||||
url: request.request.url(),
|
||||
frameId: this._serviceWorker ? undefined : request.request.frame()._id,
|
||||
options: {
|
||||
disableCache: false,
|
||||
includeCredentials: true
|
||||
}
|
||||
});
|
||||
const chunks = [];
|
||||
while (resource.resource.stream) {
|
||||
const chunk = await session.send('IO.read', {
|
||||
handle: resource.resource.stream
|
||||
});
|
||||
chunks.push(Buffer.from(chunk.data, chunk.base64Encoded ? 'base64' : 'utf-8'));
|
||||
if (chunk.eof) {
|
||||
await session.send('IO.close', {
|
||||
handle: resource.resource.stream
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Buffer.concat(chunks);
|
||||
};
|
||||
const timingPayload = responsePayload.timing;
|
||||
let timing;
|
||||
if (timingPayload && !this._responseExtraInfoTracker.servedFromCache(request._requestId)) {
|
||||
timing = {
|
||||
startTime: (timingPayload.requestTime - request._timestamp + request._wallTime) * 1000,
|
||||
domainLookupStart: timingPayload.dnsStart,
|
||||
domainLookupEnd: timingPayload.dnsEnd,
|
||||
connectStart: timingPayload.connectStart,
|
||||
secureConnectionStart: timingPayload.sslStart,
|
||||
connectEnd: timingPayload.connectEnd,
|
||||
requestStart: timingPayload.sendStart,
|
||||
responseStart: timingPayload.receiveHeadersEnd
|
||||
};
|
||||
} else {
|
||||
timing = {
|
||||
startTime: request._wallTime * 1000,
|
||||
domainLookupStart: -1,
|
||||
domainLookupEnd: -1,
|
||||
connectStart: -1,
|
||||
secureConnectionStart: -1,
|
||||
connectEnd: -1,
|
||||
requestStart: -1,
|
||||
responseStart: -1
|
||||
};
|
||||
}
|
||||
const response = new network.Response(request.request, responsePayload.status, responsePayload.statusText, (0, _utils.headersObjectToArray)(responsePayload.headers), timing, getResponseBody, !!responsePayload.fromServiceWorker, responsePayload.protocol);
|
||||
if (responsePayload !== null && responsePayload !== void 0 && responsePayload.remoteIPAddress && typeof (responsePayload === null || responsePayload === void 0 ? void 0 : responsePayload.remotePort) === 'number') {
|
||||
response._serverAddrFinished({
|
||||
ipAddress: responsePayload.remoteIPAddress,
|
||||
port: responsePayload.remotePort
|
||||
});
|
||||
} else {
|
||||
response._serverAddrFinished();
|
||||
}
|
||||
response._securityDetailsFinished({
|
||||
protocol: responsePayload === null || responsePayload === void 0 || (_responsePayload$secu = responsePayload.securityDetails) === null || _responsePayload$secu === void 0 ? void 0 : _responsePayload$secu.protocol,
|
||||
subjectName: responsePayload === null || responsePayload === void 0 || (_responsePayload$secu2 = responsePayload.securityDetails) === null || _responsePayload$secu2 === void 0 ? void 0 : _responsePayload$secu2.subjectName,
|
||||
issuer: responsePayload === null || responsePayload === void 0 || (_responsePayload$secu3 = responsePayload.securityDetails) === null || _responsePayload$secu3 === void 0 ? void 0 : _responsePayload$secu3.issuer,
|
||||
validFrom: responsePayload === null || responsePayload === void 0 || (_responsePayload$secu4 = responsePayload.securityDetails) === null || _responsePayload$secu4 === void 0 ? void 0 : _responsePayload$secu4.validFrom,
|
||||
validTo: responsePayload === null || responsePayload === void 0 || (_responsePayload$secu5 = responsePayload.securityDetails) === null || _responsePayload$secu5 === void 0 ? void 0 : _responsePayload$secu5.validTo
|
||||
});
|
||||
this._responseExtraInfoTracker.processResponse(request._requestId, response, hasExtraInfo);
|
||||
return response;
|
||||
}
|
||||
_deleteRequest(request) {
|
||||
this._requestIdToRequest.delete(request._requestId);
|
||||
if (request._interceptionId) this._attemptedAuthentications.delete(request._interceptionId);
|
||||
}
|
||||
_handleRequestRedirect(request, responsePayload, timestamp, hasExtraInfo) {
|
||||
var _this$_page4, _this$_page5;
|
||||
const response = this._createResponse(request, responsePayload, hasExtraInfo);
|
||||
response.setTransferSize(null);
|
||||
response.setEncodedBodySize(null);
|
||||
response._requestFinished((timestamp - request._timestamp) * 1000);
|
||||
this._deleteRequest(request);
|
||||
(((_this$_page4 = this._page) === null || _this$_page4 === void 0 ? void 0 : _this$_page4._frameManager) || this._serviceWorker).requestReceivedResponse(response);
|
||||
(((_this$_page5 = this._page) === null || _this$_page5 === void 0 ? void 0 : _this$_page5._frameManager) || this._serviceWorker).reportRequestFinished(request.request, response);
|
||||
}
|
||||
_onResponseReceivedExtraInfo(event) {
|
||||
this._responseExtraInfoTracker.responseReceivedExtraInfo(event);
|
||||
}
|
||||
_onResponseReceived(sessionInfo, event) {
|
||||
var _this$_page6;
|
||||
let request = this._requestIdToRequest.get(event.requestId);
|
||||
// For frame-level Requests that are handled by a Service Worker's fetch handler, we'll never get a requestPaused event, so we need to
|
||||
// manually create the request. In an ideal world, crNetworkManager would be able to know this on Network.requestWillBeSent, but there
|
||||
// is not enough metadata there.
|
||||
if (!request && event.response.fromServiceWorker) {
|
||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
|
||||
if (requestWillBeSentEvent) {
|
||||
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
|
||||
this._onRequest(sessionInfo, requestWillBeSentEvent.event, undefined, undefined);
|
||||
request = this._requestIdToRequest.get(event.requestId);
|
||||
}
|
||||
}
|
||||
// FileUpload sends a response without a matching request.
|
||||
if (!request) return;
|
||||
const response = this._createResponse(request, event.response, event.hasExtraInfo);
|
||||
(((_this$_page6 = this._page) === null || _this$_page6 === void 0 ? void 0 : _this$_page6._frameManager) || this._serviceWorker).requestReceivedResponse(response);
|
||||
}
|
||||
_onLoadingFinished(sessionInfo, event) {
|
||||
var _this$_page7;
|
||||
this._responseExtraInfoTracker.loadingFinished(event);
|
||||
const request = this._requestIdToRequest.get(event.requestId);
|
||||
// For certain requestIds we never receive requestWillBeSent event.
|
||||
// @see https://crbug.com/750469
|
||||
if (!request) return;
|
||||
this._maybeUpdateOOPIFMainRequest(sessionInfo, request);
|
||||
|
||||
// Under certain conditions we never get the Network.responseReceived
|
||||
// event from protocol. @see https://crbug.com/883475
|
||||
const response = request.request._existingResponse();
|
||||
if (response) {
|
||||
response.setTransferSize(event.encodedDataLength);
|
||||
response.responseHeadersSize().then(size => response.setEncodedBodySize(event.encodedDataLength - size));
|
||||
response._requestFinished(_helper.helper.secondsToRoundishMillis(event.timestamp - request._timestamp));
|
||||
}
|
||||
this._deleteRequest(request);
|
||||
(((_this$_page7 = this._page) === null || _this$_page7 === void 0 ? void 0 : _this$_page7._frameManager) || this._serviceWorker).reportRequestFinished(request.request, response);
|
||||
}
|
||||
_onLoadingFailed(sessionInfo, event) {
|
||||
var _this$_page8;
|
||||
this._responseExtraInfoTracker.loadingFailed(event);
|
||||
let request = this._requestIdToRequest.get(event.requestId);
|
||||
if (!request) {
|
||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(event.requestId);
|
||||
if (requestWillBeSentEvent) {
|
||||
// This is a case where request has failed before we had a chance to intercept it.
|
||||
// We stop waiting for Fetch.requestPaused (it might never come), and dispatch request event
|
||||
// right away, followed by requestfailed event.
|
||||
this._requestIdToRequestWillBeSentEvent.delete(event.requestId);
|
||||
this._onRequest(sessionInfo, requestWillBeSentEvent.event, undefined, undefined);
|
||||
request = this._requestIdToRequest.get(event.requestId);
|
||||
}
|
||||
}
|
||||
|
||||
// For certain requestIds we never receive requestWillBeSent event.
|
||||
// @see https://crbug.com/750469
|
||||
if (!request) return;
|
||||
this._maybeUpdateOOPIFMainRequest(sessionInfo, request);
|
||||
const response = request.request._existingResponse();
|
||||
if (response) {
|
||||
response.setTransferSize(null);
|
||||
response.setEncodedBodySize(null);
|
||||
response._requestFinished(_helper.helper.secondsToRoundishMillis(event.timestamp - request._timestamp));
|
||||
} else {
|
||||
// Loading failed before response has arrived - there will be no extra info events.
|
||||
request.request.setRawRequestHeaders(null);
|
||||
}
|
||||
this._deleteRequest(request);
|
||||
request.request._setFailureText(event.errorText || event.blockedReason || '');
|
||||
(((_this$_page8 = this._page) === null || _this$_page8 === void 0 ? void 0 : _this$_page8._frameManager) || this._serviceWorker).requestFailed(request.request, !!event.canceled);
|
||||
}
|
||||
_maybeUpdateOOPIFMainRequest(sessionInfo, request) {
|
||||
// OOPIF has a main request that starts in the parent session but finishes in the child session.
|
||||
// We check for the main request by matching loaderId and requestId, and if it now belongs to
|
||||
// a child session, migrate it there.
|
||||
if (request.session !== sessionInfo.session && !sessionInfo.isMain && request._documentId === request._requestId) request.session = sessionInfo.session;
|
||||
}
|
||||
}
|
||||
exports.CRNetworkManager = CRNetworkManager;
|
||||
class InterceptableRequest {
|
||||
constructor(options) {
|
||||
this.request = void 0;
|
||||
this._requestId = void 0;
|
||||
this._interceptionId = void 0;
|
||||
this._documentId = void 0;
|
||||
this._timestamp = void 0;
|
||||
this._wallTime = void 0;
|
||||
this._route = void 0;
|
||||
// Only first request in the chain can be intercepted, so this will
|
||||
// store the first and only Route in the chain (if any).
|
||||
this._originalRequestRoute = void 0;
|
||||
this.session = void 0;
|
||||
const {
|
||||
session,
|
||||
context,
|
||||
frame,
|
||||
documentId,
|
||||
route,
|
||||
requestWillBeSentEvent,
|
||||
requestPausedEvent,
|
||||
redirectedFrom,
|
||||
serviceWorker,
|
||||
headersOverride
|
||||
} = options;
|
||||
this.session = session;
|
||||
this._timestamp = requestWillBeSentEvent.timestamp;
|
||||
this._wallTime = requestWillBeSentEvent.wallTime;
|
||||
this._requestId = requestWillBeSentEvent.requestId;
|
||||
this._interceptionId = requestPausedEvent && requestPausedEvent.requestId;
|
||||
this._documentId = documentId;
|
||||
this._route = route;
|
||||
this._originalRequestRoute = route !== null && route !== void 0 ? route : redirectedFrom === null || redirectedFrom === void 0 ? void 0 : redirectedFrom._originalRequestRoute;
|
||||
const {
|
||||
headers,
|
||||
method,
|
||||
url,
|
||||
postDataEntries = null
|
||||
} = requestPausedEvent ? requestPausedEvent.request : requestWillBeSentEvent.request;
|
||||
const type = (requestWillBeSentEvent.type || '').toLowerCase();
|
||||
let postDataBuffer = null;
|
||||
const entries = postDataEntries === null || postDataEntries === void 0 ? void 0 : postDataEntries.filter(entry => entry.bytes);
|
||||
if (entries && entries.length) postDataBuffer = Buffer.concat(entries.map(entry => Buffer.from(entry.bytes, 'base64')));
|
||||
this.request = new network.Request(context, frame, serviceWorker, (redirectedFrom === null || redirectedFrom === void 0 ? void 0 : redirectedFrom.request) || null, documentId, url, type, method, postDataBuffer, headersOverride || (0, _utils.headersObjectToArray)(headers));
|
||||
}
|
||||
}
|
||||
class RouteImpl {
|
||||
constructor(session, interceptionId) {
|
||||
this._session = void 0;
|
||||
this._interceptionId = void 0;
|
||||
this._alreadyContinuedParams = void 0;
|
||||
this._fulfilled = false;
|
||||
this._session = session;
|
||||
this._interceptionId = interceptionId;
|
||||
}
|
||||
async continue(overrides) {
|
||||
this._alreadyContinuedParams = {
|
||||
requestId: this._interceptionId,
|
||||
url: overrides.url,
|
||||
headers: overrides.headers,
|
||||
method: overrides.method,
|
||||
postData: overrides.postData ? overrides.postData.toString('base64') : undefined
|
||||
};
|
||||
await catchDisallowedErrors(async () => {
|
||||
await this._session.send('Fetch.continueRequest', this._alreadyContinuedParams);
|
||||
});
|
||||
}
|
||||
async fulfill(response) {
|
||||
this._fulfilled = true;
|
||||
const body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64');
|
||||
const responseHeaders = splitSetCookieHeader(response.headers);
|
||||
await catchDisallowedErrors(async () => {
|
||||
await this._session.send('Fetch.fulfillRequest', {
|
||||
requestId: this._interceptionId,
|
||||
responseCode: response.status,
|
||||
responsePhrase: network.statusText(response.status),
|
||||
responseHeaders,
|
||||
body
|
||||
});
|
||||
});
|
||||
}
|
||||
async abort(errorCode = 'failed') {
|
||||
const errorReason = errorReasons[errorCode];
|
||||
(0, _utils.assert)(errorReason, 'Unknown error code: ' + errorCode);
|
||||
await catchDisallowedErrors(async () => {
|
||||
await this._session.send('Fetch.failRequest', {
|
||||
requestId: this._interceptionId,
|
||||
errorReason
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// In certain cases, protocol will return error if the request was already canceled
|
||||
// or the page was closed. We should tolerate these errors but propagate other.
|
||||
async function catchDisallowedErrors(callback) {
|
||||
try {
|
||||
return await callback();
|
||||
} catch (e) {
|
||||
if ((0, _protocolError.isProtocolError)(e) && e.message.includes('Invalid http status code or phrase')) throw e;
|
||||
}
|
||||
}
|
||||
function splitSetCookieHeader(headers) {
|
||||
const index = headers.findIndex(({
|
||||
name
|
||||
}) => name.toLowerCase() === 'set-cookie');
|
||||
if (index === -1) return headers;
|
||||
const header = headers[index];
|
||||
const values = header.value.split('\n');
|
||||
if (values.length === 1) return headers;
|
||||
const result = headers.slice();
|
||||
result.splice(index, 1, ...values.map(value => ({
|
||||
name: header.name,
|
||||
value
|
||||
})));
|
||||
return result;
|
||||
}
|
||||
const errorReasons = {
|
||||
'aborted': 'Aborted',
|
||||
'accessdenied': 'AccessDenied',
|
||||
'addressunreachable': 'AddressUnreachable',
|
||||
'blockedbyclient': 'BlockedByClient',
|
||||
'blockedbyresponse': 'BlockedByResponse',
|
||||
'connectionaborted': 'ConnectionAborted',
|
||||
'connectionclosed': 'ConnectionClosed',
|
||||
'connectionfailed': 'ConnectionFailed',
|
||||
'connectionrefused': 'ConnectionRefused',
|
||||
'connectionreset': 'ConnectionReset',
|
||||
'internetdisconnected': 'InternetDisconnected',
|
||||
'namenotresolved': 'NameNotResolved',
|
||||
'timedout': 'TimedOut',
|
||||
'failed': 'Failed'
|
||||
};
|
||||
// This class aligns responses with response headers from extra info:
|
||||
// - Network.requestWillBeSent, Network.responseReceived, Network.loadingFinished/loadingFailed are
|
||||
// dispatched using one channel.
|
||||
// - Network.requestWillBeSentExtraInfo and Network.responseReceivedExtraInfo are dispatched on
|
||||
// another channel. Those channels are not associated, so events come in random order.
|
||||
//
|
||||
// This class will associate responses with the new headers. These extra info headers will become
|
||||
// available to client reliably upon requestfinished event only. It consumes CDP
|
||||
// signals on one end and processResponse(network.Response) signals on the other hands. It then makes
|
||||
// sure that responses have all the extra headers in place by the time request finishes.
|
||||
//
|
||||
// The shape of the instrumentation API is deliberately following the CDP, so that it
|
||||
// is clear what is called when and what this means to the tracker without extra
|
||||
// documentation.
|
||||
class ResponseExtraInfoTracker {
|
||||
constructor() {
|
||||
this._requests = new Map();
|
||||
}
|
||||
requestWillBeSentExtraInfo(event) {
|
||||
const info = this._getOrCreateEntry(event.requestId);
|
||||
info.requestWillBeSentExtraInfo.push(event);
|
||||
this._patchHeaders(info, info.requestWillBeSentExtraInfo.length - 1);
|
||||
this._checkFinished(info);
|
||||
}
|
||||
requestServedFromCache(event) {
|
||||
const info = this._getOrCreateEntry(event.requestId);
|
||||
info.servedFromCache = true;
|
||||
}
|
||||
servedFromCache(requestId) {
|
||||
const info = this._requests.get(requestId);
|
||||
return !!(info !== null && info !== void 0 && info.servedFromCache);
|
||||
}
|
||||
responseReceivedExtraInfo(event) {
|
||||
const info = this._getOrCreateEntry(event.requestId);
|
||||
info.responseReceivedExtraInfo.push(event);
|
||||
this._patchHeaders(info, info.responseReceivedExtraInfo.length - 1);
|
||||
this._checkFinished(info);
|
||||
}
|
||||
processResponse(requestId, response, hasExtraInfo) {
|
||||
var _info;
|
||||
let info = this._requests.get(requestId);
|
||||
// Cached responses have erroneous "hasExtraInfo" flag.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1340398
|
||||
if (!hasExtraInfo || (_info = info) !== null && _info !== void 0 && _info.servedFromCache) {
|
||||
// Use "provisional" headers as "raw" ones.
|
||||
response.request().setRawRequestHeaders(null);
|
||||
response.setResponseHeadersSize(null);
|
||||
response.setRawResponseHeaders(null);
|
||||
return;
|
||||
}
|
||||
info = this._getOrCreateEntry(requestId);
|
||||
info.responses.push(response);
|
||||
this._patchHeaders(info, info.responses.length - 1);
|
||||
}
|
||||
loadingFinished(event) {
|
||||
const info = this._requests.get(event.requestId);
|
||||
if (!info) return;
|
||||
info.loadingFinished = event;
|
||||
this._checkFinished(info);
|
||||
}
|
||||
loadingFailed(event) {
|
||||
const info = this._requests.get(event.requestId);
|
||||
if (!info) return;
|
||||
info.loadingFailed = event;
|
||||
this._checkFinished(info);
|
||||
}
|
||||
_getOrCreateEntry(requestId) {
|
||||
let info = this._requests.get(requestId);
|
||||
if (!info) {
|
||||
info = {
|
||||
requestId: requestId,
|
||||
requestWillBeSentExtraInfo: [],
|
||||
responseReceivedExtraInfo: [],
|
||||
responses: []
|
||||
};
|
||||
this._requests.set(requestId, info);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
_patchHeaders(info, index) {
|
||||
const response = info.responses[index];
|
||||
const requestExtraInfo = info.requestWillBeSentExtraInfo[index];
|
||||
if (response && requestExtraInfo) {
|
||||
response.request().setRawRequestHeaders((0, _utils.headersObjectToArray)(requestExtraInfo.headers, '\n'));
|
||||
info.requestWillBeSentExtraInfo[index] = undefined;
|
||||
}
|
||||
const responseExtraInfo = info.responseReceivedExtraInfo[index];
|
||||
if (response && responseExtraInfo) {
|
||||
var _responseExtraInfo$he;
|
||||
response.setResponseHeadersSize(((_responseExtraInfo$he = responseExtraInfo.headersText) === null || _responseExtraInfo$he === void 0 ? void 0 : _responseExtraInfo$he.length) || 0);
|
||||
response.setRawResponseHeaders((0, _utils.headersObjectToArray)(responseExtraInfo.headers, '\n'));
|
||||
info.responseReceivedExtraInfo[index] = undefined;
|
||||
}
|
||||
}
|
||||
_checkFinished(info) {
|
||||
if (!info.loadingFinished && !info.loadingFailed) return;
|
||||
if (info.responses.length <= info.responseReceivedExtraInfo.length) {
|
||||
// We have extra info for each response.
|
||||
this._stopTracking(info.requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are not done yet.
|
||||
}
|
||||
_stopTracking(requestId) {
|
||||
this._requests.delete(requestId);
|
||||
}
|
||||
}
|
||||
1136
node_modules/playwright-core/lib/server/chromium/crPage.js
generated
vendored
Normal file
1136
node_modules/playwright-core/lib/server/chromium/crPage.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
153
node_modules/playwright-core/lib/server/chromium/crPdf.js
generated
vendored
Normal file
153
node_modules/playwright-core/lib/server/chromium/crPdf.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRPDF = void 0;
|
||||
var _utils = require("../../utils");
|
||||
var _crProtocolHelper = require("./crProtocolHelper");
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const PagePaperFormats = {
|
||||
letter: {
|
||||
width: 8.5,
|
||||
height: 11
|
||||
},
|
||||
legal: {
|
||||
width: 8.5,
|
||||
height: 14
|
||||
},
|
||||
tabloid: {
|
||||
width: 11,
|
||||
height: 17
|
||||
},
|
||||
ledger: {
|
||||
width: 17,
|
||||
height: 11
|
||||
},
|
||||
a0: {
|
||||
width: 33.1,
|
||||
height: 46.8
|
||||
},
|
||||
a1: {
|
||||
width: 23.4,
|
||||
height: 33.1
|
||||
},
|
||||
a2: {
|
||||
width: 16.54,
|
||||
height: 23.4
|
||||
},
|
||||
a3: {
|
||||
width: 11.7,
|
||||
height: 16.54
|
||||
},
|
||||
a4: {
|
||||
width: 8.27,
|
||||
height: 11.7
|
||||
},
|
||||
a5: {
|
||||
width: 5.83,
|
||||
height: 8.27
|
||||
},
|
||||
a6: {
|
||||
width: 4.13,
|
||||
height: 5.83
|
||||
}
|
||||
};
|
||||
const unitToPixels = {
|
||||
'px': 1,
|
||||
'in': 96,
|
||||
'cm': 37.8,
|
||||
'mm': 3.78
|
||||
};
|
||||
function convertPrintParameterToInches(text) {
|
||||
if (text === undefined) return undefined;
|
||||
let unit = text.substring(text.length - 2).toLowerCase();
|
||||
let valueText = '';
|
||||
if (unitToPixels.hasOwnProperty(unit)) {
|
||||
valueText = text.substring(0, text.length - 2);
|
||||
} else {
|
||||
// In case of unknown unit try to parse the whole parameter as number of pixels.
|
||||
// This is consistent with phantom's paperSize behavior.
|
||||
unit = 'px';
|
||||
valueText = text;
|
||||
}
|
||||
const value = Number(valueText);
|
||||
(0, _utils.assert)(!isNaN(value), 'Failed to parse parameter value: ' + text);
|
||||
const pixels = value * unitToPixels[unit];
|
||||
return pixels / 96;
|
||||
}
|
||||
class CRPDF {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._client = client;
|
||||
}
|
||||
async generate(options) {
|
||||
const {
|
||||
scale = 1,
|
||||
displayHeaderFooter = false,
|
||||
headerTemplate = '',
|
||||
footerTemplate = '',
|
||||
printBackground = false,
|
||||
landscape = false,
|
||||
pageRanges = '',
|
||||
preferCSSPageSize = false,
|
||||
margin = {},
|
||||
tagged = false,
|
||||
outline = false
|
||||
} = options;
|
||||
let paperWidth = 8.5;
|
||||
let paperHeight = 11;
|
||||
if (options.format) {
|
||||
const format = PagePaperFormats[options.format.toLowerCase()];
|
||||
(0, _utils.assert)(format, 'Unknown paper format: ' + options.format);
|
||||
paperWidth = format.width;
|
||||
paperHeight = format.height;
|
||||
} else {
|
||||
paperWidth = convertPrintParameterToInches(options.width) || paperWidth;
|
||||
paperHeight = convertPrintParameterToInches(options.height) || paperHeight;
|
||||
}
|
||||
const marginTop = convertPrintParameterToInches(margin.top) || 0;
|
||||
const marginLeft = convertPrintParameterToInches(margin.left) || 0;
|
||||
const marginBottom = convertPrintParameterToInches(margin.bottom) || 0;
|
||||
const marginRight = convertPrintParameterToInches(margin.right) || 0;
|
||||
const generateDocumentOutline = outline;
|
||||
const generateTaggedPDF = tagged;
|
||||
const result = await this._client.send('Page.printToPDF', {
|
||||
transferMode: 'ReturnAsStream',
|
||||
landscape,
|
||||
displayHeaderFooter,
|
||||
headerTemplate,
|
||||
footerTemplate,
|
||||
printBackground,
|
||||
scale,
|
||||
paperWidth,
|
||||
paperHeight,
|
||||
marginTop,
|
||||
marginBottom,
|
||||
marginLeft,
|
||||
marginRight,
|
||||
pageRanges,
|
||||
preferCSSPageSize,
|
||||
generateTaggedPDF,
|
||||
generateDocumentOutline
|
||||
});
|
||||
return await (0, _crProtocolHelper.readProtocolStream)(this._client, result.stream);
|
||||
}
|
||||
}
|
||||
exports.CRPDF = CRPDF;
|
||||
133
node_modules/playwright-core/lib/server/chromium/crProtocolHelper.js
generated
vendored
Normal file
133
node_modules/playwright-core/lib/server/chromium/crProtocolHelper.js
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.exceptionToError = exceptionToError;
|
||||
exports.getExceptionMessage = getExceptionMessage;
|
||||
exports.readProtocolStream = readProtocolStream;
|
||||
exports.releaseObject = releaseObject;
|
||||
exports.saveProtocolStream = saveProtocolStream;
|
||||
exports.toButtonsMask = toButtonsMask;
|
||||
exports.toConsoleMessageLocation = toConsoleMessageLocation;
|
||||
exports.toModifiersMask = toModifiersMask;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _fileUtils = require("../../utils/fileUtils");
|
||||
var _stackTrace = require("../../utils/stackTrace");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function getExceptionMessage(exceptionDetails) {
|
||||
if (exceptionDetails.exception) return exceptionDetails.exception.description || String(exceptionDetails.exception.value);
|
||||
let message = exceptionDetails.text;
|
||||
if (exceptionDetails.stackTrace) {
|
||||
for (const callframe of exceptionDetails.stackTrace.callFrames) {
|
||||
const location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber;
|
||||
const functionName = callframe.functionName || '<anonymous>';
|
||||
message += `\n at ${functionName} (${location})`;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
async function releaseObject(client, objectId) {
|
||||
await client.send('Runtime.releaseObject', {
|
||||
objectId
|
||||
}).catch(error => {});
|
||||
}
|
||||
async function saveProtocolStream(client, handle, path) {
|
||||
let eof = false;
|
||||
await (0, _fileUtils.mkdirIfNeeded)(path);
|
||||
const fd = await _fs.default.promises.open(path, 'w');
|
||||
while (!eof) {
|
||||
const response = await client.send('IO.read', {
|
||||
handle
|
||||
});
|
||||
eof = response.eof;
|
||||
const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
||||
await fd.write(buf);
|
||||
}
|
||||
await fd.close();
|
||||
await client.send('IO.close', {
|
||||
handle
|
||||
});
|
||||
}
|
||||
async function readProtocolStream(client, handle) {
|
||||
let eof = false;
|
||||
const chunks = [];
|
||||
while (!eof) {
|
||||
const response = await client.send('IO.read', {
|
||||
handle
|
||||
});
|
||||
eof = response.eof;
|
||||
const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
||||
chunks.push(buf);
|
||||
}
|
||||
await client.send('IO.close', {
|
||||
handle
|
||||
});
|
||||
return Buffer.concat(chunks);
|
||||
}
|
||||
function toConsoleMessageLocation(stackTrace) {
|
||||
return stackTrace && stackTrace.callFrames.length ? {
|
||||
url: stackTrace.callFrames[0].url,
|
||||
lineNumber: stackTrace.callFrames[0].lineNumber,
|
||||
columnNumber: stackTrace.callFrames[0].columnNumber
|
||||
} : {
|
||||
url: '',
|
||||
lineNumber: 0,
|
||||
columnNumber: 0
|
||||
};
|
||||
}
|
||||
function exceptionToError(exceptionDetails) {
|
||||
var _exceptionDetails$exc, _nameOverride$value;
|
||||
const messageWithStack = getExceptionMessage(exceptionDetails);
|
||||
const lines = messageWithStack.split('\n');
|
||||
const firstStackTraceLine = lines.findIndex(line => line.startsWith(' at'));
|
||||
let messageWithName = '';
|
||||
let stack = '';
|
||||
if (firstStackTraceLine === -1) {
|
||||
messageWithName = messageWithStack;
|
||||
} else {
|
||||
messageWithName = lines.slice(0, firstStackTraceLine).join('\n');
|
||||
stack = messageWithStack;
|
||||
}
|
||||
const {
|
||||
name,
|
||||
message
|
||||
} = (0, _stackTrace.splitErrorMessage)(messageWithName);
|
||||
const err = new Error(message);
|
||||
err.stack = stack;
|
||||
const nameOverride = (_exceptionDetails$exc = exceptionDetails.exception) === null || _exceptionDetails$exc === void 0 || (_exceptionDetails$exc = _exceptionDetails$exc.preview) === null || _exceptionDetails$exc === void 0 ? void 0 : _exceptionDetails$exc.properties.find(o => o.name === 'name');
|
||||
err.name = nameOverride ? (_nameOverride$value = nameOverride.value) !== null && _nameOverride$value !== void 0 ? _nameOverride$value : 'Error' : name;
|
||||
return err;
|
||||
}
|
||||
function toModifiersMask(modifiers) {
|
||||
let mask = 0;
|
||||
if (modifiers.has('Alt')) mask |= 1;
|
||||
if (modifiers.has('Control')) mask |= 2;
|
||||
if (modifiers.has('Meta')) mask |= 4;
|
||||
if (modifiers.has('Shift')) mask |= 8;
|
||||
return mask;
|
||||
}
|
||||
function toButtonsMask(buttons) {
|
||||
let mask = 0;
|
||||
if (buttons.has('left')) mask |= 1;
|
||||
if (buttons.has('right')) mask |= 2;
|
||||
if (buttons.has('middle')) mask |= 4;
|
||||
return mask;
|
||||
}
|
||||
112
node_modules/playwright-core/lib/server/chromium/crServiceWorker.js
generated
vendored
Normal file
112
node_modules/playwright-core/lib/server/chromium/crServiceWorker.js
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CRServiceWorker = void 0;
|
||||
var _page = require("../page");
|
||||
var _crExecutionContext = require("./crExecutionContext");
|
||||
var _crNetworkManager = require("./crNetworkManager");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
var _browserContext = require("../browserContext");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CRServiceWorker extends _page.Worker {
|
||||
constructor(browserContext, session, url) {
|
||||
super(browserContext, url);
|
||||
this._browserContext = void 0;
|
||||
this._networkManager = void 0;
|
||||
this._session = void 0;
|
||||
this._session = session;
|
||||
this._browserContext = browserContext;
|
||||
if (!!process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS) this._networkManager = new _crNetworkManager.CRNetworkManager(null, this);
|
||||
session.once('Runtime.executionContextCreated', event => {
|
||||
this._createExecutionContext(new _crExecutionContext.CRExecutionContext(session, event.context));
|
||||
});
|
||||
if (this._networkManager && this._isNetworkInspectionEnabled()) {
|
||||
this.updateRequestInterception();
|
||||
this.updateExtraHTTPHeaders();
|
||||
this.updateHttpCredentials();
|
||||
this.updateOffline();
|
||||
this._networkManager.addSession(session, undefined, true /* isMain */).catch(() => {});
|
||||
}
|
||||
session.send('Runtime.enable', {}).catch(e => {});
|
||||
session.send('Runtime.runIfWaitingForDebugger').catch(e => {});
|
||||
session.on('Inspector.targetReloadedAfterCrash', () => {
|
||||
// Resume service worker after restart.
|
||||
session._sendMayFail('Runtime.runIfWaitingForDebugger', {});
|
||||
});
|
||||
}
|
||||
didClose() {
|
||||
var _this$_networkManager;
|
||||
(_this$_networkManager = this._networkManager) === null || _this$_networkManager === void 0 || _this$_networkManager.removeSession(this._session);
|
||||
this._session.dispose();
|
||||
super.didClose();
|
||||
}
|
||||
async updateOffline() {
|
||||
var _this$_networkManager2;
|
||||
if (!this._isNetworkInspectionEnabled()) return;
|
||||
await ((_this$_networkManager2 = this._networkManager) === null || _this$_networkManager2 === void 0 ? void 0 : _this$_networkManager2.setOffline(!!this._browserContext._options.offline).catch(() => {}));
|
||||
}
|
||||
async updateHttpCredentials() {
|
||||
var _this$_networkManager3;
|
||||
if (!this._isNetworkInspectionEnabled()) return;
|
||||
await ((_this$_networkManager3 = this._networkManager) === null || _this$_networkManager3 === void 0 ? void 0 : _this$_networkManager3.authenticate(this._browserContext._options.httpCredentials || null).catch(() => {}));
|
||||
}
|
||||
async updateExtraHTTPHeaders() {
|
||||
var _this$_networkManager4;
|
||||
if (!this._isNetworkInspectionEnabled()) return;
|
||||
await ((_this$_networkManager4 = this._networkManager) === null || _this$_networkManager4 === void 0 ? void 0 : _this$_networkManager4.setExtraHTTPHeaders(this._browserContext._options.extraHTTPHeaders || []).catch(() => {}));
|
||||
}
|
||||
async updateRequestInterception() {
|
||||
var _this$_networkManager5;
|
||||
if (!this._isNetworkInspectionEnabled()) return;
|
||||
await ((_this$_networkManager5 = this._networkManager) === null || _this$_networkManager5 === void 0 ? void 0 : _this$_networkManager5.setRequestInterception(this.needsRequestInterception()).catch(() => {}));
|
||||
}
|
||||
needsRequestInterception() {
|
||||
return this._isNetworkInspectionEnabled() && !!this._browserContext._requestInterceptor;
|
||||
}
|
||||
reportRequestFinished(request, response) {
|
||||
this._browserContext.emit(_browserContext.BrowserContext.Events.RequestFinished, {
|
||||
request,
|
||||
response
|
||||
});
|
||||
}
|
||||
requestFailed(request, _canceled) {
|
||||
this._browserContext.emit(_browserContext.BrowserContext.Events.RequestFailed, request);
|
||||
}
|
||||
requestReceivedResponse(response) {
|
||||
this._browserContext.emit(_browserContext.BrowserContext.Events.Response, response);
|
||||
}
|
||||
requestStarted(request, route) {
|
||||
this._browserContext.emit(_browserContext.BrowserContext.Events.Request, request);
|
||||
if (route) {
|
||||
var _this$_browserContext, _this$_browserContext2;
|
||||
const r = new network.Route(request, route);
|
||||
if ((_this$_browserContext = (_this$_browserContext2 = this._browserContext)._requestInterceptor) !== null && _this$_browserContext !== void 0 && _this$_browserContext.call(_this$_browserContext2, r, request)) return;
|
||||
r.continue({
|
||||
isFallback: true
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
_isNetworkInspectionEnabled() {
|
||||
return this._browserContext._options.serviceWorkers !== 'block';
|
||||
}
|
||||
}
|
||||
exports.CRServiceWorker = CRServiceWorker;
|
||||
145
node_modules/playwright-core/lib/server/chromium/defaultFontFamilies.js
generated
vendored
Normal file
145
node_modules/playwright-core/lib/server/chromium/defaultFontFamilies.js
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.platformToFontFamilies = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// DO NOT EDIT: this map is generated from Chromium source code by utils/generate_chromium_default_font_families.js
|
||||
const platformToFontFamilies = exports.platformToFontFamilies = {
|
||||
'linux': {
|
||||
'fontFamilies': {
|
||||
'standard': 'Times New Roman',
|
||||
'fixed': 'Monospace',
|
||||
'serif': 'Times New Roman',
|
||||
'sansSerif': 'Arial',
|
||||
'cursive': 'Comic Sans MS',
|
||||
'fantasy': 'Impact'
|
||||
}
|
||||
},
|
||||
'mac': {
|
||||
'fontFamilies': {
|
||||
'standard': 'Times',
|
||||
'fixed': 'Courier',
|
||||
'serif': 'Times',
|
||||
'sansSerif': 'Helvetica',
|
||||
'cursive': 'Apple Chancery',
|
||||
'fantasy': 'Papyrus'
|
||||
},
|
||||
'forScripts': [{
|
||||
'script': 'jpan',
|
||||
'fontFamilies': {
|
||||
'standard': 'Hiragino Kaku Gothic ProN',
|
||||
'fixed': 'Osaka-Mono',
|
||||
'serif': 'Hiragino Mincho ProN',
|
||||
'sansSerif': 'Hiragino Kaku Gothic ProN'
|
||||
}
|
||||
}, {
|
||||
'script': 'hang',
|
||||
'fontFamilies': {
|
||||
'standard': 'Apple SD Gothic Neo',
|
||||
'serif': 'AppleMyungjo',
|
||||
'sansSerif': 'Apple SD Gothic Neo'
|
||||
}
|
||||
}, {
|
||||
'script': 'hans',
|
||||
'fontFamilies': {
|
||||
'standard': ',PingFang SC,STHeiti',
|
||||
'serif': 'Songti SC',
|
||||
'sansSerif': ',PingFang SC,STHeiti',
|
||||
'cursive': 'Kaiti SC'
|
||||
}
|
||||
}, {
|
||||
'script': 'hant',
|
||||
'fontFamilies': {
|
||||
'standard': ',PingFang TC,Heiti TC',
|
||||
'serif': 'Songti TC',
|
||||
'sansSerif': ',PingFang TC,Heiti TC',
|
||||
'cursive': 'Kaiti TC'
|
||||
}
|
||||
}]
|
||||
},
|
||||
'win': {
|
||||
'fontFamilies': {
|
||||
'standard': 'Times New Roman',
|
||||
'fixed': 'Consolas',
|
||||
'serif': 'Times New Roman',
|
||||
'sansSerif': 'Arial',
|
||||
'cursive': 'Comic Sans MS',
|
||||
'fantasy': 'Impact'
|
||||
},
|
||||
'forScripts': [{
|
||||
'script': 'cyrl',
|
||||
'fontFamilies': {
|
||||
'standard': 'Times New Roman',
|
||||
'fixed': 'Courier New',
|
||||
'serif': 'Times New Roman',
|
||||
'sansSerif': 'Arial'
|
||||
}
|
||||
}, {
|
||||
'script': 'arab',
|
||||
'fontFamilies': {
|
||||
'fixed': 'Courier New',
|
||||
'sansSerif': 'Segoe UI'
|
||||
}
|
||||
}, {
|
||||
'script': 'grek',
|
||||
'fontFamilies': {
|
||||
'standard': 'Times New Roman',
|
||||
'fixed': 'Courier New',
|
||||
'serif': 'Times New Roman',
|
||||
'sansSerif': 'Arial'
|
||||
}
|
||||
}, {
|
||||
'script': 'jpan',
|
||||
'fontFamilies': {
|
||||
'standard': ',Meiryo,Yu Gothic',
|
||||
'fixed': 'MS Gothic',
|
||||
'serif': ',Yu Mincho,MS PMincho',
|
||||
'sansSerif': ',Meiryo,Yu Gothic'
|
||||
}
|
||||
}, {
|
||||
'script': 'hang',
|
||||
'fontFamilies': {
|
||||
'standard': 'Malgun Gothic',
|
||||
'fixed': 'Gulimche',
|
||||
'serif': 'Batang',
|
||||
'sansSerif': 'Malgun Gothic',
|
||||
'cursive': 'Gungsuh'
|
||||
}
|
||||
}, {
|
||||
'script': 'hans',
|
||||
'fontFamilies': {
|
||||
'standard': 'Microsoft YaHei',
|
||||
'fixed': 'NSimsun',
|
||||
'serif': 'Simsun',
|
||||
'sansSerif': 'Microsoft YaHei',
|
||||
'cursive': 'KaiTi'
|
||||
}
|
||||
}, {
|
||||
'script': 'hant',
|
||||
'fontFamilies': {
|
||||
'standard': 'Microsoft JhengHei',
|
||||
'fixed': 'MingLiU',
|
||||
'serif': 'PMingLiU',
|
||||
'sansSerif': 'Microsoft JhengHei',
|
||||
'cursive': 'DFKai-SB'
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
155
node_modules/playwright-core/lib/server/chromium/videoRecorder.js
generated
vendored
Normal file
155
node_modules/playwright-core/lib/server/chromium/videoRecorder.js
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.VideoRecorder = void 0;
|
||||
var _utils = require("../../utils");
|
||||
var _page = require("../page");
|
||||
var _processLauncher = require("../../utils/processLauncher");
|
||||
var _progress = require("../progress");
|
||||
var _instrumentation = require("../instrumentation");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const fps = 25;
|
||||
class VideoRecorder {
|
||||
static async launch(page, ffmpegPath, options) {
|
||||
if (!options.outputFile.endsWith('.webm')) throw new Error('File must have .webm extension');
|
||||
const controller = new _progress.ProgressController((0, _instrumentation.serverSideCallMetadata)(), page);
|
||||
controller.setLogName('browser');
|
||||
return await controller.run(async progress => {
|
||||
const recorder = new VideoRecorder(page, ffmpegPath, progress);
|
||||
await recorder._launch(options);
|
||||
return recorder;
|
||||
});
|
||||
}
|
||||
constructor(page, ffmpegPath, progress) {
|
||||
this._process = null;
|
||||
this._gracefullyClose = null;
|
||||
this._lastWritePromise = Promise.resolve();
|
||||
this._lastFrameTimestamp = 0;
|
||||
this._lastFrameBuffer = null;
|
||||
this._lastWriteTimestamp = 0;
|
||||
this._progress = void 0;
|
||||
this._frameQueue = [];
|
||||
this._isStopped = false;
|
||||
this._ffmpegPath = void 0;
|
||||
this._progress = progress;
|
||||
this._ffmpegPath = ffmpegPath;
|
||||
page.on(_page.Page.Events.ScreencastFrame, frame => this.writeFrame(frame.buffer, frame.frameSwapWallTime / 1000));
|
||||
}
|
||||
async _launch(options) {
|
||||
// How to tune the codec:
|
||||
// 1. Read vp8 documentation to figure out the options.
|
||||
// https://www.webmproject.org/docs/encoder-parameters/
|
||||
// 2. Use the following command to map the options to ffmpeg arguments.
|
||||
// $ ./third_party/ffmpeg/ffmpeg-mac -h encoder=vp8
|
||||
// 3. A bit more about passing vp8 options to ffmpeg.
|
||||
// https://trac.ffmpeg.org/wiki/Encode/VP8
|
||||
// 4. Tuning for VP9:
|
||||
// https://developers.google.com/media/vp9/live-encoding
|
||||
//
|
||||
// How to stress-test video recording (runs 10 recorders in parallel to book all cpus available):
|
||||
// $ node ./utils/video_stress.js
|
||||
//
|
||||
// We use the following vp8 options:
|
||||
// "-qmin 0 -qmax 50" - quality variation from 0 to 50.
|
||||
// Suggested here: https://trac.ffmpeg.org/wiki/Encode/VP8
|
||||
// "-crf 8" - constant quality mode, 4-63, lower means better quality.
|
||||
// "-deadline realtime -speed 8" - do not use too much cpu to keep up with incoming frames.
|
||||
// "-b:v 1M" - video bitrate. Default value is too low for vp8
|
||||
// Suggested here: https://trac.ffmpeg.org/wiki/Encode/VP8
|
||||
// Note that we can switch to "-qmin 20 -qmax 50 -crf 30" for smaller video size but worse quality.
|
||||
//
|
||||
// We use "pad" and "crop" video filters (-vf option) to resize incoming frames
|
||||
// that might be of the different size to the desired video size.
|
||||
// https://ffmpeg.org/ffmpeg-filters.html#pad-1
|
||||
// https://ffmpeg.org/ffmpeg-filters.html#crop
|
||||
//
|
||||
// We use "image2pipe" mode to pipe frames and get a single video - https://trac.ffmpeg.org/wiki/Slideshow
|
||||
// "-f image2pipe -c:v mjpeg -i -" forces input to be read from standard input, and forces
|
||||
// mjpeg input image format.
|
||||
// "-avioflags direct" reduces general buffering.
|
||||
// "-fpsprobesize 0 -probesize 32 -analyzeduration 0" reduces initial buffering
|
||||
// while analyzing input fps and other stats.
|
||||
//
|
||||
// "-y" means overwrite output.
|
||||
// "-an" means no audio.
|
||||
// "-threads 1" means using one thread. This drastically reduces stalling when
|
||||
// cpu is overbooked. By default vp8 tries to use all available threads?
|
||||
|
||||
const w = options.width;
|
||||
const h = options.height;
|
||||
const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(' ');
|
||||
args.push(options.outputFile);
|
||||
const progress = this._progress;
|
||||
const {
|
||||
launchedProcess,
|
||||
gracefullyClose
|
||||
} = await (0, _processLauncher.launchProcess)({
|
||||
command: this._ffmpegPath,
|
||||
args,
|
||||
stdio: 'stdin',
|
||||
log: message => progress.log(message),
|
||||
tempDirectories: [],
|
||||
attemptToGracefullyClose: async () => {
|
||||
progress.log('Closing stdin...');
|
||||
launchedProcess.stdin.end();
|
||||
},
|
||||
onExit: (exitCode, signal) => {
|
||||
progress.log(`ffmpeg onkill exitCode=${exitCode} signal=${signal}`);
|
||||
}
|
||||
});
|
||||
launchedProcess.stdin.on('finish', () => {
|
||||
progress.log('ffmpeg finished input.');
|
||||
});
|
||||
launchedProcess.stdin.on('error', () => {
|
||||
progress.log('ffmpeg error.');
|
||||
});
|
||||
this._process = launchedProcess;
|
||||
this._gracefullyClose = gracefullyClose;
|
||||
}
|
||||
writeFrame(frame, timestamp) {
|
||||
(0, _utils.assert)(this._process);
|
||||
if (this._isStopped) return;
|
||||
if (this._lastFrameBuffer) {
|
||||
const durationSec = timestamp - this._lastFrameTimestamp;
|
||||
const repeatCount = Math.max(1, Math.round(fps * durationSec));
|
||||
for (let i = 0; i < repeatCount; ++i) this._frameQueue.push(this._lastFrameBuffer);
|
||||
this._lastWritePromise = this._lastWritePromise.then(() => this._sendFrames());
|
||||
}
|
||||
this._lastFrameBuffer = frame;
|
||||
this._lastFrameTimestamp = timestamp;
|
||||
this._lastWriteTimestamp = (0, _utils.monotonicTime)();
|
||||
}
|
||||
async _sendFrames() {
|
||||
while (this._frameQueue.length) await this._sendFrame(this._frameQueue.shift());
|
||||
}
|
||||
async _sendFrame(frame) {
|
||||
return new Promise(f => this._process.stdin.write(frame, f)).then(error => {
|
||||
if (error) this._progress.log(`ffmpeg failed to write: ${String(error)}`);
|
||||
});
|
||||
}
|
||||
async stop() {
|
||||
if (this._isStopped) return;
|
||||
this.writeFrame(Buffer.from([]), this._lastFrameTimestamp + ((0, _utils.monotonicTime)() - this._lastWriteTimestamp) / 1000);
|
||||
this._isStopped = true;
|
||||
await this._lastWritePromise;
|
||||
await this._gracefullyClose();
|
||||
}
|
||||
}
|
||||
exports.VideoRecorder = VideoRecorder;
|
||||
125
node_modules/playwright-core/lib/server/clock.js
generated
vendored
Normal file
125
node_modules/playwright-core/lib/server/clock.js
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Clock = void 0;
|
||||
var clockSource = _interopRequireWildcard(require("../generated/clockSource"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Clock {
|
||||
constructor(browserContext) {
|
||||
this._browserContext = void 0;
|
||||
this._scriptInstalled = false;
|
||||
this._browserContext = browserContext;
|
||||
}
|
||||
markAsUninstalled() {
|
||||
this._scriptInstalled = false;
|
||||
}
|
||||
async fastForward(ticks) {
|
||||
await this._installIfNeeded();
|
||||
const ticksMillis = parseTicks(ticks);
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('fastForward', ${Date.now()}, ${ticksMillis})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.fastForward(${ticksMillis})`);
|
||||
}
|
||||
async install(time) {
|
||||
await this._installIfNeeded();
|
||||
const timeMillis = time !== undefined ? parseTime(time) : Date.now();
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('install', ${Date.now()}, ${timeMillis})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.install(${timeMillis})`);
|
||||
}
|
||||
async pauseAt(ticks) {
|
||||
await this._installIfNeeded();
|
||||
const timeMillis = parseTime(ticks);
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('pauseAt', ${Date.now()}, ${timeMillis})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.pauseAt(${timeMillis})`);
|
||||
}
|
||||
async resume() {
|
||||
await this._installIfNeeded();
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('resume', ${Date.now()})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.resume()`);
|
||||
}
|
||||
async setFixedTime(time) {
|
||||
await this._installIfNeeded();
|
||||
const timeMillis = parseTime(time);
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('setFixedTime', ${Date.now()}, ${timeMillis})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.setFixedTime(${timeMillis})`);
|
||||
}
|
||||
async setSystemTime(time) {
|
||||
await this._installIfNeeded();
|
||||
const timeMillis = parseTime(time);
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('setSystemTime', ${Date.now()}, ${timeMillis})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.setSystemTime(${timeMillis})`);
|
||||
}
|
||||
async runFor(ticks) {
|
||||
await this._installIfNeeded();
|
||||
const ticksMillis = parseTicks(ticks);
|
||||
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('runFor', ${Date.now()}, ${ticksMillis})`);
|
||||
await this._evaluateInFrames(`globalThis.__pwClock.controller.runFor(${ticksMillis})`);
|
||||
}
|
||||
async _installIfNeeded() {
|
||||
if (this._scriptInstalled) return;
|
||||
this._scriptInstalled = true;
|
||||
const script = `(() => {
|
||||
const module = {};
|
||||
${clockSource.source}
|
||||
globalThis.__pwClock = (module.exports.inject())(globalThis);
|
||||
})();`;
|
||||
await this._browserContext.addInitScript(script);
|
||||
await this._evaluateInFrames(script);
|
||||
}
|
||||
async _evaluateInFrames(script) {
|
||||
await this._browserContext.safeNonStallingEvaluateInAllFrames(script, 'main', {
|
||||
throwOnJSErrors: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse strings like '01:10:00' (meaning 1 hour, 10 minutes, 0 seconds) into
|
||||
* number of milliseconds. This is used to support human-readable strings passed
|
||||
* to clock.tick()
|
||||
*/
|
||||
exports.Clock = Clock;
|
||||
function parseTicks(value) {
|
||||
if (typeof value === 'number') return value;
|
||||
if (!value) return 0;
|
||||
const str = value;
|
||||
const strings = str.split(':');
|
||||
const l = strings.length;
|
||||
let i = l;
|
||||
let ms = 0;
|
||||
let parsed;
|
||||
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
|
||||
throw new Error(`Clock only understands numbers, 'mm:ss' and 'hh:mm:ss'`);
|
||||
}
|
||||
while (i--) {
|
||||
parsed = parseInt(strings[i], 10);
|
||||
if (parsed >= 60) throw new Error(`Invalid time ${str}`);
|
||||
ms += parsed * Math.pow(60, l - i - 1);
|
||||
}
|
||||
return ms * 1000;
|
||||
}
|
||||
function parseTime(epoch) {
|
||||
if (!epoch) return 0;
|
||||
if (typeof epoch === 'number') return epoch;
|
||||
const parsed = new Date(epoch);
|
||||
if (!isFinite(parsed.getTime())) throw new Error(`Invalid date: ${epoch}`);
|
||||
return parsed.getTime();
|
||||
}
|
||||
294
node_modules/playwright-core/lib/server/codegen/csharp.js
generated
vendored
Normal file
294
node_modules/playwright-core/lib/server/codegen/csharp.js
generated
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CSharpLanguageGenerator = void 0;
|
||||
var _language = require("./language");
|
||||
var _utils = require("../../utils");
|
||||
var _deviceDescriptors = require("../deviceDescriptors");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CSharpLanguageGenerator {
|
||||
constructor(mode) {
|
||||
this.id = void 0;
|
||||
this.groupName = '.NET C#';
|
||||
this.name = void 0;
|
||||
this.highlighter = 'csharp';
|
||||
this._mode = void 0;
|
||||
if (mode === 'library') {
|
||||
this.name = 'Library';
|
||||
this.id = 'csharp';
|
||||
} else if (mode === 'mstest') {
|
||||
this.name = 'MSTest';
|
||||
this.id = 'csharp-mstest';
|
||||
} else if (mode === 'nunit') {
|
||||
this.name = 'NUnit';
|
||||
this.id = 'csharp-nunit';
|
||||
} else {
|
||||
throw new Error(`Unknown C# language mode: ${mode}`);
|
||||
}
|
||||
this._mode = mode;
|
||||
}
|
||||
generateAction(actionInContext) {
|
||||
const action = this._generateActionInner(actionInContext);
|
||||
if (action) return action;
|
||||
return '';
|
||||
}
|
||||
_generateActionInner(actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage')) return '';
|
||||
let pageAlias = actionInContext.frame.pageAlias;
|
||||
if (this._mode !== 'library') pageAlias = pageAlias.replace('page', 'Page');
|
||||
const formatter = new CSharpFormatter(this._mode === 'library' ? 0 : 8);
|
||||
if (action.name === 'openPage') {
|
||||
formatter.add(`var ${pageAlias} = await context.NewPageAsync();`);
|
||||
if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`await ${pageAlias}.GotoAsync(${quote(action.url)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.ContentFrame`);
|
||||
const subject = `${pageAlias}${locators.join('')}`;
|
||||
const signals = (0, _language.toSignalMap)(action);
|
||||
if (signals.dialog) {
|
||||
formatter.add(` void ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler(object sender, IDialog dialog)
|
||||
{
|
||||
Console.WriteLine($"Dialog message: {dialog.Message}");
|
||||
dialog.DismissAsync();
|
||||
${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;
|
||||
}
|
||||
${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`);
|
||||
}
|
||||
const lines = [];
|
||||
lines.push(this._generateActionCall(subject, actionInContext));
|
||||
if (signals.download) {
|
||||
lines.unshift(`var download${signals.download.downloadAlias} = await ${pageAlias}.RunAndWaitForDownloadAsync(async () =>\n{`);
|
||||
lines.push(`});`);
|
||||
}
|
||||
if (signals.popup) {
|
||||
lines.unshift(`var ${signals.popup.popupAlias} = await ${pageAlias}.RunAndWaitForPopupAsync(async () =>\n{`);
|
||||
lines.push(`});`);
|
||||
}
|
||||
for (const line of lines) formatter.add(line);
|
||||
return formatter.format();
|
||||
}
|
||||
_generateActionCall(subject, actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
switch (action.name) {
|
||||
case 'openPage':
|
||||
throw Error('Not reached');
|
||||
case 'closePage':
|
||||
return `await ${subject}.CloseAsync();`;
|
||||
case 'click':
|
||||
{
|
||||
let method = 'Click';
|
||||
if (action.clickCount === 2) method = 'DblClick';
|
||||
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
||||
if (!Object.entries(options).length) return `await ${subject}.${this._asLocator(action.selector)}.${method}Async();`;
|
||||
const optionsString = formatObject(options, ' ', 'Locator' + method + 'Options');
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.${method}Async(${optionsString});`;
|
||||
}
|
||||
case 'check':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.CheckAsync();`;
|
||||
case 'uncheck':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.UncheckAsync();`;
|
||||
case 'fill':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.FillAsync(${quote(action.text)});`;
|
||||
case 'setInputFiles':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.SetInputFilesAsync(${formatObject(action.files)});`;
|
||||
case 'press':
|
||||
{
|
||||
const modifiers = (0, _language.toKeyboardModifiers)(action.modifiers);
|
||||
const shortcut = [...modifiers, action.key].join('+');
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.PressAsync(${quote(shortcut)});`;
|
||||
}
|
||||
case 'navigate':
|
||||
return `await ${subject}.GotoAsync(${quote(action.url)});`;
|
||||
case 'select':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.SelectOptionAsync(${formatObject(action.options)});`;
|
||||
case 'assertText':
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'ToContainTextAsync' : 'ToHaveTextAsync'}(${quote(action.text)});`;
|
||||
case 'assertChecked':
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)})${action.checked ? '' : '.Not'}.ToBeCheckedAsync();`;
|
||||
case 'assertVisible':
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToBeVisibleAsync();`;
|
||||
case 'assertValue':
|
||||
{
|
||||
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`;
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||
}
|
||||
case 'assertSnapshot':
|
||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToMatchAriaSnapshotAsync(${quote(action.snapshot)});`;
|
||||
}
|
||||
}
|
||||
_asLocator(selector) {
|
||||
return (0, _utils.asLocator)('csharp', selector);
|
||||
}
|
||||
generateHeader(options) {
|
||||
if (this._mode === 'library') return this.generateStandaloneHeader(options);
|
||||
return this.generateTestRunnerHeader(options);
|
||||
}
|
||||
generateStandaloneHeader(options) {
|
||||
const formatter = new CSharpFormatter(0);
|
||||
formatter.add(`
|
||||
using Microsoft.Playwright;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});
|
||||
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)});`);
|
||||
formatter.newLine();
|
||||
return formatter.format();
|
||||
}
|
||||
generateTestRunnerHeader(options) {
|
||||
const formatter = new CSharpFormatter(0);
|
||||
formatter.add(`
|
||||
using Microsoft.Playwright.${this._mode === 'nunit' ? 'NUnit' : 'MSTest'};
|
||||
using Microsoft.Playwright;
|
||||
|
||||
${this._mode === 'nunit' ? `[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]` : '[TestClass]'}
|
||||
public class Tests : PageTest
|
||||
{`);
|
||||
const formattedContextOptions = formatContextOptions(options.contextOptions, options.deviceName);
|
||||
if (formattedContextOptions) {
|
||||
formatter.add(`public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return ${formattedContextOptions};
|
||||
}`);
|
||||
formatter.newLine();
|
||||
}
|
||||
formatter.add(` [${this._mode === 'nunit' ? 'Test' : 'TestMethod'}]
|
||||
public async Task MyTest()
|
||||
{`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
generateFooter(saveStorage) {
|
||||
const offset = this._mode === 'library' ? '' : ' ';
|
||||
let storageStateLine = saveStorage ? `\n${offset}await context.StorageStateAsync(new BrowserContextStorageStateOptions\n${offset}{\n${offset} Path = ${quote(saveStorage)}\n${offset}});\n` : '';
|
||||
if (this._mode !== 'library') storageStateLine += ` }\n}\n`;
|
||||
return storageStateLine;
|
||||
}
|
||||
}
|
||||
exports.CSharpLanguageGenerator = CSharpLanguageGenerator;
|
||||
function formatObject(value, indent = ' ', name = '') {
|
||||
if (typeof value === 'string') {
|
||||
if (['permissions', 'colorScheme', 'modifiers', 'button', 'recordHarContent', 'recordHarMode', 'serviceWorkers'].includes(name)) return `${getClassName(name)}.${toPascal(value)}`;
|
||||
return quote(value);
|
||||
}
|
||||
if (Array.isArray(value)) return `new[] { ${value.map(o => formatObject(o, indent, name)).join(', ')} }`;
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
||||
if (!keys.length) return name ? `new ${getClassName(name)}` : '';
|
||||
const tokens = [];
|
||||
for (const key of keys) {
|
||||
const property = getPropertyName(key);
|
||||
tokens.push(`${property} = ${formatObject(value[key], indent, key)},`);
|
||||
}
|
||||
if (name) return `new ${getClassName(name)}\n{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;
|
||||
return `{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;
|
||||
}
|
||||
if (name === 'latitude' || name === 'longitude') return String(value) + 'm';
|
||||
return String(value);
|
||||
}
|
||||
function getClassName(value) {
|
||||
switch (value) {
|
||||
case 'viewport':
|
||||
return 'ViewportSize';
|
||||
case 'proxy':
|
||||
return 'ProxySettings';
|
||||
case 'permissions':
|
||||
return 'ContextPermission';
|
||||
case 'modifiers':
|
||||
return 'KeyboardModifier';
|
||||
case 'button':
|
||||
return 'MouseButton';
|
||||
case 'recordHarMode':
|
||||
return 'HarMode';
|
||||
case 'recordHarContent':
|
||||
return 'HarContentPolicy';
|
||||
case 'serviceWorkers':
|
||||
return 'ServiceWorkerPolicy';
|
||||
default:
|
||||
return toPascal(value);
|
||||
}
|
||||
}
|
||||
function getPropertyName(key) {
|
||||
switch (key) {
|
||||
case 'storageState':
|
||||
return 'StorageStatePath';
|
||||
case 'viewport':
|
||||
return 'ViewportSize';
|
||||
default:
|
||||
return toPascal(key);
|
||||
}
|
||||
}
|
||||
function toPascal(value) {
|
||||
return value[0].toUpperCase() + value.slice(1);
|
||||
}
|
||||
function formatContextOptions(contextOptions, deviceName) {
|
||||
let options = {
|
||||
...contextOptions
|
||||
};
|
||||
// recordHAR is replaced with routeFromHAR in the generated code.
|
||||
delete options.recordHar;
|
||||
const device = deviceName && _deviceDescriptors.deviceDescriptors[deviceName];
|
||||
if (!device) {
|
||||
if (!Object.entries(options).length) return '';
|
||||
return formatObject(options, ' ', 'BrowserNewContextOptions');
|
||||
}
|
||||
options = (0, _language.sanitizeDeviceOptions)(device, options);
|
||||
if (!Object.entries(options).length) return `playwright.Devices[${quote(deviceName)}]`;
|
||||
return formatObject(options, ' ', `BrowserNewContextOptions(playwright.Devices[${quote(deviceName)}])`);
|
||||
}
|
||||
class CSharpFormatter {
|
||||
constructor(offset = 0) {
|
||||
this._baseIndent = void 0;
|
||||
this._baseOffset = void 0;
|
||||
this._lines = [];
|
||||
this._baseIndent = ' '.repeat(4);
|
||||
this._baseOffset = ' '.repeat(offset);
|
||||
}
|
||||
prepend(text) {
|
||||
this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);
|
||||
}
|
||||
add(text) {
|
||||
this._lines.push(...text.trim().split('\n').map(line => line.trim()));
|
||||
}
|
||||
newLine() {
|
||||
this._lines.push('');
|
||||
}
|
||||
format() {
|
||||
let spaces = '';
|
||||
let previousLine = '';
|
||||
return this._lines.map(line => {
|
||||
if (line === '') return line;
|
||||
if (line.startsWith('}') || line.startsWith(']') || line.includes('});') || line === ');') spaces = spaces.substring(this._baseIndent.length);
|
||||
const extraSpaces = /^(for|while|if).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';
|
||||
previousLine = line;
|
||||
line = spaces + extraSpaces + line;
|
||||
if (line.endsWith('{') || line.endsWith('[') || line.endsWith('(')) spaces += this._baseIndent;
|
||||
if (line.endsWith('));')) spaces = spaces.substring(this._baseIndent.length);
|
||||
return this._baseOffset + line;
|
||||
}).join('\n');
|
||||
}
|
||||
}
|
||||
function quote(text) {
|
||||
return (0, _utils.escapeWithQuotes)(text, '\"');
|
||||
}
|
||||
232
node_modules/playwright-core/lib/server/codegen/java.js
generated
vendored
Normal file
232
node_modules/playwright-core/lib/server/codegen/java.js
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.JavaLanguageGenerator = void 0;
|
||||
var _language = require("./language");
|
||||
var _deviceDescriptors = require("../deviceDescriptors");
|
||||
var _javascript = require("./javascript");
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JavaLanguageGenerator {
|
||||
constructor(mode) {
|
||||
this.id = void 0;
|
||||
this.groupName = 'Java';
|
||||
this.name = void 0;
|
||||
this.highlighter = 'java';
|
||||
this._mode = void 0;
|
||||
if (mode === 'library') {
|
||||
this.name = 'Library';
|
||||
this.id = 'java';
|
||||
} else if (mode === 'junit') {
|
||||
this.name = 'JUnit';
|
||||
this.id = 'java-junit';
|
||||
} else {
|
||||
throw new Error(`Unknown Java language mode: ${mode}`);
|
||||
}
|
||||
this._mode = mode;
|
||||
}
|
||||
generateAction(actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
const pageAlias = actionInContext.frame.pageAlias;
|
||||
const offset = this._mode === 'junit' ? 4 : 6;
|
||||
const formatter = new _javascript.JavaScriptFormatter(offset);
|
||||
if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage')) return '';
|
||||
if (action.name === 'openPage') {
|
||||
formatter.add(`Page ${pageAlias} = context.newPage();`);
|
||||
if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`${pageAlias}.navigate(${quote(action.url)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector, false)}.contentFrame()`);
|
||||
const subject = `${pageAlias}${locators.join('')}`;
|
||||
const signals = (0, _language.toSignalMap)(action);
|
||||
if (signals.dialog) {
|
||||
formatter.add(` ${pageAlias}.onceDialog(dialog -> {
|
||||
System.out.println(String.format("Dialog message: %s", dialog.message()));
|
||||
dialog.dismiss();
|
||||
});`);
|
||||
}
|
||||
let code = this._generateActionCall(subject, actionInContext, !!actionInContext.frame.framePath.length);
|
||||
if (signals.popup) {
|
||||
code = `Page ${signals.popup.popupAlias} = ${pageAlias}.waitForPopup(() -> {
|
||||
${code}
|
||||
});`;
|
||||
}
|
||||
if (signals.download) {
|
||||
code = `Download download${signals.download.downloadAlias} = ${pageAlias}.waitForDownload(() -> {
|
||||
${code}
|
||||
});`;
|
||||
}
|
||||
formatter.add(code);
|
||||
return formatter.format();
|
||||
}
|
||||
_generateActionCall(subject, actionInContext, inFrameLocator) {
|
||||
const action = actionInContext.action;
|
||||
switch (action.name) {
|
||||
case 'openPage':
|
||||
throw Error('Not reached');
|
||||
case 'closePage':
|
||||
return `${subject}.close();`;
|
||||
case 'click':
|
||||
{
|
||||
let method = 'click';
|
||||
if (action.clickCount === 2) method = 'dblclick';
|
||||
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
||||
const optionsText = formatClickOptions(options);
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.${method}(${optionsText});`;
|
||||
}
|
||||
case 'check':
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.check();`;
|
||||
case 'uncheck':
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.uncheck();`;
|
||||
case 'fill':
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.fill(${quote(action.text)});`;
|
||||
case 'setInputFiles':
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.setInputFiles(${formatPath(action.files.length === 1 ? action.files[0] : action.files)});`;
|
||||
case 'press':
|
||||
{
|
||||
const modifiers = (0, _language.toKeyboardModifiers)(action.modifiers);
|
||||
const shortcut = [...modifiers, action.key].join('+');
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.press(${quote(shortcut)});`;
|
||||
}
|
||||
case 'navigate':
|
||||
return `${subject}.navigate(${quote(action.url)});`;
|
||||
case 'select':
|
||||
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.selectOption(${formatSelectOption(action.options.length === 1 ? action.options[0] : action.options)});`;
|
||||
case 'assertText':
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${action.substring ? 'containsText' : 'hasText'}(${quote(action.text)});`;
|
||||
case 'assertChecked':
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)})${action.checked ? '' : '.not()'}.isChecked();`;
|
||||
case 'assertVisible':
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).isVisible();`;
|
||||
case 'assertValue':
|
||||
{
|
||||
const assertion = action.value ? `hasValue(${quote(action.value)})` : `isEmpty()`;
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`;
|
||||
}
|
||||
case 'assertSnapshot':
|
||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).matchesAriaSnapshot(${quote(action.snapshot)});`;
|
||||
}
|
||||
}
|
||||
_asLocator(selector, inFrameLocator) {
|
||||
return (0, _utils.asLocator)('java', selector, inFrameLocator);
|
||||
}
|
||||
generateHeader(options) {
|
||||
const formatter = new _javascript.JavaScriptFormatter();
|
||||
if (this._mode === 'junit') {
|
||||
formatter.add(`
|
||||
import com.microsoft.playwright.junit.UsePlaywright;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import org.junit.jupiter.api.*;
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.*;
|
||||
|
||||
@UsePlaywright
|
||||
public class TestExample {
|
||||
@Test
|
||||
void test(Page page) {`);
|
||||
return formatter.format();
|
||||
}
|
||||
formatter.add(`
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import java.util.*;
|
||||
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.${options.browserName}().launch(${formatLaunchOptions(options.launchOptions)});
|
||||
BrowserContext context = browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` context.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
generateFooter(saveStorage) {
|
||||
const storageStateLine = saveStorage ? `\n context.storageState(new BrowserContext.StorageStateOptions().setPath(${quote(saveStorage)}));\n` : '';
|
||||
if (this._mode === 'junit') {
|
||||
return `${storageStateLine} }
|
||||
}`;
|
||||
}
|
||||
return `${storageStateLine} }
|
||||
}
|
||||
}`;
|
||||
}
|
||||
}
|
||||
exports.JavaLanguageGenerator = JavaLanguageGenerator;
|
||||
function formatPath(files) {
|
||||
if (Array.isArray(files)) {
|
||||
if (files.length === 0) return 'new Path[0]';
|
||||
return `new Path[] {${files.map(s => 'Paths.get(' + quote(s) + ')').join(', ')}}`;
|
||||
}
|
||||
return `Paths.get(${quote(files)})`;
|
||||
}
|
||||
function formatSelectOption(options) {
|
||||
if (Array.isArray(options)) {
|
||||
if (options.length === 0) return 'new String[0]';
|
||||
return `new String[] {${options.map(s => quote(s)).join(', ')}}`;
|
||||
}
|
||||
return quote(options);
|
||||
}
|
||||
function formatLaunchOptions(options) {
|
||||
const lines = [];
|
||||
if (!Object.keys(options).filter(key => options[key] !== undefined).length) return '';
|
||||
lines.push('new BrowserType.LaunchOptions()');
|
||||
if (options.channel) lines.push(` .setChannel(${quote(options.channel)})`);
|
||||
if (typeof options.headless === 'boolean') lines.push(` .setHeadless(false)`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
function formatContextOptions(contextOptions, deviceName) {
|
||||
const lines = [];
|
||||
if (!Object.keys(contextOptions).length && !deviceName) return '';
|
||||
const device = deviceName ? _deviceDescriptors.deviceDescriptors[deviceName] : {};
|
||||
const options = {
|
||||
...device,
|
||||
...contextOptions
|
||||
};
|
||||
lines.push('new Browser.NewContextOptions()');
|
||||
if (options.acceptDownloads) lines.push(` .setAcceptDownloads(true)`);
|
||||
if (options.bypassCSP) lines.push(` .setBypassCSP(true)`);
|
||||
if (options.colorScheme) lines.push(` .setColorScheme(ColorScheme.${options.colorScheme.toUpperCase()})`);
|
||||
if (options.deviceScaleFactor) lines.push(` .setDeviceScaleFactor(${options.deviceScaleFactor})`);
|
||||
if (options.geolocation) lines.push(` .setGeolocation(${options.geolocation.latitude}, ${options.geolocation.longitude})`);
|
||||
if (options.hasTouch) lines.push(` .setHasTouch(${options.hasTouch})`);
|
||||
if (options.isMobile) lines.push(` .setIsMobile(${options.isMobile})`);
|
||||
if (options.locale) lines.push(` .setLocale(${quote(options.locale)})`);
|
||||
if (options.proxy) lines.push(` .setProxy(new Proxy(${quote(options.proxy.server)}))`);
|
||||
if (options.serviceWorkers) lines.push(` .setServiceWorkers(ServiceWorkerPolicy.${options.serviceWorkers.toUpperCase()})`);
|
||||
if (options.storageState) lines.push(` .setStorageStatePath(Paths.get(${quote(options.storageState)}))`);
|
||||
if (options.timezoneId) lines.push(` .setTimezoneId(${quote(options.timezoneId)})`);
|
||||
if (options.userAgent) lines.push(` .setUserAgent(${quote(options.userAgent)})`);
|
||||
if (options.viewport) lines.push(` .setViewportSize(${options.viewport.width}, ${options.viewport.height})`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
function formatClickOptions(options) {
|
||||
const lines = [];
|
||||
if (options.button) lines.push(` .setButton(MouseButton.${options.button.toUpperCase()})`);
|
||||
if (options.modifiers) lines.push(` .setModifiers(Arrays.asList(${options.modifiers.map(m => `KeyboardModifier.${m.toUpperCase()}`).join(', ')}))`);
|
||||
if (options.clickCount) lines.push(` .setClickCount(${options.clickCount})`);
|
||||
if (options.position) lines.push(` .setPosition(${options.position.x}, ${options.position.y})`);
|
||||
if (!lines.length) return '';
|
||||
lines.unshift(`new Locator.ClickOptions()`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
function quote(text) {
|
||||
return (0, _utils.escapeWithQuotes)(text, '\"');
|
||||
}
|
||||
245
node_modules/playwright-core/lib/server/codegen/javascript.js
generated
vendored
Normal file
245
node_modules/playwright-core/lib/server/codegen/javascript.js
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.JavaScriptLanguageGenerator = exports.JavaScriptFormatter = void 0;
|
||||
exports.quoteMultiline = quoteMultiline;
|
||||
var _language = require("./language");
|
||||
var _deviceDescriptors = require("../deviceDescriptors");
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JavaScriptLanguageGenerator {
|
||||
constructor(isTest) {
|
||||
this.id = void 0;
|
||||
this.groupName = 'Node.js';
|
||||
this.name = void 0;
|
||||
this.highlighter = 'javascript';
|
||||
this._isTest = void 0;
|
||||
this.id = isTest ? 'playwright-test' : 'javascript';
|
||||
this.name = isTest ? 'Test Runner' : 'Library';
|
||||
this._isTest = isTest;
|
||||
}
|
||||
generateAction(actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
if (this._isTest && (action.name === 'openPage' || action.name === 'closePage')) return '';
|
||||
const pageAlias = actionInContext.frame.pageAlias;
|
||||
const formatter = new JavaScriptFormatter(2);
|
||||
if (action.name === 'openPage') {
|
||||
formatter.add(`const ${pageAlias} = await context.newPage();`);
|
||||
if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`await ${pageAlias}.goto(${quote(action.url)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.contentFrame()`);
|
||||
const subject = `${pageAlias}${locators.join('')}`;
|
||||
const signals = (0, _language.toSignalMap)(action);
|
||||
if (signals.dialog) {
|
||||
formatter.add(` ${pageAlias}.once('dialog', dialog => {
|
||||
console.log(\`Dialog message: $\{dialog.message()}\`);
|
||||
dialog.dismiss().catch(() => {});
|
||||
});`);
|
||||
}
|
||||
if (signals.popup) formatter.add(`const ${signals.popup.popupAlias}Promise = ${pageAlias}.waitForEvent('popup');`);
|
||||
if (signals.download) formatter.add(`const download${signals.download.downloadAlias}Promise = ${pageAlias}.waitForEvent('download');`);
|
||||
formatter.add(wrapWithStep(actionInContext.description, this._generateActionCall(subject, actionInContext)));
|
||||
if (signals.popup) formatter.add(`const ${signals.popup.popupAlias} = await ${signals.popup.popupAlias}Promise;`);
|
||||
if (signals.download) formatter.add(`const download${signals.download.downloadAlias} = await download${signals.download.downloadAlias}Promise;`);
|
||||
return formatter.format();
|
||||
}
|
||||
_generateActionCall(subject, actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
switch (action.name) {
|
||||
case 'openPage':
|
||||
throw Error('Not reached');
|
||||
case 'closePage':
|
||||
return `await ${subject}.close();`;
|
||||
case 'click':
|
||||
{
|
||||
let method = 'click';
|
||||
if (action.clickCount === 2) method = 'dblclick';
|
||||
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
||||
const optionsString = formatOptions(options, false);
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.${method}(${optionsString});`;
|
||||
}
|
||||
case 'check':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.check();`;
|
||||
case 'uncheck':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.uncheck();`;
|
||||
case 'fill':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.fill(${quote(action.text)});`;
|
||||
case 'setInputFiles':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.setInputFiles(${formatObject(action.files.length === 1 ? action.files[0] : action.files)});`;
|
||||
case 'press':
|
||||
{
|
||||
const modifiers = (0, _language.toKeyboardModifiers)(action.modifiers);
|
||||
const shortcut = [...modifiers, action.key].join('+');
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.press(${quote(shortcut)});`;
|
||||
}
|
||||
case 'navigate':
|
||||
return `await ${subject}.goto(${quote(action.url)});`;
|
||||
case 'select':
|
||||
return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length === 1 ? action.options[0] : action.options)});`;
|
||||
case 'assertText':
|
||||
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'toContainText' : 'toHaveText'}(${quote(action.text)});`;
|
||||
case 'assertChecked':
|
||||
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)})${action.checked ? '' : '.not'}.toBeChecked();`;
|
||||
case 'assertVisible':
|
||||
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).toBeVisible();`;
|
||||
case 'assertValue':
|
||||
{
|
||||
const assertion = action.value ? `toHaveValue(${quote(action.value)})` : `toBeEmpty()`;
|
||||
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||
}
|
||||
case 'assertSnapshot':
|
||||
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).toMatchAriaSnapshot(${quoteMultiline(action.snapshot)});`;
|
||||
}
|
||||
}
|
||||
_asLocator(selector) {
|
||||
return (0, _utils.asLocator)('javascript', selector);
|
||||
}
|
||||
generateHeader(options) {
|
||||
if (this._isTest) return this.generateTestHeader(options);
|
||||
return this.generateStandaloneHeader(options);
|
||||
}
|
||||
generateFooter(saveStorage) {
|
||||
if (this._isTest) return this.generateTestFooter(saveStorage);
|
||||
return this.generateStandaloneFooter(saveStorage);
|
||||
}
|
||||
generateTestHeader(options) {
|
||||
const formatter = new JavaScriptFormatter();
|
||||
const useText = formatContextOptions(options.contextOptions, options.deviceName, this._isTest);
|
||||
formatter.add(`
|
||||
import { test, expect${options.deviceName ? ', devices' : ''} } from '@playwright/test';
|
||||
${useText ? '\ntest.use(' + useText + ');\n' : ''}
|
||||
test('test', async ({ page }) => {`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` await page.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
generateTestFooter(saveStorage) {
|
||||
return `});`;
|
||||
}
|
||||
generateStandaloneHeader(options) {
|
||||
const formatter = new JavaScriptFormatter();
|
||||
formatter.add(`
|
||||
const { ${options.browserName}${options.deviceName ? ', devices' : ''} } = require('playwright');
|
||||
|
||||
(async () => {
|
||||
const browser = await ${options.browserName}.launch(${formatObjectOrVoid(options.launchOptions)});
|
||||
const context = await browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName, false)});`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` await context.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
generateStandaloneFooter(saveStorage) {
|
||||
const storageStateLine = saveStorage ? `\n await context.storageState({ path: ${quote(saveStorage)} });` : '';
|
||||
return `\n // ---------------------${storageStateLine}
|
||||
await context.close();
|
||||
await browser.close();
|
||||
})();`;
|
||||
}
|
||||
}
|
||||
exports.JavaScriptLanguageGenerator = JavaScriptLanguageGenerator;
|
||||
function formatOptions(value, hasArguments) {
|
||||
const keys = Object.keys(value);
|
||||
if (!keys.length) return '';
|
||||
return (hasArguments ? ', ' : '') + formatObject(value);
|
||||
}
|
||||
function formatObject(value, indent = ' ') {
|
||||
if (typeof value === 'string') return quote(value);
|
||||
if (Array.isArray(value)) return `[${value.map(o => formatObject(o)).join(', ')}]`;
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
||||
if (!keys.length) return '{}';
|
||||
const tokens = [];
|
||||
for (const key of keys) tokens.push(`${key}: ${formatObject(value[key])}`);
|
||||
return `{\n${indent}${tokens.join(`,\n${indent}`)}\n}`;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
function formatObjectOrVoid(value, indent = ' ') {
|
||||
const result = formatObject(value, indent);
|
||||
return result === '{}' ? '' : result;
|
||||
}
|
||||
function formatContextOptions(options, deviceName, isTest) {
|
||||
const device = deviceName && _deviceDescriptors.deviceDescriptors[deviceName];
|
||||
// recordHAR is replaced with routeFromHAR in the generated code.
|
||||
options = {
|
||||
...options,
|
||||
recordHar: undefined
|
||||
};
|
||||
if (!device) return formatObjectOrVoid(options);
|
||||
// Filter out all the properties from the device descriptor.
|
||||
let serializedObject = formatObjectOrVoid((0, _language.sanitizeDeviceOptions)(device, options));
|
||||
// When there are no additional context options, we still want to spread the device inside.
|
||||
if (!serializedObject) serializedObject = '{\n}';
|
||||
const lines = serializedObject.split('\n');
|
||||
lines.splice(1, 0, `...devices[${quote(deviceName)}],`);
|
||||
return lines.join('\n');
|
||||
}
|
||||
class JavaScriptFormatter {
|
||||
constructor(offset = 0) {
|
||||
this._baseIndent = void 0;
|
||||
this._baseOffset = void 0;
|
||||
this._lines = [];
|
||||
this._baseIndent = ' '.repeat(2);
|
||||
this._baseOffset = ' '.repeat(offset);
|
||||
}
|
||||
prepend(text) {
|
||||
const trim = isMultilineString(text) ? line => line : line => line.trim();
|
||||
this._lines = text.trim().split('\n').map(trim).concat(this._lines);
|
||||
}
|
||||
add(text) {
|
||||
const trim = isMultilineString(text) ? line => line : line => line.trim();
|
||||
this._lines.push(...text.trim().split('\n').map(trim));
|
||||
}
|
||||
newLine() {
|
||||
this._lines.push('');
|
||||
}
|
||||
format() {
|
||||
let spaces = '';
|
||||
let previousLine = '';
|
||||
return this._lines.map(line => {
|
||||
if (line === '') return line;
|
||||
if (line.startsWith('}') || line.startsWith(']')) spaces = spaces.substring(this._baseIndent.length);
|
||||
const extraSpaces = /^(for|while|if|try).*\(.*\)$/.test(previousLine) ? this._baseIndent : '';
|
||||
previousLine = line;
|
||||
const callCarryOver = line.startsWith('.set');
|
||||
line = spaces + extraSpaces + (callCarryOver ? this._baseIndent : '') + line;
|
||||
if (line.endsWith('{') || line.endsWith('[')) spaces += this._baseIndent;
|
||||
return this._baseOffset + line;
|
||||
}).join('\n');
|
||||
}
|
||||
}
|
||||
exports.JavaScriptFormatter = JavaScriptFormatter;
|
||||
function quote(text) {
|
||||
return (0, _utils.escapeWithQuotes)(text, '\'');
|
||||
}
|
||||
function wrapWithStep(description, body) {
|
||||
return description ? `await test.step(\`${description}\`, async () => {
|
||||
${body}
|
||||
});` : body;
|
||||
}
|
||||
function quoteMultiline(text, indent = ' ') {
|
||||
const escape = text => text.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
||||
const lines = text.split('\n');
|
||||
if (lines.length === 1) return '`' + escape(text) + '`';
|
||||
return '`\n' + lines.map(line => indent + escape(line).replace(/\${/g, '\\${')).join('\n') + `\n${indent}\``;
|
||||
}
|
||||
function isMultilineString(text) {
|
||||
var _text$match;
|
||||
return (_text$match = text.match(/`[\S\s]*`/)) === null || _text$match === void 0 ? void 0 : _text$match[0].includes('\n');
|
||||
}
|
||||
47
node_modules/playwright-core/lib/server/codegen/jsonl.js
generated
vendored
Normal file
47
node_modules/playwright-core/lib/server/codegen/jsonl.js
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.JsonlLanguageGenerator = void 0;
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JsonlLanguageGenerator {
|
||||
constructor() {
|
||||
this.id = 'jsonl';
|
||||
this.groupName = '';
|
||||
this.name = 'JSONL';
|
||||
this.highlighter = 'javascript';
|
||||
}
|
||||
generateAction(actionInContext) {
|
||||
const locator = actionInContext.action.selector ? JSON.parse((0, _utils.asLocator)('jsonl', actionInContext.action.selector)) : undefined;
|
||||
const entry = {
|
||||
...actionInContext.action,
|
||||
pageAlias: actionInContext.frame.pageAlias,
|
||||
locator
|
||||
};
|
||||
return JSON.stringify(entry);
|
||||
}
|
||||
generateHeader(options) {
|
||||
return JSON.stringify(options);
|
||||
}
|
||||
generateFooter(saveStorage) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
exports.JsonlLanguageGenerator = JsonlLanguageGenerator;
|
||||
88
node_modules/playwright-core/lib/server/codegen/language.js
generated
vendored
Normal file
88
node_modules/playwright-core/lib/server/codegen/language.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.fromKeyboardModifiers = fromKeyboardModifiers;
|
||||
exports.generateCode = generateCode;
|
||||
exports.sanitizeDeviceOptions = sanitizeDeviceOptions;
|
||||
exports.toClickOptionsForSourceCode = toClickOptionsForSourceCode;
|
||||
exports.toKeyboardModifiers = toKeyboardModifiers;
|
||||
exports.toSignalMap = toSignalMap;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function generateCode(actions, languageGenerator, options) {
|
||||
const header = languageGenerator.generateHeader(options);
|
||||
const footer = languageGenerator.generateFooter(options.saveStorage);
|
||||
const actionTexts = actions.map(a => languageGenerator.generateAction(a)).filter(Boolean);
|
||||
const text = [header, ...actionTexts, footer].join('\n');
|
||||
return {
|
||||
header,
|
||||
footer,
|
||||
actionTexts,
|
||||
text
|
||||
};
|
||||
}
|
||||
function sanitizeDeviceOptions(device, options) {
|
||||
// Filter out all the properties from the device descriptor.
|
||||
const cleanedOptions = {};
|
||||
for (const property in options) {
|
||||
if (JSON.stringify(device[property]) !== JSON.stringify(options[property])) cleanedOptions[property] = options[property];
|
||||
}
|
||||
return cleanedOptions;
|
||||
}
|
||||
function toSignalMap(action) {
|
||||
let popup;
|
||||
let download;
|
||||
let dialog;
|
||||
for (const signal of action.signals) {
|
||||
if (signal.name === 'popup') popup = signal;else if (signal.name === 'download') download = signal;else if (signal.name === 'dialog') dialog = signal;
|
||||
}
|
||||
return {
|
||||
popup,
|
||||
download,
|
||||
dialog
|
||||
};
|
||||
}
|
||||
function toKeyboardModifiers(modifiers) {
|
||||
const result = [];
|
||||
if (modifiers & 1) result.push('Alt');
|
||||
if (modifiers & 2) result.push('ControlOrMeta');
|
||||
if (modifiers & 4) result.push('ControlOrMeta');
|
||||
if (modifiers & 8) result.push('Shift');
|
||||
return result;
|
||||
}
|
||||
function fromKeyboardModifiers(modifiers) {
|
||||
let result = 0;
|
||||
if (!modifiers) return result;
|
||||
if (modifiers.includes('Alt')) result |= 1;
|
||||
if (modifiers.includes('Control')) result |= 2;
|
||||
if (modifiers.includes('ControlOrMeta')) result |= 2;
|
||||
if (modifiers.includes('Meta')) result |= 4;
|
||||
if (modifiers.includes('Shift')) result |= 8;
|
||||
return result;
|
||||
}
|
||||
function toClickOptionsForSourceCode(action) {
|
||||
const modifiers = toKeyboardModifiers(action.modifiers);
|
||||
const options = {};
|
||||
if (action.button !== 'left') options.button = action.button;
|
||||
if (modifiers.length) options.modifiers = modifiers;
|
||||
// Do not render clickCount === 2 for dblclick.
|
||||
if (action.clickCount > 2) options.clickCount = action.clickCount;
|
||||
if (action.position) options.position = action.position;
|
||||
return options;
|
||||
}
|
||||
30
node_modules/playwright-core/lib/server/codegen/languages.js
generated
vendored
Normal file
30
node_modules/playwright-core/lib/server/codegen/languages.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.languageSet = languageSet;
|
||||
var _java = require("./java");
|
||||
var _javascript = require("./javascript");
|
||||
var _jsonl = require("./jsonl");
|
||||
var _csharp = require("./csharp");
|
||||
var _python = require("./python");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function languageSet() {
|
||||
return new Set([new _java.JavaLanguageGenerator('junit'), new _java.JavaLanguageGenerator('library'), new _javascript.JavaScriptLanguageGenerator( /* isPlaywrightTest */false), new _javascript.JavaScriptLanguageGenerator( /* isPlaywrightTest */true), new _python.PythonLanguageGenerator( /* isAsync */false, /* isPytest */true), new _python.PythonLanguageGenerator( /* isAsync */false, /* isPytest */false), new _python.PythonLanguageGenerator( /* isAsync */true, /* isPytest */false), new _csharp.CSharpLanguageGenerator('mstest'), new _csharp.CSharpLanguageGenerator('nunit'), new _csharp.CSharpLanguageGenerator('library'), new _jsonl.JsonlLanguageGenerator()]);
|
||||
}
|
||||
261
node_modules/playwright-core/lib/server/codegen/python.js
generated
vendored
Normal file
261
node_modules/playwright-core/lib/server/codegen/python.js
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.PythonLanguageGenerator = void 0;
|
||||
var _language = require("./language");
|
||||
var _utils = require("../../utils");
|
||||
var _deviceDescriptors = require("../deviceDescriptors");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PythonLanguageGenerator {
|
||||
constructor(isAsync, isPyTest) {
|
||||
this.id = void 0;
|
||||
this.groupName = 'Python';
|
||||
this.name = void 0;
|
||||
this.highlighter = 'python';
|
||||
this._awaitPrefix = void 0;
|
||||
this._asyncPrefix = void 0;
|
||||
this._isAsync = void 0;
|
||||
this._isPyTest = void 0;
|
||||
this.id = isPyTest ? 'python-pytest' : isAsync ? 'python-async' : 'python';
|
||||
this.name = isPyTest ? 'Pytest' : isAsync ? 'Library Async' : 'Library';
|
||||
this._isAsync = isAsync;
|
||||
this._isPyTest = isPyTest;
|
||||
this._awaitPrefix = isAsync ? 'await ' : '';
|
||||
this._asyncPrefix = isAsync ? 'async ' : '';
|
||||
}
|
||||
generateAction(actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
if (this._isPyTest && (action.name === 'openPage' || action.name === 'closePage')) return '';
|
||||
const pageAlias = actionInContext.frame.pageAlias;
|
||||
const formatter = new PythonFormatter(4);
|
||||
if (action.name === 'openPage') {
|
||||
formatter.add(`${pageAlias} = ${this._awaitPrefix}context.new_page()`);
|
||||
if (action.url && action.url !== 'about:blank' && action.url !== 'chrome://newtab/') formatter.add(`${this._awaitPrefix}${pageAlias}.goto(${quote(action.url)})`);
|
||||
return formatter.format();
|
||||
}
|
||||
const locators = actionInContext.frame.framePath.map(selector => `.${this._asLocator(selector)}.content_frame`);
|
||||
const subject = `${pageAlias}${locators.join('')}`;
|
||||
const signals = (0, _language.toSignalMap)(action);
|
||||
if (signals.dialog) formatter.add(` ${pageAlias}.once("dialog", lambda dialog: dialog.dismiss())`);
|
||||
let code = `${this._awaitPrefix}${this._generateActionCall(subject, actionInContext)}`;
|
||||
if (signals.popup) {
|
||||
code = `${this._asyncPrefix}with ${pageAlias}.expect_popup() as ${signals.popup.popupAlias}_info {
|
||||
${code}
|
||||
}
|
||||
${signals.popup.popupAlias} = ${this._awaitPrefix}${signals.popup.popupAlias}_info.value`;
|
||||
}
|
||||
if (signals.download) {
|
||||
code = `${this._asyncPrefix}with ${pageAlias}.expect_download() as download${signals.download.downloadAlias}_info {
|
||||
${code}
|
||||
}
|
||||
download${signals.download.downloadAlias} = ${this._awaitPrefix}download${signals.download.downloadAlias}_info.value`;
|
||||
}
|
||||
formatter.add(code);
|
||||
return formatter.format();
|
||||
}
|
||||
_generateActionCall(subject, actionInContext) {
|
||||
const action = actionInContext.action;
|
||||
switch (action.name) {
|
||||
case 'openPage':
|
||||
throw Error('Not reached');
|
||||
case 'closePage':
|
||||
return `${subject}.close()`;
|
||||
case 'click':
|
||||
{
|
||||
let method = 'click';
|
||||
if (action.clickCount === 2) method = 'dblclick';
|
||||
const options = (0, _language.toClickOptionsForSourceCode)(action);
|
||||
const optionsString = formatOptions(options, false);
|
||||
return `${subject}.${this._asLocator(action.selector)}.${method}(${optionsString})`;
|
||||
}
|
||||
case 'check':
|
||||
return `${subject}.${this._asLocator(action.selector)}.check()`;
|
||||
case 'uncheck':
|
||||
return `${subject}.${this._asLocator(action.selector)}.uncheck()`;
|
||||
case 'fill':
|
||||
return `${subject}.${this._asLocator(action.selector)}.fill(${quote(action.text)})`;
|
||||
case 'setInputFiles':
|
||||
return `${subject}.${this._asLocator(action.selector)}.set_input_files(${formatValue(action.files.length === 1 ? action.files[0] : action.files)})`;
|
||||
case 'press':
|
||||
{
|
||||
const modifiers = (0, _language.toKeyboardModifiers)(action.modifiers);
|
||||
const shortcut = [...modifiers, action.key].join('+');
|
||||
return `${subject}.${this._asLocator(action.selector)}.press(${quote(shortcut)})`;
|
||||
}
|
||||
case 'navigate':
|
||||
return `${subject}.goto(${quote(action.url)})`;
|
||||
case 'select':
|
||||
return `${subject}.${this._asLocator(action.selector)}.select_option(${formatValue(action.options.length === 1 ? action.options[0] : action.options)})`;
|
||||
case 'assertText':
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? 'to_contain_text' : 'to_have_text'}(${quote(action.text)})`;
|
||||
case 'assertChecked':
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).${action.checked ? 'to_be_checked()' : 'not_to_be_checked()'}`;
|
||||
case 'assertVisible':
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).to_be_visible()`;
|
||||
case 'assertValue':
|
||||
{
|
||||
const assertion = action.value ? `to_have_value(${quote(action.value)})` : `to_be_empty()`;
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||
}
|
||||
case 'assertSnapshot':
|
||||
return `expect(${subject}.${this._asLocator(action.selector)}).to_match_aria_snapshot(${quote(action.snapshot)})`;
|
||||
}
|
||||
}
|
||||
_asLocator(selector) {
|
||||
return (0, _utils.asLocator)('python', selector);
|
||||
}
|
||||
generateHeader(options) {
|
||||
const formatter = new PythonFormatter();
|
||||
if (this._isPyTest) {
|
||||
const contextOptions = formatContextOptions(options.contextOptions, options.deviceName, true /* asDict */);
|
||||
const fixture = contextOptions ? `
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def browser_context_args(browser_context_args, playwright) {
|
||||
return {${contextOptions}}
|
||||
}
|
||||
` : '';
|
||||
formatter.add(`${options.deviceName ? 'import pytest\n' : ''}import re
|
||||
from playwright.sync_api import Page, expect
|
||||
${fixture}
|
||||
|
||||
def test_example(page: Page) -> None {`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` page.route_from_har(${quote(options.contextOptions.recordHar.path)})`);
|
||||
} else if (this._isAsync) {
|
||||
formatter.add(`
|
||||
import asyncio
|
||||
import re
|
||||
from playwright.async_api import Playwright, async_playwright, expect
|
||||
|
||||
|
||||
async def run(playwright: Playwright) -> None {
|
||||
browser = await playwright.${options.browserName}.launch(${formatOptions(options.launchOptions, false)})
|
||||
context = await browser.new_context(${formatContextOptions(options.contextOptions, options.deviceName)})`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` await page.route_from_har(${quote(options.contextOptions.recordHar.path)})`);
|
||||
} else {
|
||||
formatter.add(`
|
||||
import re
|
||||
from playwright.sync_api import Playwright, sync_playwright, expect
|
||||
|
||||
|
||||
def run(playwright: Playwright) -> None {
|
||||
browser = playwright.${options.browserName}.launch(${formatOptions(options.launchOptions, false)})
|
||||
context = browser.new_context(${formatContextOptions(options.contextOptions, options.deviceName)})`);
|
||||
if (options.contextOptions.recordHar) formatter.add(` context.route_from_har(${quote(options.contextOptions.recordHar.path)})`);
|
||||
}
|
||||
return formatter.format();
|
||||
}
|
||||
generateFooter(saveStorage) {
|
||||
if (this._isPyTest) {
|
||||
return '';
|
||||
} else if (this._isAsync) {
|
||||
const storageStateLine = saveStorage ? `\n await context.storage_state(path=${quote(saveStorage)})` : '';
|
||||
return `\n # ---------------------${storageStateLine}
|
||||
await context.close()
|
||||
await browser.close()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with async_playwright() as playwright:
|
||||
await run(playwright)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
`;
|
||||
} else {
|
||||
const storageStateLine = saveStorage ? `\n context.storage_state(path=${quote(saveStorage)})` : '';
|
||||
return `\n # ---------------------${storageStateLine}
|
||||
context.close()
|
||||
browser.close()
|
||||
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.PythonLanguageGenerator = PythonLanguageGenerator;
|
||||
function formatValue(value) {
|
||||
if (value === false) return 'False';
|
||||
if (value === true) return 'True';
|
||||
if (value === undefined) return 'None';
|
||||
if (Array.isArray(value)) return `[${value.map(formatValue).join(', ')}]`;
|
||||
if (typeof value === 'string') return quote(value);
|
||||
if (typeof value === 'object') return JSON.stringify(value);
|
||||
return String(value);
|
||||
}
|
||||
function formatOptions(value, hasArguments, asDict) {
|
||||
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
|
||||
if (!keys.length) return '';
|
||||
return (hasArguments ? ', ' : '') + keys.map(key => {
|
||||
if (asDict) return `"${(0, _utils.toSnakeCase)(key)}": ${formatValue(value[key])}`;
|
||||
return `${(0, _utils.toSnakeCase)(key)}=${formatValue(value[key])}`;
|
||||
}).join(', ');
|
||||
}
|
||||
function formatContextOptions(options, deviceName, asDict) {
|
||||
// recordHAR is replaced with routeFromHAR in the generated code.
|
||||
options = {
|
||||
...options,
|
||||
recordHar: undefined
|
||||
};
|
||||
const device = deviceName && _deviceDescriptors.deviceDescriptors[deviceName];
|
||||
if (!device) return formatOptions(options, false, asDict);
|
||||
return `**playwright.devices[${quote(deviceName)}]` + formatOptions((0, _language.sanitizeDeviceOptions)(device, options), true, asDict);
|
||||
}
|
||||
class PythonFormatter {
|
||||
constructor(offset = 0) {
|
||||
this._baseIndent = void 0;
|
||||
this._baseOffset = void 0;
|
||||
this._lines = [];
|
||||
this._baseIndent = ' '.repeat(4);
|
||||
this._baseOffset = ' '.repeat(offset);
|
||||
}
|
||||
prepend(text) {
|
||||
this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);
|
||||
}
|
||||
add(text) {
|
||||
this._lines.push(...text.trim().split('\n').map(line => line.trim()));
|
||||
}
|
||||
newLine() {
|
||||
this._lines.push('');
|
||||
}
|
||||
format() {
|
||||
let spaces = '';
|
||||
const lines = [];
|
||||
this._lines.forEach(line => {
|
||||
if (line === '') return lines.push(line);
|
||||
if (line === '}') {
|
||||
spaces = spaces.substring(this._baseIndent.length);
|
||||
return;
|
||||
}
|
||||
line = spaces + line;
|
||||
if (line.endsWith('{')) {
|
||||
spaces += this._baseIndent;
|
||||
line = line.substring(0, line.length - 1).trimEnd() + ':';
|
||||
}
|
||||
return lines.push(this._baseOffset + line);
|
||||
});
|
||||
return lines.join('\n');
|
||||
}
|
||||
}
|
||||
function quote(text) {
|
||||
return (0, _utils.escapeWithQuotes)(text, '\"');
|
||||
}
|
||||
5
node_modules/playwright-core/lib/server/codegen/types.js
generated
vendored
Normal file
5
node_modules/playwright-core/lib/server/codegen/types.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
57
node_modules/playwright-core/lib/server/console.js
generated
vendored
Normal file
57
node_modules/playwright-core/lib/server/console.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ConsoleMessage = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ConsoleMessage {
|
||||
constructor(page, type, text, args, location) {
|
||||
this._type = void 0;
|
||||
this._text = void 0;
|
||||
this._args = void 0;
|
||||
this._location = void 0;
|
||||
this._page = void 0;
|
||||
this._page = page;
|
||||
this._type = type;
|
||||
this._text = text;
|
||||
this._args = args;
|
||||
this._location = location || {
|
||||
url: '',
|
||||
lineNumber: 0,
|
||||
columnNumber: 0
|
||||
};
|
||||
}
|
||||
page() {
|
||||
return this._page;
|
||||
}
|
||||
type() {
|
||||
return this._type;
|
||||
}
|
||||
text() {
|
||||
if (this._text === undefined) this._text = this._args.map(arg => arg.preview()).join(' ');
|
||||
return this._text;
|
||||
}
|
||||
args() {
|
||||
return this._args;
|
||||
}
|
||||
location() {
|
||||
return this._location;
|
||||
}
|
||||
}
|
||||
exports.ConsoleMessage = ConsoleMessage;
|
||||
185
node_modules/playwright-core/lib/server/cookieStore.js
generated
vendored
Normal file
185
node_modules/playwright-core/lib/server/cookieStore.js
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CookieStore = void 0;
|
||||
exports.domainMatches = domainMatches;
|
||||
exports.parseRawCookie = parseRawCookie;
|
||||
var _network = require("./network");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Cookie {
|
||||
constructor(data) {
|
||||
this._raw = void 0;
|
||||
this._raw = data;
|
||||
}
|
||||
name() {
|
||||
return this._raw.name;
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.4
|
||||
matches(url) {
|
||||
if (this._raw.secure && url.protocol !== 'https:' && url.hostname !== 'localhost') return false;
|
||||
if (!domainMatches(url.hostname, this._raw.domain)) return false;
|
||||
if (!pathMatches(url.pathname, this._raw.path)) return false;
|
||||
return true;
|
||||
}
|
||||
equals(other) {
|
||||
return this._raw.name === other._raw.name && this._raw.domain === other._raw.domain && this._raw.path === other._raw.path;
|
||||
}
|
||||
networkCookie() {
|
||||
return this._raw;
|
||||
}
|
||||
updateExpiresFrom(other) {
|
||||
this._raw.expires = other._raw.expires;
|
||||
}
|
||||
expired() {
|
||||
if (this._raw.expires === -1) return false;
|
||||
return this._raw.expires * 1000 < Date.now();
|
||||
}
|
||||
}
|
||||
class CookieStore {
|
||||
constructor() {
|
||||
this._nameToCookies = new Map();
|
||||
}
|
||||
addCookies(cookies) {
|
||||
for (const cookie of cookies) this._addCookie(new Cookie(cookie));
|
||||
}
|
||||
cookies(url) {
|
||||
const result = [];
|
||||
for (const cookie of this._cookiesIterator()) {
|
||||
if (cookie.matches(url)) result.push(cookie.networkCookie());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
allCookies() {
|
||||
const result = [];
|
||||
for (const cookie of this._cookiesIterator()) result.push(cookie.networkCookie());
|
||||
return result;
|
||||
}
|
||||
_addCookie(cookie) {
|
||||
let set = this._nameToCookies.get(cookie.name());
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
this._nameToCookies.set(cookie.name(), set);
|
||||
}
|
||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.3
|
||||
for (const other of set) {
|
||||
if (other.equals(cookie)) set.delete(other);
|
||||
}
|
||||
set.add(cookie);
|
||||
CookieStore.pruneExpired(set);
|
||||
}
|
||||
*_cookiesIterator() {
|
||||
for (const [name, cookies] of this._nameToCookies) {
|
||||
CookieStore.pruneExpired(cookies);
|
||||
for (const cookie of cookies) yield cookie;
|
||||
if (cookies.size === 0) this._nameToCookies.delete(name);
|
||||
}
|
||||
}
|
||||
static pruneExpired(cookies) {
|
||||
for (const cookie of cookies) {
|
||||
if (cookie.expired()) cookies.delete(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.CookieStore = CookieStore;
|
||||
function parseRawCookie(header) {
|
||||
const pairs = header.split(';').filter(s => s.trim().length > 0).map(p => {
|
||||
let key = '';
|
||||
let value = '';
|
||||
const separatorPos = p.indexOf('=');
|
||||
if (separatorPos === -1) {
|
||||
// If only a key is specified, the value is left undefined.
|
||||
key = p.trim();
|
||||
} else {
|
||||
// Otherwise we assume that the key is the element before the first `=`
|
||||
key = p.slice(0, separatorPos).trim();
|
||||
// And the value is the rest of the string.
|
||||
value = p.slice(separatorPos + 1).trim();
|
||||
}
|
||||
return [key, value];
|
||||
});
|
||||
if (!pairs.length) return null;
|
||||
const [name, value] = pairs[0];
|
||||
const cookie = {
|
||||
name,
|
||||
value
|
||||
};
|
||||
for (let i = 1; i < pairs.length; i++) {
|
||||
const [name, value] = pairs[i];
|
||||
switch (name.toLowerCase()) {
|
||||
case 'expires':
|
||||
const expiresMs = +new Date(value);
|
||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.1
|
||||
if (isFinite(expiresMs)) {
|
||||
if (expiresMs <= 0) cookie.expires = 0;else cookie.expires = Math.min(expiresMs / 1000, _network.kMaxCookieExpiresDateInSeconds);
|
||||
}
|
||||
break;
|
||||
case 'max-age':
|
||||
const maxAgeSec = parseInt(value, 10);
|
||||
if (isFinite(maxAgeSec)) {
|
||||
// From https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.2
|
||||
// If delta-seconds is less than or equal to zero (0), let expiry-time
|
||||
// be the earliest representable date and time.
|
||||
if (maxAgeSec <= 0) cookie.expires = 0;else cookie.expires = Math.min(Date.now() / 1000 + maxAgeSec, _network.kMaxCookieExpiresDateInSeconds);
|
||||
}
|
||||
break;
|
||||
case 'domain':
|
||||
cookie.domain = value.toLocaleLowerCase() || '';
|
||||
if (cookie.domain && !cookie.domain.startsWith('.') && cookie.domain.includes('.')) cookie.domain = '.' + cookie.domain;
|
||||
break;
|
||||
case 'path':
|
||||
cookie.path = value || '';
|
||||
break;
|
||||
case 'secure':
|
||||
cookie.secure = true;
|
||||
break;
|
||||
case 'httponly':
|
||||
cookie.httpOnly = true;
|
||||
break;
|
||||
case 'samesite':
|
||||
switch (value.toLowerCase()) {
|
||||
case 'none':
|
||||
cookie.sameSite = 'None';
|
||||
break;
|
||||
case 'lax':
|
||||
cookie.sameSite = 'Lax';
|
||||
break;
|
||||
case 'strict':
|
||||
cookie.sameSite = 'Strict';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
function domainMatches(value, domain) {
|
||||
if (value === domain) return true;
|
||||
// Only strict match is allowed if domain doesn't start with '.' (host-only-flag is true in the spec)
|
||||
if (!domain.startsWith('.')) return false;
|
||||
value = '.' + value;
|
||||
return value.endsWith(domain);
|
||||
}
|
||||
function pathMatches(value, path) {
|
||||
if (value === path) return true;
|
||||
if (!value.endsWith('/')) value = value + '/';
|
||||
if (!path.endsWith('/')) path = path + '/';
|
||||
return value.startsWith(path);
|
||||
}
|
||||
237
node_modules/playwright-core/lib/server/debugController.js
generated
vendored
Normal file
237
node_modules/playwright-core/lib/server/debugController.js
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.DebugController = void 0;
|
||||
var _processLauncher = require("../utils/processLauncher");
|
||||
var _instrumentation = require("./instrumentation");
|
||||
var _recorder = require("./recorder");
|
||||
var _recorderApp = require("./recorder/recorderApp");
|
||||
var _utils = require("../utils");
|
||||
var _ariaSnapshot = require("./ariaSnapshot");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const internalMetadata = (0, _instrumentation.serverSideCallMetadata)();
|
||||
class DebugController extends _instrumentation.SdkObject {
|
||||
constructor(playwright) {
|
||||
super({
|
||||
attribution: {
|
||||
isInternalPlaywright: true
|
||||
},
|
||||
instrumentation: (0, _instrumentation.createInstrumentation)()
|
||||
}, undefined, 'DebugController');
|
||||
this._autoCloseTimer = void 0;
|
||||
// TODO: remove in 1.27
|
||||
this._autoCloseAllowed = false;
|
||||
this._trackHierarchyListener = void 0;
|
||||
this._playwright = void 0;
|
||||
this._sdkLanguage = 'javascript';
|
||||
this._codegenId = 'playwright-test';
|
||||
this._playwright = playwright;
|
||||
}
|
||||
initialize(codegenId, sdkLanguage) {
|
||||
this._codegenId = codegenId;
|
||||
this._sdkLanguage = sdkLanguage;
|
||||
}
|
||||
setAutoCloseAllowed(allowed) {
|
||||
this._autoCloseAllowed = allowed;
|
||||
}
|
||||
dispose() {
|
||||
this.setReportStateChanged(false);
|
||||
this.setAutoCloseAllowed(false);
|
||||
}
|
||||
setReportStateChanged(enabled) {
|
||||
if (enabled && !this._trackHierarchyListener) {
|
||||
this._trackHierarchyListener = {
|
||||
onPageOpen: () => this._emitSnapshot(),
|
||||
onPageClose: () => this._emitSnapshot()
|
||||
};
|
||||
this._playwright.instrumentation.addListener(this._trackHierarchyListener, null);
|
||||
} else if (!enabled && this._trackHierarchyListener) {
|
||||
this._playwright.instrumentation.removeListener(this._trackHierarchyListener);
|
||||
this._trackHierarchyListener = undefined;
|
||||
}
|
||||
}
|
||||
async resetForReuse() {
|
||||
const contexts = new Set();
|
||||
for (const page of this._playwright.allPages()) contexts.add(page.context());
|
||||
for (const context of contexts) await context.resetForReuse(internalMetadata, null);
|
||||
}
|
||||
async navigate(url) {
|
||||
for (const p of this._playwright.allPages()) await p.mainFrame().goto(internalMetadata, url);
|
||||
}
|
||||
async setRecorderMode(params) {
|
||||
// TODO: |file| is only used in the legacy mode.
|
||||
await this._closeBrowsersWithoutPages();
|
||||
if (params.mode === 'none') {
|
||||
for (const recorder of await this._allRecorders()) {
|
||||
recorder.hideHighlightedSelector();
|
||||
recorder.setMode('none');
|
||||
}
|
||||
this.setAutoCloseEnabled(true);
|
||||
return;
|
||||
}
|
||||
if (!this._playwright.allBrowsers().length) await this._playwright.chromium.launch(internalMetadata, {
|
||||
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS
|
||||
});
|
||||
// Create page if none.
|
||||
const pages = this._playwright.allPages();
|
||||
if (!pages.length) {
|
||||
const [browser] = this._playwright.allBrowsers();
|
||||
const {
|
||||
context
|
||||
} = await browser.newContextForReuse({}, internalMetadata);
|
||||
await context.newPage(internalMetadata);
|
||||
}
|
||||
// Update test id attribute.
|
||||
if (params.testIdAttributeName) {
|
||||
for (const page of this._playwright.allPages()) page.context().selectors().setTestIdAttributeName(params.testIdAttributeName);
|
||||
}
|
||||
// Toggle the mode.
|
||||
for (const recorder of await this._allRecorders()) {
|
||||
recorder.hideHighlightedSelector();
|
||||
if (params.mode !== 'inspecting') recorder.setOutput(this._codegenId, params.file);
|
||||
recorder.setMode(params.mode);
|
||||
}
|
||||
this.setAutoCloseEnabled(true);
|
||||
}
|
||||
async setAutoCloseEnabled(enabled) {
|
||||
if (!this._autoCloseAllowed) return;
|
||||
if (this._autoCloseTimer) clearTimeout(this._autoCloseTimer);
|
||||
if (!enabled) return;
|
||||
const heartBeat = () => {
|
||||
if (!this._playwright.allPages().length) (0, _processLauncher.gracefullyProcessExitDoNotHang)(0);else this._autoCloseTimer = setTimeout(heartBeat, 5000);
|
||||
};
|
||||
this._autoCloseTimer = setTimeout(heartBeat, 30000);
|
||||
}
|
||||
async highlight(params) {
|
||||
for (const recorder of await this._allRecorders()) {
|
||||
if (params.ariaTemplate) recorder.setHighlightedAriaTemplate((0, _ariaSnapshot.parseYamlForAriaSnapshot)(params.ariaTemplate));else if (params.selector) recorder.setHighlightedSelector(this._sdkLanguage, params.selector);
|
||||
}
|
||||
}
|
||||
async hideHighlight() {
|
||||
// Hide all active recorder highlights.
|
||||
for (const recorder of await this._allRecorders()) recorder.hideHighlightedSelector();
|
||||
// Hide all locator.highlight highlights.
|
||||
await this._playwright.hideHighlight();
|
||||
}
|
||||
allBrowsers() {
|
||||
return [...this._playwright.allBrowsers()];
|
||||
}
|
||||
async resume() {
|
||||
for (const recorder of await this._allRecorders()) recorder.resume();
|
||||
}
|
||||
async kill() {
|
||||
(0, _processLauncher.gracefullyProcessExitDoNotHang)(0);
|
||||
}
|
||||
async closeAllBrowsers() {
|
||||
await Promise.all(this.allBrowsers().map(browser => browser.close({
|
||||
reason: 'Close all browsers requested'
|
||||
})));
|
||||
}
|
||||
_emitSnapshot() {
|
||||
const browsers = [];
|
||||
let pageCount = 0;
|
||||
for (const browser of this._playwright.allBrowsers()) {
|
||||
const b = {
|
||||
contexts: []
|
||||
};
|
||||
browsers.push(b);
|
||||
for (const context of browser.contexts()) {
|
||||
const c = {
|
||||
pages: []
|
||||
};
|
||||
b.contexts.push(c);
|
||||
for (const page of context.pages()) c.pages.push(page.mainFrame().url());
|
||||
pageCount += context.pages().length;
|
||||
}
|
||||
}
|
||||
this.emit(DebugController.Events.StateChanged, {
|
||||
pageCount
|
||||
});
|
||||
}
|
||||
async _allRecorders() {
|
||||
const contexts = new Set();
|
||||
for (const page of this._playwright.allPages()) contexts.add(page.context());
|
||||
const result = await Promise.all([...contexts].map(c => _recorder.Recorder.showInspector(c, {
|
||||
omitCallTracking: true
|
||||
}, () => Promise.resolve(new InspectingRecorderApp(this)))));
|
||||
return result.filter(Boolean);
|
||||
}
|
||||
async _closeBrowsersWithoutPages() {
|
||||
for (const browser of this._playwright.allBrowsers()) {
|
||||
for (const context of browser.contexts()) {
|
||||
if (!context.pages().length) await context.close({
|
||||
reason: 'Browser collected'
|
||||
});
|
||||
}
|
||||
if (!browser.contexts()) await browser.close({
|
||||
reason: 'Browser collected'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.DebugController = DebugController;
|
||||
DebugController.Events = {
|
||||
StateChanged: 'stateChanged',
|
||||
InspectRequested: 'inspectRequested',
|
||||
SourceChanged: 'sourceChanged',
|
||||
Paused: 'paused',
|
||||
SetModeRequested: 'setModeRequested'
|
||||
};
|
||||
class InspectingRecorderApp extends _recorderApp.EmptyRecorderApp {
|
||||
constructor(debugController) {
|
||||
super();
|
||||
this._debugController = void 0;
|
||||
this._debugController = debugController;
|
||||
}
|
||||
async elementPicked(elementInfo) {
|
||||
const locator = (0, _utils.asLocator)(this._debugController._sdkLanguage, elementInfo.selector);
|
||||
this._debugController.emit(DebugController.Events.InspectRequested, {
|
||||
selector: elementInfo.selector,
|
||||
locator
|
||||
});
|
||||
}
|
||||
async setSources(sources) {
|
||||
const source = sources.find(s => s.id === this._debugController._codegenId);
|
||||
const {
|
||||
text,
|
||||
header,
|
||||
footer,
|
||||
actions
|
||||
} = source || {
|
||||
text: ''
|
||||
};
|
||||
this._debugController.emit(DebugController.Events.SourceChanged, {
|
||||
text,
|
||||
header,
|
||||
footer,
|
||||
actions
|
||||
});
|
||||
}
|
||||
async setPaused(paused) {
|
||||
this._debugController.emit(DebugController.Events.Paused, {
|
||||
paused
|
||||
});
|
||||
}
|
||||
async setMode(mode) {
|
||||
this._debugController.emit(DebugController.Events.SetModeRequested, {
|
||||
mode
|
||||
});
|
||||
}
|
||||
}
|
||||
132
node_modules/playwright-core/lib/server/debugger.js
generated
vendored
Normal file
132
node_modules/playwright-core/lib/server/debugger.js
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Debugger = void 0;
|
||||
exports.shouldSlowMo = shouldSlowMo;
|
||||
var _events = require("events");
|
||||
var _utils = require("../utils");
|
||||
var _browserContext = require("./browserContext");
|
||||
var _debug = require("../protocol/debug");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const symbol = Symbol('Debugger');
|
||||
class Debugger extends _events.EventEmitter {
|
||||
constructor(context) {
|
||||
super();
|
||||
this._pauseOnNextStatement = false;
|
||||
this._pausedCallsMetadata = new Map();
|
||||
this._enabled = void 0;
|
||||
this._context = void 0;
|
||||
this._muted = false;
|
||||
this._slowMo = void 0;
|
||||
this._context = context;
|
||||
this._context[symbol] = this;
|
||||
this._enabled = (0, _utils.debugMode)() === 'inspector';
|
||||
if (this._enabled) this.pauseOnNextStatement();
|
||||
context.instrumentation.addListener(this, context);
|
||||
this._context.once(_browserContext.BrowserContext.Events.Close, () => {
|
||||
this._context.instrumentation.removeListener(this);
|
||||
});
|
||||
this._slowMo = this._context._browser.options.slowMo;
|
||||
}
|
||||
async setMuted(muted) {
|
||||
this._muted = muted;
|
||||
}
|
||||
async onBeforeCall(sdkObject, metadata) {
|
||||
if (this._muted) return;
|
||||
if (shouldPauseOnCall(sdkObject, metadata) || this._pauseOnNextStatement && shouldPauseBeforeStep(metadata)) await this.pause(sdkObject, metadata);
|
||||
}
|
||||
async _doSlowMo() {
|
||||
await new Promise(f => setTimeout(f, this._slowMo));
|
||||
}
|
||||
async onAfterCall(sdkObject, metadata) {
|
||||
if (this._slowMo && shouldSlowMo(metadata)) await this._doSlowMo();
|
||||
}
|
||||
async onBeforeInputAction(sdkObject, metadata) {
|
||||
if (this._muted) return;
|
||||
if (this._enabled && this._pauseOnNextStatement) await this.pause(sdkObject, metadata);
|
||||
}
|
||||
async pause(sdkObject, metadata) {
|
||||
if (this._muted) return;
|
||||
this._enabled = true;
|
||||
metadata.pauseStartTime = (0, _utils.monotonicTime)();
|
||||
const result = new Promise(resolve => {
|
||||
this._pausedCallsMetadata.set(metadata, {
|
||||
resolve,
|
||||
sdkObject
|
||||
});
|
||||
});
|
||||
this.emit(Debugger.Events.PausedStateChanged);
|
||||
return result;
|
||||
}
|
||||
resume(step) {
|
||||
if (!this.isPaused()) return;
|
||||
this._pauseOnNextStatement = step;
|
||||
const endTime = (0, _utils.monotonicTime)();
|
||||
for (const [metadata, {
|
||||
resolve
|
||||
}] of this._pausedCallsMetadata) {
|
||||
metadata.pauseEndTime = endTime;
|
||||
resolve();
|
||||
}
|
||||
this._pausedCallsMetadata.clear();
|
||||
this.emit(Debugger.Events.PausedStateChanged);
|
||||
}
|
||||
pauseOnNextStatement() {
|
||||
this._pauseOnNextStatement = true;
|
||||
}
|
||||
isPaused(metadata) {
|
||||
if (metadata) return this._pausedCallsMetadata.has(metadata);
|
||||
return !!this._pausedCallsMetadata.size;
|
||||
}
|
||||
pausedDetails() {
|
||||
const result = [];
|
||||
for (const [metadata, {
|
||||
sdkObject
|
||||
}] of this._pausedCallsMetadata) result.push({
|
||||
metadata,
|
||||
sdkObject
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.Debugger = Debugger;
|
||||
Debugger.Events = {
|
||||
PausedStateChanged: 'pausedstatechanged'
|
||||
};
|
||||
function shouldPauseOnCall(sdkObject, metadata) {
|
||||
var _sdkObject$attributio;
|
||||
if (sdkObject.attribution.playwright.options.isServer) return false;
|
||||
if (!((_sdkObject$attributio = sdkObject.attribution.browser) !== null && _sdkObject$attributio !== void 0 && _sdkObject$attributio.options.headful) && !(0, _utils.isUnderTest)()) return false;
|
||||
return metadata.method === 'pause';
|
||||
}
|
||||
function shouldPauseBeforeStep(metadata) {
|
||||
// Don't stop on internal.
|
||||
if (!metadata.apiName) return false;
|
||||
// Always stop on 'close'
|
||||
if (metadata.method === 'close') return true;
|
||||
if (metadata.method === 'waitForSelector' || metadata.method === 'waitForEventInfo') return false; // Never stop on those, primarily for the test harness.
|
||||
const step = metadata.type + '.' + metadata.method;
|
||||
// Stop before everything that generates snapshot. But don't stop before those marked as pausesBeforeInputActions
|
||||
// since we stop in them on a separate instrumentation signal.
|
||||
return _debug.commandsWithTracingSnapshots.has(step) && !_debug.pausesBeforeInputActions.has(metadata.type + '.' + metadata.method);
|
||||
}
|
||||
function shouldSlowMo(metadata) {
|
||||
return _debug.slowMoActions.has(metadata.type + '.' + metadata.method);
|
||||
}
|
||||
26
node_modules/playwright-core/lib/server/deviceDescriptors.js
generated
vendored
Normal file
26
node_modules/playwright-core/lib/server/deviceDescriptors.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.deviceDescriptors = void 0;
|
||||
var _deviceDescriptorsSource = _interopRequireDefault(require("./deviceDescriptorsSource.json"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const deviceDescriptors = exports.deviceDescriptors = _deviceDescriptorsSource.default;
|
||||
1669
node_modules/playwright-core/lib/server/deviceDescriptorsSource.json
generated
vendored
Normal file
1669
node_modules/playwright-core/lib/server/deviceDescriptorsSource.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
71
node_modules/playwright-core/lib/server/dialog.js
generated
vendored
Normal file
71
node_modules/playwright-core/lib/server/dialog.js
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Dialog = void 0;
|
||||
var _utils = require("../utils");
|
||||
var _instrumentation = require("./instrumentation");
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Dialog extends _instrumentation.SdkObject {
|
||||
constructor(page, type, message, onHandle, defaultValue) {
|
||||
super(page, 'dialog');
|
||||
this._page = void 0;
|
||||
this._type = void 0;
|
||||
this._message = void 0;
|
||||
this._onHandle = void 0;
|
||||
this._handled = false;
|
||||
this._defaultValue = void 0;
|
||||
this._page = page;
|
||||
this._type = type;
|
||||
this._message = message;
|
||||
this._onHandle = onHandle;
|
||||
this._defaultValue = defaultValue || '';
|
||||
this._page._frameManager.dialogDidOpen(this);
|
||||
this.instrumentation.onDialog(this);
|
||||
}
|
||||
page() {
|
||||
return this._page;
|
||||
}
|
||||
type() {
|
||||
return this._type;
|
||||
}
|
||||
message() {
|
||||
return this._message;
|
||||
}
|
||||
defaultValue() {
|
||||
return this._defaultValue;
|
||||
}
|
||||
async accept(promptText) {
|
||||
(0, _utils.assert)(!this._handled, 'Cannot accept dialog which is already handled!');
|
||||
this._handled = true;
|
||||
this._page._frameManager.dialogWillClose(this);
|
||||
await this._onHandle(true, promptText);
|
||||
}
|
||||
async dismiss() {
|
||||
(0, _utils.assert)(!this._handled, 'Cannot dismiss dialog which is already handled!');
|
||||
this._handled = true;
|
||||
this._page._frameManager.dialogWillClose(this);
|
||||
await this._onHandle(false);
|
||||
}
|
||||
async close() {
|
||||
if (this._type === 'beforeunload') await this.accept();else await this.dismiss();
|
||||
}
|
||||
}
|
||||
exports.Dialog = Dialog;
|
||||
204
node_modules/playwright-core/lib/server/dispatchers/androidDispatcher.js
generated
vendored
Normal file
204
node_modules/playwright-core/lib/server/dispatchers/androidDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.AndroidSocketDispatcher = exports.AndroidDispatcher = exports.AndroidDeviceDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _android = require("../android/android");
|
||||
var _browserContextDispatcher = require("./browserContextDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class AndroidDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, android) {
|
||||
super(scope, android, 'Android', {});
|
||||
this._type_Android = true;
|
||||
}
|
||||
async devices(params) {
|
||||
const devices = await this._object.devices(params);
|
||||
return {
|
||||
devices: devices.map(d => AndroidDeviceDispatcher.from(this, d))
|
||||
};
|
||||
}
|
||||
async setDefaultTimeoutNoReply(params) {
|
||||
this._object.setDefaultTimeout(params.timeout);
|
||||
}
|
||||
}
|
||||
exports.AndroidDispatcher = AndroidDispatcher;
|
||||
class AndroidDeviceDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, device) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(device);
|
||||
return result || new AndroidDeviceDispatcher(scope, device);
|
||||
}
|
||||
constructor(scope, device) {
|
||||
super(scope, device, 'AndroidDevice', {
|
||||
model: device.model,
|
||||
serial: device.serial
|
||||
});
|
||||
this._type_EventTarget = true;
|
||||
this._type_AndroidDevice = true;
|
||||
for (const webView of device.webViews()) this._dispatchEvent('webViewAdded', {
|
||||
webView
|
||||
});
|
||||
this.addObjectListener(_android.AndroidDevice.Events.WebViewAdded, webView => this._dispatchEvent('webViewAdded', {
|
||||
webView
|
||||
}));
|
||||
this.addObjectListener(_android.AndroidDevice.Events.WebViewRemoved, socketName => this._dispatchEvent('webViewRemoved', {
|
||||
socketName
|
||||
}));
|
||||
this.addObjectListener(_android.AndroidDevice.Events.Close, socketName => this._dispatchEvent('close'));
|
||||
}
|
||||
async wait(params) {
|
||||
await this._object.send('wait', params);
|
||||
}
|
||||
async fill(params) {
|
||||
await this._object.send('click', {
|
||||
selector: params.selector
|
||||
});
|
||||
await this._object.send('fill', params);
|
||||
}
|
||||
async tap(params) {
|
||||
await this._object.send('click', params);
|
||||
}
|
||||
async drag(params) {
|
||||
await this._object.send('drag', params);
|
||||
}
|
||||
async fling(params) {
|
||||
await this._object.send('fling', params);
|
||||
}
|
||||
async longTap(params) {
|
||||
await this._object.send('longClick', params);
|
||||
}
|
||||
async pinchClose(params) {
|
||||
await this._object.send('pinchClose', params);
|
||||
}
|
||||
async pinchOpen(params) {
|
||||
await this._object.send('pinchOpen', params);
|
||||
}
|
||||
async scroll(params) {
|
||||
await this._object.send('scroll', params);
|
||||
}
|
||||
async swipe(params) {
|
||||
await this._object.send('swipe', params);
|
||||
}
|
||||
async info(params) {
|
||||
const info = await this._object.send('info', params);
|
||||
fixupAndroidElementInfo(info);
|
||||
return {
|
||||
info
|
||||
};
|
||||
}
|
||||
async inputType(params) {
|
||||
const text = params.text;
|
||||
const keyCodes = [];
|
||||
for (let i = 0; i < text.length; ++i) {
|
||||
const code = keyMap.get(text[i].toUpperCase());
|
||||
if (code === undefined) throw new Error('No mapping for ' + text[i] + ' found');
|
||||
keyCodes.push(code);
|
||||
}
|
||||
await Promise.all(keyCodes.map(keyCode => this._object.send('inputPress', {
|
||||
keyCode
|
||||
})));
|
||||
}
|
||||
async inputPress(params) {
|
||||
if (!keyMap.has(params.key)) throw new Error('Unknown key: ' + params.key);
|
||||
await this._object.send('inputPress', {
|
||||
keyCode: keyMap.get(params.key)
|
||||
});
|
||||
}
|
||||
async inputTap(params) {
|
||||
await this._object.send('inputClick', params);
|
||||
}
|
||||
async inputSwipe(params) {
|
||||
await this._object.send('inputSwipe', params);
|
||||
}
|
||||
async inputDrag(params) {
|
||||
await this._object.send('inputDrag', params);
|
||||
}
|
||||
async screenshot(params) {
|
||||
return {
|
||||
binary: await this._object.screenshot()
|
||||
};
|
||||
}
|
||||
async shell(params) {
|
||||
return {
|
||||
result: await this._object.shell(params.command)
|
||||
};
|
||||
}
|
||||
async open(params, metadata) {
|
||||
const socket = await this._object.open(params.command);
|
||||
return {
|
||||
socket: new AndroidSocketDispatcher(this, socket)
|
||||
};
|
||||
}
|
||||
async installApk(params) {
|
||||
await this._object.installApk(params.file, {
|
||||
args: params.args
|
||||
});
|
||||
}
|
||||
async push(params) {
|
||||
await this._object.push(params.file, params.path, params.mode);
|
||||
}
|
||||
async launchBrowser(params) {
|
||||
const context = await this._object.launchBrowser(params.pkg, params);
|
||||
return {
|
||||
context: new _browserContextDispatcher.BrowserContextDispatcher(this, context)
|
||||
};
|
||||
}
|
||||
async close(params) {
|
||||
await this._object.close();
|
||||
}
|
||||
async setDefaultTimeoutNoReply(params) {
|
||||
this._object.setDefaultTimeout(params.timeout);
|
||||
}
|
||||
async connectToWebView(params) {
|
||||
return {
|
||||
context: new _browserContextDispatcher.BrowserContextDispatcher(this, await this._object.connectToWebView(params.socketName))
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.AndroidDeviceDispatcher = AndroidDeviceDispatcher;
|
||||
class AndroidSocketDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, socket) {
|
||||
super(scope, socket, 'AndroidSocket', {});
|
||||
this._type_AndroidSocket = true;
|
||||
this.addObjectListener('data', data => this._dispatchEvent('data', {
|
||||
data
|
||||
}));
|
||||
this.addObjectListener('close', () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
}
|
||||
async write(params, metadata) {
|
||||
await this._object.write(params.data);
|
||||
}
|
||||
async close(params, metadata) {
|
||||
this._object.close();
|
||||
}
|
||||
}
|
||||
exports.AndroidSocketDispatcher = AndroidSocketDispatcher;
|
||||
const keyMap = new Map([['Unknown', 0], ['SoftLeft', 1], ['SoftRight', 2], ['Home', 3], ['Back', 4], ['Call', 5], ['EndCall', 6], ['0', 7], ['1', 8], ['2', 9], ['3', 10], ['4', 11], ['5', 12], ['6', 13], ['7', 14], ['8', 15], ['9', 16], ['Star', 17], ['*', 17], ['Pound', 18], ['#', 18], ['DialUp', 19], ['DialDown', 20], ['DialLeft', 21], ['DialRight', 22], ['DialCenter', 23], ['VolumeUp', 24], ['VolumeDown', 25], ['Power', 26], ['Camera', 27], ['Clear', 28], ['A', 29], ['B', 30], ['C', 31], ['D', 32], ['E', 33], ['F', 34], ['G', 35], ['H', 36], ['I', 37], ['J', 38], ['K', 39], ['L', 40], ['M', 41], ['N', 42], ['O', 43], ['P', 44], ['Q', 45], ['R', 46], ['S', 47], ['T', 48], ['U', 49], ['V', 50], ['W', 51], ['X', 52], ['Y', 53], ['Z', 54], ['Comma', 55], [',', 55], ['Period', 56], ['.', 56], ['AltLeft', 57], ['AltRight', 58], ['ShiftLeft', 59], ['ShiftRight', 60], ['Tab', 61], ['\t', 61], ['Space', 62], [' ', 62], ['Sym', 63], ['Explorer', 64], ['Envelop', 65], ['Enter', 66], ['Del', 67], ['Grave', 68], ['Minus', 69], ['-', 69], ['Equals', 70], ['=', 70], ['LeftBracket', 71], ['(', 71], ['RightBracket', 72], [')', 72], ['Backslash', 73], ['\\', 73], ['Semicolon', 74], [';', 74], ['Apostrophe', 75], ['`', 75], ['Slash', 76], ['/', 76], ['At', 77], ['@', 77], ['Num', 78], ['HeadsetHook', 79], ['Focus', 80], ['Plus', 81], ['Menu', 82], ['Notification', 83], ['Search', 84], ['ChannelUp', 166], ['ChannelDown', 167], ['AppSwitch', 187], ['Assist', 219], ['Cut', 277], ['Copy', 278], ['Paste', 279]]);
|
||||
function fixupAndroidElementInfo(info) {
|
||||
// Some of the properties are nullable, see https://developer.android.com/reference/androidx/test/uiautomator/UiObject2.
|
||||
info.clazz = info.clazz || '';
|
||||
info.pkg = info.pkg || '';
|
||||
info.res = info.res || '';
|
||||
info.desc = info.desc || '';
|
||||
info.text = info.text || '';
|
||||
for (const child of info.children || []) fixupAndroidElementInfo(child);
|
||||
}
|
||||
118
node_modules/playwright-core/lib/server/dispatchers/artifactDispatcher.js
generated
vendored
Normal file
118
node_modules/playwright-core/lib/server/dispatchers/artifactDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ArtifactDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _streamDispatcher = require("./streamDispatcher");
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _fileUtils = require("../../utils/fileUtils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ArtifactDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(parentScope, artifact) {
|
||||
return ArtifactDispatcher.fromNullable(parentScope, artifact);
|
||||
}
|
||||
static fromNullable(parentScope, artifact) {
|
||||
if (!artifact) return undefined;
|
||||
const result = (0, _dispatcher.existingDispatcher)(artifact);
|
||||
return result || new ArtifactDispatcher(parentScope, artifact);
|
||||
}
|
||||
constructor(scope, artifact) {
|
||||
super(scope, artifact, 'Artifact', {
|
||||
absolutePath: artifact.localPath()
|
||||
});
|
||||
this._type_Artifact = true;
|
||||
}
|
||||
async pathAfterFinished() {
|
||||
const path = await this._object.localPathAfterFinished();
|
||||
return {
|
||||
value: path
|
||||
};
|
||||
}
|
||||
async saveAs(params) {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this._object.saveAs(async (localPath, error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await (0, _fileUtils.mkdirIfNeeded)(params.path);
|
||||
await _fs.default.promises.copyFile(localPath, params.path);
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
async saveAsStream() {
|
||||
return await new Promise((resolve, reject) => {
|
||||
this._object.saveAs(async (localPath, error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const readable = _fs.default.createReadStream(localPath, {
|
||||
highWaterMark: 1024 * 1024
|
||||
});
|
||||
const stream = new _streamDispatcher.StreamDispatcher(this, readable);
|
||||
// Resolve with a stream, so that client starts saving the data.
|
||||
resolve({
|
||||
stream
|
||||
});
|
||||
// Block the Artifact until the stream is consumed.
|
||||
await new Promise(resolve => {
|
||||
readable.on('close', resolve);
|
||||
readable.on('end', resolve);
|
||||
readable.on('error', resolve);
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
async stream() {
|
||||
const fileName = await this._object.localPathAfterFinished();
|
||||
const readable = _fs.default.createReadStream(fileName, {
|
||||
highWaterMark: 1024 * 1024
|
||||
});
|
||||
return {
|
||||
stream: new _streamDispatcher.StreamDispatcher(this, readable)
|
||||
};
|
||||
}
|
||||
async failure() {
|
||||
const error = await this._object.failureError();
|
||||
return {
|
||||
error: error || undefined
|
||||
};
|
||||
}
|
||||
async cancel() {
|
||||
await this._object.cancel();
|
||||
}
|
||||
async delete(_, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
await this._object.delete();
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
exports.ArtifactDispatcher = ArtifactDispatcher;
|
||||
368
node_modules/playwright-core/lib/server/dispatchers/browserContextDispatcher.js
generated
vendored
Normal file
368
node_modules/playwright-core/lib/server/dispatchers/browserContextDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BrowserContextDispatcher = void 0;
|
||||
var _browserContext = require("../browserContext");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _pageDispatcher = require("./pageDispatcher");
|
||||
var _networkDispatchers = require("./networkDispatchers");
|
||||
var _crBrowser = require("../chromium/crBrowser");
|
||||
var _cdpSessionDispatcher = require("./cdpSessionDispatcher");
|
||||
var _recorder = require("../recorder");
|
||||
var _artifactDispatcher = require("./artifactDispatcher");
|
||||
var _tracingDispatcher = require("./tracingDispatcher");
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
var path = _interopRequireWildcard(require("path"));
|
||||
var _utils = require("../../utils");
|
||||
var _writableStreamDispatcher = require("./writableStreamDispatcher");
|
||||
var _dialogDispatcher = require("./dialogDispatcher");
|
||||
var _errors = require("../errors");
|
||||
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
|
||||
var _recorderInTraceViewer = require("../recorder/recorderInTraceViewer");
|
||||
var _recorderApp = require("../recorder/recorderApp");
|
||||
var _webSocketRouteDispatcher = require("./webSocketRouteDispatcher");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BrowserContextDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(parentScope, context) {
|
||||
// We will reparent these to the context below.
|
||||
const requestContext = _networkDispatchers.APIRequestContextDispatcher.from(parentScope, context.fetchRequest);
|
||||
const tracing = _tracingDispatcher.TracingDispatcher.from(parentScope, context.tracing);
|
||||
super(parentScope, context, 'BrowserContext', {
|
||||
isChromium: context._browser.options.isChromium,
|
||||
isLocalBrowserOnServer: context._browser._isCollocatedWithServer,
|
||||
requestContext,
|
||||
tracing
|
||||
});
|
||||
this._type_EventTarget = true;
|
||||
this._type_BrowserContext = true;
|
||||
this._context = void 0;
|
||||
this._subscriptions = new Set();
|
||||
this._webSocketInterceptionPatterns = [];
|
||||
this.adopt(requestContext);
|
||||
this.adopt(tracing);
|
||||
this._context = context;
|
||||
// Note: when launching persistent context, dispatcher is created very late,
|
||||
// so we can already have pages, videos and everything else.
|
||||
|
||||
const onVideo = artifact => {
|
||||
// Note: Video must outlive Page and BrowserContext, so that client can saveAs it
|
||||
// after closing the context. We use |scope| for it.
|
||||
const artifactDispatcher = _artifactDispatcher.ArtifactDispatcher.from(parentScope, artifact);
|
||||
this._dispatchEvent('video', {
|
||||
artifact: artifactDispatcher
|
||||
});
|
||||
};
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.VideoStarted, onVideo);
|
||||
for (const video of context._browser._idToVideo.values()) {
|
||||
if (video.context === context) onVideo(video.artifact);
|
||||
}
|
||||
for (const page of context.pages()) this._dispatchEvent('page', {
|
||||
page: _pageDispatcher.PageDispatcher.from(this, page)
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.Page, page => {
|
||||
this._dispatchEvent('page', {
|
||||
page: _pageDispatcher.PageDispatcher.from(this, page)
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.PageError, (error, page) => {
|
||||
this._dispatchEvent('pageError', {
|
||||
error: (0, _errors.serializeError)(error),
|
||||
page: _pageDispatcher.PageDispatcher.from(this, page)
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.Console, message => {
|
||||
const page = message.page();
|
||||
if (this._shouldDispatchEvent(page, 'console')) {
|
||||
const pageDispatcher = _pageDispatcher.PageDispatcher.from(this, page);
|
||||
this._dispatchEvent('console', {
|
||||
page: pageDispatcher,
|
||||
type: message.type(),
|
||||
text: message.text(),
|
||||
args: message.args().map(a => _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(pageDispatcher, a)),
|
||||
location: message.location()
|
||||
});
|
||||
}
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.Dialog, dialog => {
|
||||
if (this._shouldDispatchEvent(dialog.page(), 'dialog')) this._dispatchEvent('dialog', {
|
||||
dialog: new _dialogDispatcher.DialogDispatcher(this, dialog)
|
||||
});else dialog.close().catch(() => {});
|
||||
});
|
||||
if (context._browser.options.name === 'chromium') {
|
||||
for (const page of context.backgroundPages()) this._dispatchEvent('backgroundPage', {
|
||||
page: _pageDispatcher.PageDispatcher.from(this, page)
|
||||
});
|
||||
this.addObjectListener(_crBrowser.CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', {
|
||||
page: _pageDispatcher.PageDispatcher.from(this, page)
|
||||
}));
|
||||
for (const serviceWorker of context.serviceWorkers()) this._dispatchEvent('serviceWorker', {
|
||||
worker: new _pageDispatcher.WorkerDispatcher(this, serviceWorker)
|
||||
});
|
||||
this.addObjectListener(_crBrowser.CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', {
|
||||
worker: new _pageDispatcher.WorkerDispatcher(this, serviceWorker)
|
||||
}));
|
||||
}
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.Request, request => {
|
||||
var _request$frame;
|
||||
// Create dispatcher, if:
|
||||
// - There are listeners to the requests.
|
||||
// - We are redirected from a reported request so that redirectedTo was updated on client.
|
||||
// - We are a navigation request and dispatcher will be reported as a part of the goto return value and newDocument param anyways.
|
||||
// By the time requestFinished is triggered to update the request, we should have a request on the client already.
|
||||
const redirectFromDispatcher = request.redirectedFrom() && (0, _dispatcher.existingDispatcher)(request.redirectedFrom());
|
||||
if (!redirectFromDispatcher && !this._shouldDispatchNetworkEvent(request, 'request') && !request.isNavigationRequest()) return;
|
||||
const requestDispatcher = _networkDispatchers.RequestDispatcher.from(this, request);
|
||||
this._dispatchEvent('request', {
|
||||
request: requestDispatcher,
|
||||
page: _pageDispatcher.PageDispatcher.fromNullable(this, (_request$frame = request.frame()) === null || _request$frame === void 0 ? void 0 : _request$frame._page.initializedOrUndefined())
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.Response, response => {
|
||||
var _response$frame;
|
||||
const requestDispatcher = (0, _dispatcher.existingDispatcher)(response.request());
|
||||
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(response.request(), 'response')) return;
|
||||
this._dispatchEvent('response', {
|
||||
response: _networkDispatchers.ResponseDispatcher.from(this, response),
|
||||
page: _pageDispatcher.PageDispatcher.fromNullable(this, (_response$frame = response.frame()) === null || _response$frame === void 0 ? void 0 : _response$frame._page.initializedOrUndefined())
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.RequestFailed, request => {
|
||||
var _request$frame2;
|
||||
const requestDispatcher = (0, _dispatcher.existingDispatcher)(request);
|
||||
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFailed')) return;
|
||||
this._dispatchEvent('requestFailed', {
|
||||
request: _networkDispatchers.RequestDispatcher.from(this, request),
|
||||
failureText: request._failureText || undefined,
|
||||
responseEndTiming: request._responseEndTiming,
|
||||
page: _pageDispatcher.PageDispatcher.fromNullable(this, (_request$frame2 = request.frame()) === null || _request$frame2 === void 0 ? void 0 : _request$frame2._page.initializedOrUndefined())
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_browserContext.BrowserContext.Events.RequestFinished, ({
|
||||
request,
|
||||
response
|
||||
}) => {
|
||||
var _request$frame3;
|
||||
const requestDispatcher = (0, _dispatcher.existingDispatcher)(request);
|
||||
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFinished')) return;
|
||||
this._dispatchEvent('requestFinished', {
|
||||
request: _networkDispatchers.RequestDispatcher.from(this, request),
|
||||
response: _networkDispatchers.ResponseDispatcher.fromNullable(this, response),
|
||||
responseEndTiming: request._responseEndTiming,
|
||||
page: _pageDispatcher.PageDispatcher.fromNullable(this, (_request$frame3 = request.frame()) === null || _request$frame3 === void 0 ? void 0 : _request$frame3._page.initializedOrUndefined())
|
||||
});
|
||||
});
|
||||
}
|
||||
_shouldDispatchNetworkEvent(request, event) {
|
||||
var _request$frame4;
|
||||
return this._shouldDispatchEvent((_request$frame4 = request.frame()) === null || _request$frame4 === void 0 || (_request$frame4 = _request$frame4._page) === null || _request$frame4 === void 0 ? void 0 : _request$frame4.initializedOrUndefined(), event);
|
||||
}
|
||||
_shouldDispatchEvent(page, event) {
|
||||
if (this._subscriptions.has(event)) return true;
|
||||
const pageDispatcher = page ? (0, _dispatcher.existingDispatcher)(page) : undefined;
|
||||
if (pageDispatcher !== null && pageDispatcher !== void 0 && pageDispatcher._subscriptions.has(event)) return true;
|
||||
return false;
|
||||
}
|
||||
async createTempFiles(params) {
|
||||
const dir = this._context._browser.options.artifactsDir;
|
||||
const tmpDir = path.join(dir, 'upload-' + (0, _utils.createGuid)());
|
||||
const tempDirWithRootName = params.rootDirName ? path.join(tmpDir, path.basename(params.rootDirName)) : tmpDir;
|
||||
await fs.promises.mkdir(tempDirWithRootName, {
|
||||
recursive: true
|
||||
});
|
||||
this._context._tempDirs.push(tmpDir);
|
||||
return {
|
||||
rootDir: params.rootDirName ? new _writableStreamDispatcher.WritableStreamDispatcher(this, tempDirWithRootName) : undefined,
|
||||
writableStreams: await Promise.all(params.items.map(async item => {
|
||||
await fs.promises.mkdir(path.dirname(path.join(tempDirWithRootName, item.name)), {
|
||||
recursive: true
|
||||
});
|
||||
const file = fs.createWriteStream(path.join(tempDirWithRootName, item.name));
|
||||
return new _writableStreamDispatcher.WritableStreamDispatcher(this, file, item.lastModifiedMs);
|
||||
}))
|
||||
};
|
||||
}
|
||||
async setDefaultNavigationTimeoutNoReply(params) {
|
||||
this._context.setDefaultNavigationTimeout(params.timeout);
|
||||
}
|
||||
async setDefaultTimeoutNoReply(params) {
|
||||
this._context.setDefaultTimeout(params.timeout);
|
||||
}
|
||||
async exposeBinding(params) {
|
||||
await this._context.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => {
|
||||
// When reusing the context, we might have some bindings called late enough,
|
||||
// after context and page dispatchers have been disposed.
|
||||
if (this._disposed) return;
|
||||
const pageDispatcher = _pageDispatcher.PageDispatcher.from(this, source.page);
|
||||
const binding = new _pageDispatcher.BindingCallDispatcher(pageDispatcher, params.name, !!params.needsHandle, source, args);
|
||||
this._dispatchEvent('bindingCall', {
|
||||
binding
|
||||
});
|
||||
return binding.promise();
|
||||
});
|
||||
}
|
||||
async newPage(params, metadata) {
|
||||
return {
|
||||
page: _pageDispatcher.PageDispatcher.from(this, await this._context.newPage(metadata))
|
||||
};
|
||||
}
|
||||
async cookies(params) {
|
||||
return {
|
||||
cookies: await this._context.cookies(params.urls)
|
||||
};
|
||||
}
|
||||
async addCookies(params) {
|
||||
await this._context.addCookies(params.cookies);
|
||||
}
|
||||
async clearCookies(params) {
|
||||
const nameRe = params.nameRegexSource !== undefined && params.nameRegexFlags !== undefined ? new RegExp(params.nameRegexSource, params.nameRegexFlags) : undefined;
|
||||
const domainRe = params.domainRegexSource !== undefined && params.domainRegexFlags !== undefined ? new RegExp(params.domainRegexSource, params.domainRegexFlags) : undefined;
|
||||
const pathRe = params.pathRegexSource !== undefined && params.pathRegexFlags !== undefined ? new RegExp(params.pathRegexSource, params.pathRegexFlags) : undefined;
|
||||
await this._context.clearCookies({
|
||||
name: nameRe || params.name,
|
||||
domain: domainRe || params.domain,
|
||||
path: pathRe || params.path
|
||||
});
|
||||
}
|
||||
async grantPermissions(params) {
|
||||
await this._context.grantPermissions(params.permissions, params.origin);
|
||||
}
|
||||
async clearPermissions() {
|
||||
await this._context.clearPermissions();
|
||||
}
|
||||
async setGeolocation(params) {
|
||||
await this._context.setGeolocation(params.geolocation);
|
||||
}
|
||||
async setExtraHTTPHeaders(params) {
|
||||
await this._context.setExtraHTTPHeaders(params.headers);
|
||||
}
|
||||
async setOffline(params) {
|
||||
await this._context.setOffline(params.offline);
|
||||
}
|
||||
async setHTTPCredentials(params) {
|
||||
await this._context.setHTTPCredentials(params.httpCredentials);
|
||||
}
|
||||
async addInitScript(params) {
|
||||
await this._context.addInitScript(params.source);
|
||||
}
|
||||
async setNetworkInterceptionPatterns(params) {
|
||||
if (!params.patterns.length) {
|
||||
await this._context.setRequestInterceptor(undefined);
|
||||
return;
|
||||
}
|
||||
const urlMatchers = params.patterns.map(pattern => pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob);
|
||||
await this._context.setRequestInterceptor((route, request) => {
|
||||
const matchesSome = urlMatchers.some(urlMatch => (0, _utils.urlMatches)(this._context._options.baseURL, request.url(), urlMatch));
|
||||
if (!matchesSome) return false;
|
||||
this._dispatchEvent('route', {
|
||||
route: _networkDispatchers.RouteDispatcher.from(_networkDispatchers.RequestDispatcher.from(this, request), route)
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
async setWebSocketInterceptionPatterns(params, metadata) {
|
||||
this._webSocketInterceptionPatterns = params.patterns;
|
||||
if (params.patterns.length) await _webSocketRouteDispatcher.WebSocketRouteDispatcher.installIfNeeded(this, this._context);
|
||||
}
|
||||
async storageState(params, metadata) {
|
||||
return await this._context.storageState();
|
||||
}
|
||||
async close(params, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
await this._context.close(params);
|
||||
}
|
||||
async enableRecorder(params) {
|
||||
if (params.codegenMode === 'trace-events') {
|
||||
await this._context.tracing.start({
|
||||
name: 'trace',
|
||||
snapshots: true,
|
||||
screenshots: true,
|
||||
live: true
|
||||
});
|
||||
await _recorder.Recorder.show('trace-events', this._context, _recorderInTraceViewer.RecorderInTraceViewer.factory(this._context), params);
|
||||
} else {
|
||||
await _recorder.Recorder.show('actions', this._context, _recorderApp.RecorderApp.factory(this._context), params);
|
||||
}
|
||||
}
|
||||
async pause(params, metadata) {
|
||||
// Debugger will take care of this.
|
||||
}
|
||||
async newCDPSession(params) {
|
||||
if (!this._object._browser.options.isChromium) throw new Error(`CDP session is only available in Chromium`);
|
||||
if (!params.page && !params.frame || params.page && params.frame) throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`);
|
||||
const crBrowserContext = this._object;
|
||||
return {
|
||||
session: new _cdpSessionDispatcher.CDPSessionDispatcher(this, await crBrowserContext.newCDPSession((params.page ? params.page : params.frame)._object))
|
||||
};
|
||||
}
|
||||
async harStart(params) {
|
||||
const harId = await this._context._harStart(params.page ? params.page._object : null, params.options);
|
||||
return {
|
||||
harId
|
||||
};
|
||||
}
|
||||
async harExport(params) {
|
||||
const artifact = await this._context._harExport(params.harId);
|
||||
if (!artifact) throw new Error('No HAR artifact. Ensure record.harPath is set.');
|
||||
return {
|
||||
artifact: _artifactDispatcher.ArtifactDispatcher.from(this, artifact)
|
||||
};
|
||||
}
|
||||
async clockFastForward(params, metadata) {
|
||||
var _ref, _params$ticksString;
|
||||
await this._context.clock.fastForward((_ref = (_params$ticksString = params.ticksString) !== null && _params$ticksString !== void 0 ? _params$ticksString : params.ticksNumber) !== null && _ref !== void 0 ? _ref : 0);
|
||||
}
|
||||
async clockInstall(params, metadata) {
|
||||
var _ref2, _params$timeString;
|
||||
await this._context.clock.install((_ref2 = (_params$timeString = params.timeString) !== null && _params$timeString !== void 0 ? _params$timeString : params.timeNumber) !== null && _ref2 !== void 0 ? _ref2 : undefined);
|
||||
}
|
||||
async clockPauseAt(params, metadata) {
|
||||
var _ref3, _params$timeString2;
|
||||
await this._context.clock.pauseAt((_ref3 = (_params$timeString2 = params.timeString) !== null && _params$timeString2 !== void 0 ? _params$timeString2 : params.timeNumber) !== null && _ref3 !== void 0 ? _ref3 : 0);
|
||||
}
|
||||
async clockResume(params, metadata) {
|
||||
await this._context.clock.resume();
|
||||
}
|
||||
async clockRunFor(params, metadata) {
|
||||
var _ref4, _params$ticksString2;
|
||||
await this._context.clock.runFor((_ref4 = (_params$ticksString2 = params.ticksString) !== null && _params$ticksString2 !== void 0 ? _params$ticksString2 : params.ticksNumber) !== null && _ref4 !== void 0 ? _ref4 : 0);
|
||||
}
|
||||
async clockSetFixedTime(params, metadata) {
|
||||
var _ref5, _params$timeString3;
|
||||
await this._context.clock.setFixedTime((_ref5 = (_params$timeString3 = params.timeString) !== null && _params$timeString3 !== void 0 ? _params$timeString3 : params.timeNumber) !== null && _ref5 !== void 0 ? _ref5 : 0);
|
||||
}
|
||||
async clockSetSystemTime(params, metadata) {
|
||||
var _ref6, _params$timeString4;
|
||||
await this._context.clock.setSystemTime((_ref6 = (_params$timeString4 = params.timeString) !== null && _params$timeString4 !== void 0 ? _params$timeString4 : params.timeNumber) !== null && _ref6 !== void 0 ? _ref6 : 0);
|
||||
}
|
||||
async updateSubscription(params) {
|
||||
if (params.enabled) this._subscriptions.add(params.event);else this._subscriptions.delete(params.event);
|
||||
}
|
||||
_onDispose() {
|
||||
// Avoid protocol calls for the closed context.
|
||||
if (!this._context.isClosingOrClosed()) this._context.setRequestInterceptor(undefined).catch(() => {});
|
||||
}
|
||||
}
|
||||
exports.BrowserContextDispatcher = BrowserContextDispatcher;
|
||||
170
node_modules/playwright-core/lib/server/dispatchers/browserDispatcher.js
generated
vendored
Normal file
170
node_modules/playwright-core/lib/server/dispatchers/browserDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ConnectedBrowserDispatcher = exports.BrowserDispatcher = void 0;
|
||||
var _browser = require("../browser");
|
||||
var _browserContextDispatcher = require("./browserContextDispatcher");
|
||||
var _cdpSessionDispatcher = require("./cdpSessionDispatcher");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _selectors = require("../selectors");
|
||||
var _artifactDispatcher = require("./artifactDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BrowserDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, browser) {
|
||||
super(scope, browser, 'Browser', {
|
||||
version: browser.version(),
|
||||
name: browser.options.name
|
||||
});
|
||||
this._type_Browser = true;
|
||||
this.addObjectListener(_browser.Browser.Events.Disconnected, () => this._didClose());
|
||||
}
|
||||
_didClose() {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
}
|
||||
async newContext(params, metadata) {
|
||||
const context = await this._object.newContext(metadata, params);
|
||||
return {
|
||||
context: new _browserContextDispatcher.BrowserContextDispatcher(this, context)
|
||||
};
|
||||
}
|
||||
async newContextForReuse(params, metadata) {
|
||||
return await newContextForReuse(this._object, this, params, null, metadata);
|
||||
}
|
||||
async stopPendingOperations(params, metadata) {
|
||||
await this._object.stopPendingOperations(params.reason);
|
||||
}
|
||||
async close(params, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
await this._object.close(params);
|
||||
}
|
||||
async killForTests(_, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
await this._object.killForTests();
|
||||
}
|
||||
async defaultUserAgentForTest() {
|
||||
return {
|
||||
userAgent: this._object.userAgent()
|
||||
};
|
||||
}
|
||||
async newBrowserCDPSession() {
|
||||
if (!this._object.options.isChromium) throw new Error(`CDP session is only available in Chromium`);
|
||||
const crBrowser = this._object;
|
||||
return {
|
||||
session: new _cdpSessionDispatcher.CDPSessionDispatcher(this, await crBrowser.newBrowserCDPSession())
|
||||
};
|
||||
}
|
||||
async startTracing(params) {
|
||||
if (!this._object.options.isChromium) throw new Error(`Tracing is only available in Chromium`);
|
||||
const crBrowser = this._object;
|
||||
await crBrowser.startTracing(params.page ? params.page._object : undefined, params);
|
||||
}
|
||||
async stopTracing() {
|
||||
if (!this._object.options.isChromium) throw new Error(`Tracing is only available in Chromium`);
|
||||
const crBrowser = this._object;
|
||||
return {
|
||||
artifact: _artifactDispatcher.ArtifactDispatcher.from(this, await crBrowser.stopTracing())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// This class implements multiplexing browser dispatchers over a single Browser instance.
|
||||
exports.BrowserDispatcher = BrowserDispatcher;
|
||||
class ConnectedBrowserDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, browser) {
|
||||
super(scope, browser, 'Browser', {
|
||||
version: browser.version(),
|
||||
name: browser.options.name
|
||||
});
|
||||
// When we have a remotely-connected browser, each client gets a fresh Selector instance,
|
||||
// so that two clients do not interfere between each other.
|
||||
this._type_Browser = true;
|
||||
this._contexts = new Set();
|
||||
this.selectors = void 0;
|
||||
this.selectors = new _selectors.Selectors();
|
||||
}
|
||||
async newContext(params, metadata) {
|
||||
if (params.recordVideo) params.recordVideo.dir = this._object.options.artifactsDir;
|
||||
const context = await this._object.newContext(metadata, params);
|
||||
this._contexts.add(context);
|
||||
context.setSelectors(this.selectors);
|
||||
context.on(_browserContext.BrowserContext.Events.Close, () => this._contexts.delete(context));
|
||||
return {
|
||||
context: new _browserContextDispatcher.BrowserContextDispatcher(this, context)
|
||||
};
|
||||
}
|
||||
async newContextForReuse(params, metadata) {
|
||||
return await newContextForReuse(this._object, this, params, this.selectors, metadata);
|
||||
}
|
||||
async stopPendingOperations(params, metadata) {
|
||||
await this._object.stopPendingOperations(params.reason);
|
||||
}
|
||||
async close() {
|
||||
// Client should not send us Browser.close.
|
||||
}
|
||||
async killForTests() {
|
||||
// Client should not send us Browser.killForTests.
|
||||
}
|
||||
async defaultUserAgentForTest() {
|
||||
throw new Error('Client should not send us Browser.defaultUserAgentForTest');
|
||||
}
|
||||
async newBrowserCDPSession() {
|
||||
if (!this._object.options.isChromium) throw new Error(`CDP session is only available in Chromium`);
|
||||
const crBrowser = this._object;
|
||||
return {
|
||||
session: new _cdpSessionDispatcher.CDPSessionDispatcher(this, await crBrowser.newBrowserCDPSession())
|
||||
};
|
||||
}
|
||||
async startTracing(params) {
|
||||
if (!this._object.options.isChromium) throw new Error(`Tracing is only available in Chromium`);
|
||||
const crBrowser = this._object;
|
||||
await crBrowser.startTracing(params.page ? params.page._object : undefined, params);
|
||||
}
|
||||
async stopTracing() {
|
||||
if (!this._object.options.isChromium) throw new Error(`Tracing is only available in Chromium`);
|
||||
const crBrowser = this._object;
|
||||
return {
|
||||
artifact: _artifactDispatcher.ArtifactDispatcher.from(this, await crBrowser.stopTracing())
|
||||
};
|
||||
}
|
||||
async cleanupContexts() {
|
||||
await Promise.all(Array.from(this._contexts).map(context => context.close({
|
||||
reason: 'Global context cleanup (connection terminated)'
|
||||
})));
|
||||
}
|
||||
}
|
||||
exports.ConnectedBrowserDispatcher = ConnectedBrowserDispatcher;
|
||||
async function newContextForReuse(browser, scope, params, selectors, metadata) {
|
||||
const {
|
||||
context,
|
||||
needsReset
|
||||
} = await browser.newContextForReuse(params, metadata);
|
||||
if (needsReset) {
|
||||
const oldContextDispatcher = (0, _dispatcher.existingDispatcher)(context);
|
||||
if (oldContextDispatcher) oldContextDispatcher._dispose();
|
||||
await context.resetForReuse(metadata, params);
|
||||
}
|
||||
if (selectors) context.setSelectors(selectors);
|
||||
const contextDispatcher = new _browserContextDispatcher.BrowserContextDispatcher(scope, context);
|
||||
return {
|
||||
context: contextDispatcher
|
||||
};
|
||||
}
|
||||
55
node_modules/playwright-core/lib/server/dispatchers/browserTypeDispatcher.js
generated
vendored
Normal file
55
node_modules/playwright-core/lib/server/dispatchers/browserTypeDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.BrowserTypeDispatcher = void 0;
|
||||
var _browserDispatcher = require("./browserDispatcher");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _browserContextDispatcher = require("./browserContextDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class BrowserTypeDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, browserType) {
|
||||
super(scope, browserType, 'BrowserType', {
|
||||
executablePath: browserType.executablePath(),
|
||||
name: browserType.name()
|
||||
});
|
||||
this._type_BrowserType = true;
|
||||
}
|
||||
async launch(params, metadata) {
|
||||
const browser = await this._object.launch(metadata, params);
|
||||
return {
|
||||
browser: new _browserDispatcher.BrowserDispatcher(this, browser)
|
||||
};
|
||||
}
|
||||
async launchPersistentContext(params, metadata) {
|
||||
const browserContext = await this._object.launchPersistentContext(metadata, params.userDataDir, params);
|
||||
return {
|
||||
context: new _browserContextDispatcher.BrowserContextDispatcher(this, browserContext)
|
||||
};
|
||||
}
|
||||
async connectOverCDP(params, metadata) {
|
||||
const browser = await this._object.connectOverCDP(metadata, params.endpointURL, params, params.timeout);
|
||||
const browserDispatcher = new _browserDispatcher.BrowserDispatcher(this, browser);
|
||||
return {
|
||||
browser: browserDispatcher,
|
||||
defaultContext: browser._defaultContext ? new _browserContextDispatcher.BrowserContextDispatcher(browserDispatcher, browser._defaultContext) : undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.BrowserTypeDispatcher = BrowserTypeDispatcher;
|
||||
48
node_modules/playwright-core/lib/server/dispatchers/cdpSessionDispatcher.js
generated
vendored
Normal file
48
node_modules/playwright-core/lib/server/dispatchers/cdpSessionDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.CDPSessionDispatcher = void 0;
|
||||
var _crConnection = require("../chromium/crConnection");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CDPSessionDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, cdpSession) {
|
||||
super(scope, cdpSession, 'CDPSession', {});
|
||||
this._type_CDPSession = true;
|
||||
this.addObjectListener(_crConnection.CDPSession.Events.Event, ({
|
||||
method,
|
||||
params
|
||||
}) => this._dispatchEvent('event', {
|
||||
method,
|
||||
params
|
||||
}));
|
||||
this.addObjectListener(_crConnection.CDPSession.Events.Closed, () => this._dispose());
|
||||
}
|
||||
async send(params) {
|
||||
return {
|
||||
result: await this._object.send(params.method, params.params)
|
||||
};
|
||||
}
|
||||
async detach(_, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
await this._object.detach();
|
||||
}
|
||||
}
|
||||
exports.CDPSessionDispatcher = CDPSessionDispatcher;
|
||||
103
node_modules/playwright-core/lib/server/dispatchers/debugControllerDispatcher.js
generated
vendored
Normal file
103
node_modules/playwright-core/lib/server/dispatchers/debugControllerDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.DebugControllerDispatcher = void 0;
|
||||
var _utils = require("../../utils");
|
||||
var _debugController = require("../debugController");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DebugControllerDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(connection, debugController) {
|
||||
super(connection, debugController, 'DebugController', {});
|
||||
this._type_DebugController = void 0;
|
||||
this._listeners = void 0;
|
||||
this._type_DebugController = true;
|
||||
this._listeners = [_utils.eventsHelper.addEventListener(this._object, _debugController.DebugController.Events.StateChanged, params => {
|
||||
this._dispatchEvent('stateChanged', params);
|
||||
}), _utils.eventsHelper.addEventListener(this._object, _debugController.DebugController.Events.InspectRequested, ({
|
||||
selector,
|
||||
locator
|
||||
}) => {
|
||||
this._dispatchEvent('inspectRequested', {
|
||||
selector,
|
||||
locator
|
||||
});
|
||||
}), _utils.eventsHelper.addEventListener(this._object, _debugController.DebugController.Events.SourceChanged, ({
|
||||
text,
|
||||
header,
|
||||
footer,
|
||||
actions
|
||||
}) => {
|
||||
this._dispatchEvent('sourceChanged', {
|
||||
text,
|
||||
header,
|
||||
footer,
|
||||
actions
|
||||
});
|
||||
}), _utils.eventsHelper.addEventListener(this._object, _debugController.DebugController.Events.Paused, ({
|
||||
paused
|
||||
}) => {
|
||||
this._dispatchEvent('paused', {
|
||||
paused
|
||||
});
|
||||
}), _utils.eventsHelper.addEventListener(this._object, _debugController.DebugController.Events.SetModeRequested, ({
|
||||
mode
|
||||
}) => {
|
||||
this._dispatchEvent('setModeRequested', {
|
||||
mode
|
||||
});
|
||||
})];
|
||||
}
|
||||
async initialize(params) {
|
||||
this._object.initialize(params.codegenId, params.sdkLanguage);
|
||||
}
|
||||
async setReportStateChanged(params) {
|
||||
this._object.setReportStateChanged(params.enabled);
|
||||
}
|
||||
async resetForReuse() {
|
||||
await this._object.resetForReuse();
|
||||
}
|
||||
async navigate(params) {
|
||||
await this._object.navigate(params.url);
|
||||
}
|
||||
async setRecorderMode(params) {
|
||||
await this._object.setRecorderMode(params);
|
||||
}
|
||||
async highlight(params) {
|
||||
await this._object.highlight(params);
|
||||
}
|
||||
async hideHighlight() {
|
||||
await this._object.hideHighlight();
|
||||
}
|
||||
async resume() {
|
||||
await this._object.resume();
|
||||
}
|
||||
async kill() {
|
||||
await this._object.kill();
|
||||
}
|
||||
async closeAllBrowsers() {
|
||||
await this._object.closeAllBrowsers();
|
||||
}
|
||||
_onDispose() {
|
||||
_utils.eventsHelper.removeEventListeners(this._listeners);
|
||||
this._object.dispose();
|
||||
}
|
||||
}
|
||||
exports.DebugControllerDispatcher = DebugControllerDispatcher;
|
||||
44
node_modules/playwright-core/lib/server/dispatchers/dialogDispatcher.js
generated
vendored
Normal file
44
node_modules/playwright-core/lib/server/dispatchers/dialogDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.DialogDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _pageDispatcher = require("./pageDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DialogDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, dialog) {
|
||||
const page = _pageDispatcher.PageDispatcher.fromNullable(scope, dialog.page().initializedOrUndefined());
|
||||
// Prefer scoping to the page, unless we don't have one.
|
||||
super(page || scope, dialog, 'Dialog', {
|
||||
page,
|
||||
type: dialog.type(),
|
||||
message: dialog.message(),
|
||||
defaultValue: dialog.defaultValue()
|
||||
});
|
||||
this._type_Dialog = true;
|
||||
}
|
||||
async accept(params) {
|
||||
await this._object.accept(params.promptText);
|
||||
}
|
||||
async dismiss() {
|
||||
await this._object.dismiss();
|
||||
}
|
||||
}
|
||||
exports.DialogDispatcher = DialogDispatcher;
|
||||
395
node_modules/playwright-core/lib/server/dispatchers/dispatcher.js
generated
vendored
Normal file
395
node_modules/playwright-core/lib/server/dispatchers/dispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.dispatcherSymbol = exports.RootDispatcher = exports.DispatcherConnection = exports.Dispatcher = void 0;
|
||||
exports.existingDispatcher = existingDispatcher;
|
||||
exports.setMaxDispatchersForTest = setMaxDispatchersForTest;
|
||||
var _events = require("events");
|
||||
var _validator = require("../../protocol/validator");
|
||||
var _utils = require("../../utils");
|
||||
var _errors = require("../errors");
|
||||
var _instrumentation = require("../instrumentation");
|
||||
var _eventsHelper = require("../..//utils/eventsHelper");
|
||||
var _protocolError = require("../protocolError");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const dispatcherSymbol = exports.dispatcherSymbol = Symbol('dispatcher');
|
||||
const metadataValidator = (0, _validator.createMetadataValidator)();
|
||||
function existingDispatcher(object) {
|
||||
return object[dispatcherSymbol];
|
||||
}
|
||||
let maxDispatchersOverride;
|
||||
function setMaxDispatchersForTest(value) {
|
||||
maxDispatchersOverride = value;
|
||||
}
|
||||
function maxDispatchersForBucket(gcBucket) {
|
||||
var _ref, _maxDispatchersOverri;
|
||||
return (_ref = (_maxDispatchersOverri = maxDispatchersOverride) !== null && _maxDispatchersOverri !== void 0 ? _maxDispatchersOverri : {
|
||||
'JSHandle': 100000,
|
||||
'ElementHandle': 100000
|
||||
}[gcBucket]) !== null && _ref !== void 0 ? _ref : 10000;
|
||||
}
|
||||
class Dispatcher extends _events.EventEmitter {
|
||||
constructor(parent, object, type, initializer, gcBucket) {
|
||||
super();
|
||||
this._connection = void 0;
|
||||
// Parent is always "isScope".
|
||||
this._parent = void 0;
|
||||
// Only "isScope" channel owners have registered dispatchers inside.
|
||||
this._dispatchers = new Map();
|
||||
this._disposed = false;
|
||||
this._eventListeners = [];
|
||||
this._guid = void 0;
|
||||
this._type = void 0;
|
||||
this._gcBucket = void 0;
|
||||
this._object = void 0;
|
||||
this._openScope = new _utils.LongStandingScope();
|
||||
this._connection = parent instanceof DispatcherConnection ? parent : parent._connection;
|
||||
this._parent = parent instanceof DispatcherConnection ? undefined : parent;
|
||||
const guid = object.guid;
|
||||
this._guid = guid;
|
||||
this._type = type;
|
||||
this._object = object;
|
||||
this._gcBucket = gcBucket !== null && gcBucket !== void 0 ? gcBucket : type;
|
||||
object[dispatcherSymbol] = this;
|
||||
this._connection.registerDispatcher(this);
|
||||
if (this._parent) {
|
||||
(0, _utils.assert)(!this._parent._dispatchers.has(guid));
|
||||
this._parent._dispatchers.set(guid, this);
|
||||
}
|
||||
if (this._parent) this._connection.sendCreate(this._parent, type, guid, initializer);
|
||||
this._connection.maybeDisposeStaleDispatchers(this._gcBucket);
|
||||
}
|
||||
parentScope() {
|
||||
return this._parent;
|
||||
}
|
||||
addObjectListener(eventName, handler) {
|
||||
this._eventListeners.push(_eventsHelper.eventsHelper.addEventListener(this._object, eventName, handler));
|
||||
}
|
||||
adopt(child) {
|
||||
if (child._parent === this) return;
|
||||
const oldParent = child._parent;
|
||||
oldParent._dispatchers.delete(child._guid);
|
||||
this._dispatchers.set(child._guid, child);
|
||||
child._parent = this;
|
||||
this._connection.sendAdopt(this, child);
|
||||
}
|
||||
async _handleCommand(callMetadata, method, validParams) {
|
||||
const commandPromise = this[method](validParams, callMetadata);
|
||||
try {
|
||||
return await this._openScope.race(commandPromise);
|
||||
} catch (e) {
|
||||
if (callMetadata.potentiallyClosesScope && (0, _errors.isTargetClosedError)(e)) return await commandPromise;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
_dispatchEvent(method, params) {
|
||||
if (this._disposed) {
|
||||
if ((0, _utils.isUnderTest)()) throw new Error(`${this._guid} is sending "${String(method)}" event after being disposed`);
|
||||
// Just ignore this event outside of tests.
|
||||
return;
|
||||
}
|
||||
this._connection.sendEvent(this, method, params);
|
||||
}
|
||||
_dispose(reason) {
|
||||
this._disposeRecursively(new _errors.TargetClosedError());
|
||||
this._connection.sendDispose(this, reason);
|
||||
}
|
||||
_onDispose() {}
|
||||
_disposeRecursively(error) {
|
||||
var _this$_parent;
|
||||
(0, _utils.assert)(!this._disposed, `${this._guid} is disposed more than once`);
|
||||
this._onDispose();
|
||||
this._disposed = true;
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
|
||||
// Clean up from parent and connection.
|
||||
(_this$_parent = this._parent) === null || _this$_parent === void 0 || _this$_parent._dispatchers.delete(this._guid);
|
||||
const list = this._connection._dispatchersByBucket.get(this._gcBucket);
|
||||
list === null || list === void 0 || list.delete(this._guid);
|
||||
this._connection._dispatchers.delete(this._guid);
|
||||
|
||||
// Dispose all children.
|
||||
for (const dispatcher of [...this._dispatchers.values()]) dispatcher._disposeRecursively(error);
|
||||
this._dispatchers.clear();
|
||||
delete this._object[dispatcherSymbol];
|
||||
this._openScope.close(error);
|
||||
}
|
||||
_debugScopeState() {
|
||||
return {
|
||||
_guid: this._guid,
|
||||
objects: Array.from(this._dispatchers.values()).map(o => o._debugScopeState())
|
||||
};
|
||||
}
|
||||
async waitForEventInfo() {
|
||||
// Instrumentation takes care of this.
|
||||
}
|
||||
}
|
||||
exports.Dispatcher = Dispatcher;
|
||||
class RootDispatcher extends Dispatcher {
|
||||
constructor(connection, createPlaywright) {
|
||||
super(connection, {
|
||||
guid: ''
|
||||
}, 'Root', {});
|
||||
this._initialized = false;
|
||||
this.createPlaywright = createPlaywright;
|
||||
}
|
||||
async initialize(params) {
|
||||
(0, _utils.assert)(this.createPlaywright);
|
||||
(0, _utils.assert)(!this._initialized);
|
||||
this._initialized = true;
|
||||
return {
|
||||
playwright: await this.createPlaywright(this, params)
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.RootDispatcher = RootDispatcher;
|
||||
class DispatcherConnection {
|
||||
constructor(isLocal) {
|
||||
this._dispatchers = new Map();
|
||||
this._dispatchersByBucket = new Map();
|
||||
this.onmessage = message => {};
|
||||
this._waitOperations = new Map();
|
||||
this._isLocal = void 0;
|
||||
this._isLocal = !!isLocal;
|
||||
}
|
||||
sendEvent(dispatcher, event, params) {
|
||||
const validator = (0, _validator.findValidator)(dispatcher._type, event, 'Event');
|
||||
params = validator(params, '', {
|
||||
tChannelImpl: this._tChannelImplToWire.bind(this),
|
||||
binary: this._isLocal ? 'buffer' : 'toBase64'
|
||||
});
|
||||
this.onmessage({
|
||||
guid: dispatcher._guid,
|
||||
method: event,
|
||||
params
|
||||
});
|
||||
}
|
||||
sendCreate(parent, type, guid, initializer) {
|
||||
const validator = (0, _validator.findValidator)(type, '', 'Initializer');
|
||||
initializer = validator(initializer, '', {
|
||||
tChannelImpl: this._tChannelImplToWire.bind(this),
|
||||
binary: this._isLocal ? 'buffer' : 'toBase64'
|
||||
});
|
||||
this.onmessage({
|
||||
guid: parent._guid,
|
||||
method: '__create__',
|
||||
params: {
|
||||
type,
|
||||
initializer,
|
||||
guid
|
||||
}
|
||||
});
|
||||
}
|
||||
sendAdopt(parent, dispatcher) {
|
||||
this.onmessage({
|
||||
guid: parent._guid,
|
||||
method: '__adopt__',
|
||||
params: {
|
||||
guid: dispatcher._guid
|
||||
}
|
||||
});
|
||||
}
|
||||
sendDispose(dispatcher, reason) {
|
||||
this.onmessage({
|
||||
guid: dispatcher._guid,
|
||||
method: '__dispose__',
|
||||
params: {
|
||||
reason
|
||||
}
|
||||
});
|
||||
}
|
||||
_tChannelImplFromWire(names, arg, path, context) {
|
||||
if (arg && typeof arg === 'object' && typeof arg.guid === 'string') {
|
||||
const guid = arg.guid;
|
||||
const dispatcher = this._dispatchers.get(guid);
|
||||
if (!dispatcher) throw new _validator.ValidationError(`${path}: no object with guid ${guid}`);
|
||||
if (names !== '*' && !names.includes(dispatcher._type)) throw new _validator.ValidationError(`${path}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}`);
|
||||
return dispatcher;
|
||||
}
|
||||
throw new _validator.ValidationError(`${path}: expected guid for ${names.toString()}`);
|
||||
}
|
||||
_tChannelImplToWire(names, arg, path, context) {
|
||||
if (arg instanceof Dispatcher) {
|
||||
if (names !== '*' && !names.includes(arg._type)) throw new _validator.ValidationError(`${path}: dispatcher with guid ${arg._guid} has type ${arg._type}, expected ${names.toString()}`);
|
||||
return {
|
||||
guid: arg._guid
|
||||
};
|
||||
}
|
||||
throw new _validator.ValidationError(`${path}: expected dispatcher ${names.toString()}`);
|
||||
}
|
||||
registerDispatcher(dispatcher) {
|
||||
(0, _utils.assert)(!this._dispatchers.has(dispatcher._guid));
|
||||
this._dispatchers.set(dispatcher._guid, dispatcher);
|
||||
let list = this._dispatchersByBucket.get(dispatcher._gcBucket);
|
||||
if (!list) {
|
||||
list = new Set();
|
||||
this._dispatchersByBucket.set(dispatcher._gcBucket, list);
|
||||
}
|
||||
list.add(dispatcher._guid);
|
||||
}
|
||||
maybeDisposeStaleDispatchers(gcBucket) {
|
||||
const maxDispatchers = maxDispatchersForBucket(gcBucket);
|
||||
const list = this._dispatchersByBucket.get(gcBucket);
|
||||
if (!list || list.size <= maxDispatchers) return;
|
||||
const dispatchersArray = [...list];
|
||||
const disposeCount = maxDispatchers / 10 | 0;
|
||||
this._dispatchersByBucket.set(gcBucket, new Set(dispatchersArray.slice(disposeCount)));
|
||||
for (let i = 0; i < disposeCount; ++i) {
|
||||
const d = this._dispatchers.get(dispatchersArray[i]);
|
||||
if (!d) continue;
|
||||
d._dispose('gc');
|
||||
}
|
||||
}
|
||||
async dispatch(message) {
|
||||
var _sdkObject$attributio, _sdkObject$attributio2, _params$info;
|
||||
const {
|
||||
id,
|
||||
guid,
|
||||
method,
|
||||
params,
|
||||
metadata
|
||||
} = message;
|
||||
const dispatcher = this._dispatchers.get(guid);
|
||||
if (!dispatcher) {
|
||||
this.onmessage({
|
||||
id,
|
||||
error: (0, _errors.serializeError)(new _errors.TargetClosedError())
|
||||
});
|
||||
return;
|
||||
}
|
||||
let validParams;
|
||||
let validMetadata;
|
||||
try {
|
||||
const validator = (0, _validator.findValidator)(dispatcher._type, method, 'Params');
|
||||
validParams = validator(params, '', {
|
||||
tChannelImpl: this._tChannelImplFromWire.bind(this),
|
||||
binary: this._isLocal ? 'buffer' : 'fromBase64'
|
||||
});
|
||||
validMetadata = metadataValidator(metadata, '', {
|
||||
tChannelImpl: this._tChannelImplFromWire.bind(this),
|
||||
binary: this._isLocal ? 'buffer' : 'fromBase64'
|
||||
});
|
||||
if (typeof dispatcher[method] !== 'function') throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
|
||||
} catch (e) {
|
||||
this.onmessage({
|
||||
id,
|
||||
error: (0, _errors.serializeError)(e)
|
||||
});
|
||||
return;
|
||||
}
|
||||
const sdkObject = dispatcher._object instanceof _instrumentation.SdkObject ? dispatcher._object : undefined;
|
||||
const callMetadata = {
|
||||
id: `call@${id}`,
|
||||
location: validMetadata.location,
|
||||
apiName: validMetadata.apiName,
|
||||
internal: validMetadata.internal,
|
||||
stepId: validMetadata.stepId,
|
||||
objectId: sdkObject === null || sdkObject === void 0 ? void 0 : sdkObject.guid,
|
||||
pageId: sdkObject === null || sdkObject === void 0 || (_sdkObject$attributio = sdkObject.attribution) === null || _sdkObject$attributio === void 0 || (_sdkObject$attributio = _sdkObject$attributio.page) === null || _sdkObject$attributio === void 0 ? void 0 : _sdkObject$attributio.guid,
|
||||
frameId: sdkObject === null || sdkObject === void 0 || (_sdkObject$attributio2 = sdkObject.attribution) === null || _sdkObject$attributio2 === void 0 || (_sdkObject$attributio2 = _sdkObject$attributio2.frame) === null || _sdkObject$attributio2 === void 0 ? void 0 : _sdkObject$attributio2.guid,
|
||||
startTime: (0, _utils.monotonicTime)(),
|
||||
endTime: 0,
|
||||
type: dispatcher._type,
|
||||
method,
|
||||
params: params || {},
|
||||
log: []
|
||||
};
|
||||
if (sdkObject && params !== null && params !== void 0 && (_params$info = params.info) !== null && _params$info !== void 0 && _params$info.waitId) {
|
||||
// Process logs for waitForNavigation/waitForLoadState/etc.
|
||||
const info = params.info;
|
||||
switch (info.phase) {
|
||||
case 'before':
|
||||
{
|
||||
this._waitOperations.set(info.waitId, callMetadata);
|
||||
await sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata);
|
||||
this.onmessage({
|
||||
id
|
||||
});
|
||||
return;
|
||||
}
|
||||
case 'log':
|
||||
{
|
||||
const originalMetadata = this._waitOperations.get(info.waitId);
|
||||
originalMetadata.log.push(info.message);
|
||||
sdkObject.instrumentation.onCallLog(sdkObject, originalMetadata, 'api', info.message);
|
||||
this.onmessage({
|
||||
id
|
||||
});
|
||||
return;
|
||||
}
|
||||
case 'after':
|
||||
{
|
||||
const originalMetadata = this._waitOperations.get(info.waitId);
|
||||
originalMetadata.endTime = (0, _utils.monotonicTime)();
|
||||
originalMetadata.error = info.error ? {
|
||||
error: {
|
||||
name: 'Error',
|
||||
message: info.error
|
||||
}
|
||||
} : undefined;
|
||||
this._waitOperations.delete(info.waitId);
|
||||
await sdkObject.instrumentation.onAfterCall(sdkObject, originalMetadata);
|
||||
this.onmessage({
|
||||
id
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
await (sdkObject === null || sdkObject === void 0 ? void 0 : sdkObject.instrumentation.onBeforeCall(sdkObject, callMetadata));
|
||||
const response = {
|
||||
id
|
||||
};
|
||||
try {
|
||||
const result = await dispatcher._handleCommand(callMetadata, method, validParams);
|
||||
const validator = (0, _validator.findValidator)(dispatcher._type, method, 'Result');
|
||||
response.result = validator(result, '', {
|
||||
tChannelImpl: this._tChannelImplToWire.bind(this),
|
||||
binary: this._isLocal ? 'buffer' : 'toBase64'
|
||||
});
|
||||
callMetadata.result = result;
|
||||
} catch (e) {
|
||||
if ((0, _errors.isTargetClosedError)(e) && sdkObject) {
|
||||
const reason = closeReason(sdkObject);
|
||||
if (reason) (0, _utils.rewriteErrorMessage)(e, reason);
|
||||
} else if ((0, _protocolError.isProtocolError)(e)) {
|
||||
if (e.type === 'closed') {
|
||||
const reason = sdkObject ? closeReason(sdkObject) : undefined;
|
||||
e = new _errors.TargetClosedError(reason, e.browserLogMessage());
|
||||
} else if (e.type === 'crashed') {
|
||||
(0, _utils.rewriteErrorMessage)(e, 'Target crashed ' + e.browserLogMessage());
|
||||
}
|
||||
}
|
||||
response.error = (0, _errors.serializeError)(e);
|
||||
// The command handler could have set error in the metadata, do not reset it if there was no exception.
|
||||
callMetadata.error = response.error;
|
||||
} finally {
|
||||
callMetadata.endTime = (0, _utils.monotonicTime)();
|
||||
await (sdkObject === null || sdkObject === void 0 ? void 0 : sdkObject.instrumentation.onAfterCall(sdkObject, callMetadata));
|
||||
}
|
||||
if (response.error) response.log = (0, _utils.compressCallLog)(callMetadata.log);
|
||||
this.onmessage(response);
|
||||
}
|
||||
}
|
||||
exports.DispatcherConnection = DispatcherConnection;
|
||||
function closeReason(sdkObject) {
|
||||
var _sdkObject$attributio3, _sdkObject$attributio4, _sdkObject$attributio5;
|
||||
return ((_sdkObject$attributio3 = sdkObject.attribution.page) === null || _sdkObject$attributio3 === void 0 ? void 0 : _sdkObject$attributio3._closeReason) || ((_sdkObject$attributio4 = sdkObject.attribution.context) === null || _sdkObject$attributio4 === void 0 ? void 0 : _sdkObject$attributio4._closeReason) || ((_sdkObject$attributio5 = sdkObject.attribution.browser) === null || _sdkObject$attributio5 === void 0 ? void 0 : _sdkObject$attributio5._closeReason);
|
||||
}
|
||||
93
node_modules/playwright-core/lib/server/dispatchers/electronDispatcher.js
generated
vendored
Normal file
93
node_modules/playwright-core/lib/server/dispatchers/electronDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ElectronDispatcher = exports.ElectronApplicationDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _electron = require("../electron/electron");
|
||||
var _browserContextDispatcher = require("./browserContextDispatcher");
|
||||
var _jsHandleDispatcher = require("./jsHandleDispatcher");
|
||||
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ElectronDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, electron) {
|
||||
super(scope, electron, 'Electron', {});
|
||||
this._type_Electron = true;
|
||||
}
|
||||
async launch(params) {
|
||||
const electronApplication = await this._object.launch(params);
|
||||
return {
|
||||
electronApplication: new ElectronApplicationDispatcher(this, electronApplication)
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.ElectronDispatcher = ElectronDispatcher;
|
||||
class ElectronApplicationDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, electronApplication) {
|
||||
super(scope, electronApplication, 'ElectronApplication', {
|
||||
context: new _browserContextDispatcher.BrowserContextDispatcher(scope, electronApplication.context())
|
||||
});
|
||||
this._type_EventTarget = true;
|
||||
this._type_ElectronApplication = true;
|
||||
this._subscriptions = new Set();
|
||||
this.addObjectListener(_electron.ElectronApplication.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
this.addObjectListener(_electron.ElectronApplication.Events.Console, message => {
|
||||
if (!this._subscriptions.has('console')) return;
|
||||
this._dispatchEvent('console', {
|
||||
type: message.type(),
|
||||
text: message.text(),
|
||||
args: message.args().map(a => _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, a)),
|
||||
location: message.location()
|
||||
});
|
||||
});
|
||||
}
|
||||
async browserWindow(params) {
|
||||
const handle = await this._object.browserWindow(params.page.page());
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, handle)
|
||||
};
|
||||
}
|
||||
async evaluateExpression(params) {
|
||||
const handle = await this._object._nodeElectronHandlePromise;
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await handle.evaluateExpression(params.expression, {
|
||||
isFunction: params.isFunction
|
||||
}, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async evaluateExpressionHandle(params) {
|
||||
const handle = await this._object._nodeElectronHandlePromise;
|
||||
const result = await handle.evaluateExpressionHandle(params.expression, {
|
||||
isFunction: params.isFunction
|
||||
}, (0, _jsHandleDispatcher.parseArgument)(params.arg));
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, result)
|
||||
};
|
||||
}
|
||||
async updateSubscription(params) {
|
||||
if (params.enabled) this._subscriptions.add(params.event);else this._subscriptions.delete(params.event);
|
||||
}
|
||||
async close() {
|
||||
await this._object.close();
|
||||
}
|
||||
}
|
||||
exports.ElectronApplicationDispatcher = ElectronApplicationDispatcher;
|
||||
228
node_modules/playwright-core/lib/server/dispatchers/elementHandlerDispatcher.js
generated
vendored
Normal file
228
node_modules/playwright-core/lib/server/dispatchers/elementHandlerDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ElementHandleDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _jsHandleDispatcher = require("./jsHandleDispatcher");
|
||||
var _frameDispatcher = require("./frameDispatcher");
|
||||
var _browserContextDispatcher = require("./browserContextDispatcher");
|
||||
var _pageDispatcher = require("./pageDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class ElementHandleDispatcher extends _jsHandleDispatcher.JSHandleDispatcher {
|
||||
static from(scope, handle) {
|
||||
return (0, _dispatcher.existingDispatcher)(handle) || new ElementHandleDispatcher(scope, handle);
|
||||
}
|
||||
static fromNullable(scope, handle) {
|
||||
if (!handle) return undefined;
|
||||
return (0, _dispatcher.existingDispatcher)(handle) || new ElementHandleDispatcher(scope, handle);
|
||||
}
|
||||
static fromJSHandle(scope, handle) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(handle);
|
||||
if (result) return result;
|
||||
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()) : new _jsHandleDispatcher.JSHandleDispatcher(scope, handle);
|
||||
}
|
||||
constructor(scope, elementHandle) {
|
||||
super(scope, elementHandle);
|
||||
this._type_ElementHandle = true;
|
||||
this._elementHandle = void 0;
|
||||
this._elementHandle = elementHandle;
|
||||
}
|
||||
async ownerFrame(params, metadata) {
|
||||
const frame = await this._elementHandle.ownerFrame();
|
||||
return {
|
||||
frame: frame ? _frameDispatcher.FrameDispatcher.from(this._browserContextDispatcher(), frame) : undefined
|
||||
};
|
||||
}
|
||||
async contentFrame(params, metadata) {
|
||||
const frame = await this._elementHandle.contentFrame();
|
||||
return {
|
||||
frame: frame ? _frameDispatcher.FrameDispatcher.from(this._browserContextDispatcher(), frame) : undefined
|
||||
};
|
||||
}
|
||||
async getAttribute(params, metadata) {
|
||||
const value = await this._elementHandle.getAttribute(metadata, params.name);
|
||||
return {
|
||||
value: value === null ? undefined : value
|
||||
};
|
||||
}
|
||||
async inputValue(params, metadata) {
|
||||
const value = await this._elementHandle.inputValue(metadata);
|
||||
return {
|
||||
value
|
||||
};
|
||||
}
|
||||
async textContent(params, metadata) {
|
||||
const value = await this._elementHandle.textContent(metadata);
|
||||
return {
|
||||
value: value === null ? undefined : value
|
||||
};
|
||||
}
|
||||
async innerText(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.innerText(metadata)
|
||||
};
|
||||
}
|
||||
async innerHTML(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.innerHTML(metadata)
|
||||
};
|
||||
}
|
||||
async isChecked(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.isChecked(metadata)
|
||||
};
|
||||
}
|
||||
async isDisabled(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.isDisabled(metadata)
|
||||
};
|
||||
}
|
||||
async isEditable(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.isEditable(metadata)
|
||||
};
|
||||
}
|
||||
async isEnabled(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.isEnabled(metadata)
|
||||
};
|
||||
}
|
||||
async isHidden(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.isHidden(metadata)
|
||||
};
|
||||
}
|
||||
async isVisible(params, metadata) {
|
||||
return {
|
||||
value: await this._elementHandle.isVisible(metadata)
|
||||
};
|
||||
}
|
||||
async dispatchEvent(params, metadata) {
|
||||
await this._elementHandle.dispatchEvent(metadata, params.type, (0, _jsHandleDispatcher.parseArgument)(params.eventInit));
|
||||
}
|
||||
async scrollIntoViewIfNeeded(params, metadata) {
|
||||
await this._elementHandle.scrollIntoViewIfNeeded(metadata, params);
|
||||
}
|
||||
async hover(params, metadata) {
|
||||
return await this._elementHandle.hover(metadata, params);
|
||||
}
|
||||
async click(params, metadata) {
|
||||
return await this._elementHandle.click(metadata, params);
|
||||
}
|
||||
async dblclick(params, metadata) {
|
||||
return await this._elementHandle.dblclick(metadata, params);
|
||||
}
|
||||
async tap(params, metadata) {
|
||||
return await this._elementHandle.tap(metadata, params);
|
||||
}
|
||||
async selectOption(params, metadata) {
|
||||
const elements = (params.elements || []).map(e => e._elementHandle);
|
||||
return {
|
||||
values: await this._elementHandle.selectOption(metadata, elements, params.options || [], params)
|
||||
};
|
||||
}
|
||||
async fill(params, metadata) {
|
||||
return await this._elementHandle.fill(metadata, params.value, params);
|
||||
}
|
||||
async selectText(params, metadata) {
|
||||
await this._elementHandle.selectText(metadata, params);
|
||||
}
|
||||
async setInputFiles(params, metadata) {
|
||||
return await this._elementHandle.setInputFiles(metadata, params);
|
||||
}
|
||||
async focus(params, metadata) {
|
||||
await this._elementHandle.focus(metadata);
|
||||
}
|
||||
async type(params, metadata) {
|
||||
return await this._elementHandle.type(metadata, params.text, params);
|
||||
}
|
||||
async press(params, metadata) {
|
||||
return await this._elementHandle.press(metadata, params.key, params);
|
||||
}
|
||||
async check(params, metadata) {
|
||||
return await this._elementHandle.check(metadata, params);
|
||||
}
|
||||
async uncheck(params, metadata) {
|
||||
return await this._elementHandle.uncheck(metadata, params);
|
||||
}
|
||||
async boundingBox(params, metadata) {
|
||||
const value = await this._elementHandle.boundingBox();
|
||||
return {
|
||||
value: value || undefined
|
||||
};
|
||||
}
|
||||
async screenshot(params, metadata) {
|
||||
const mask = (params.mask || []).map(({
|
||||
frame,
|
||||
selector
|
||||
}) => ({
|
||||
frame: frame._object,
|
||||
selector
|
||||
}));
|
||||
return {
|
||||
binary: await this._elementHandle.screenshot(metadata, {
|
||||
...params,
|
||||
mask
|
||||
})
|
||||
};
|
||||
}
|
||||
async querySelector(params, metadata) {
|
||||
const handle = await this._elementHandle.querySelector(params.selector, params);
|
||||
return {
|
||||
element: ElementHandleDispatcher.fromNullable(this.parentScope(), handle)
|
||||
};
|
||||
}
|
||||
async querySelectorAll(params, metadata) {
|
||||
const elements = await this._elementHandle.querySelectorAll(params.selector);
|
||||
return {
|
||||
elements: elements.map(e => ElementHandleDispatcher.from(this.parentScope(), e))
|
||||
};
|
||||
}
|
||||
async evalOnSelector(params, metadata) {
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await this._elementHandle.evalOnSelector(params.selector, !!params.strict, params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async evalOnSelectorAll(params, metadata) {
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await this._elementHandle.evalOnSelectorAll(params.selector, params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async waitForElementState(params, metadata) {
|
||||
await this._elementHandle.waitForElementState(metadata, params.state, params);
|
||||
}
|
||||
async waitForSelector(params, metadata) {
|
||||
return {
|
||||
element: ElementHandleDispatcher.fromNullable(this.parentScope(), await this._elementHandle.waitForSelector(metadata, params.selector, params))
|
||||
};
|
||||
}
|
||||
_browserContextDispatcher() {
|
||||
const scope = this.parentScope();
|
||||
if (scope instanceof _browserContextDispatcher.BrowserContextDispatcher) return scope;
|
||||
if (scope instanceof _pageDispatcher.PageDispatcher) return scope.parentScope();
|
||||
if (scope instanceof _pageDispatcher.WorkerDispatcher || scope instanceof _frameDispatcher.FrameDispatcher) {
|
||||
const parentScope = scope.parentScope();
|
||||
if (parentScope instanceof _browserContextDispatcher.BrowserContextDispatcher) return parentScope;
|
||||
return parentScope.parentScope();
|
||||
}
|
||||
throw new Error('ElementHandle belongs to unexpected scope');
|
||||
}
|
||||
}
|
||||
exports.ElementHandleDispatcher = ElementHandleDispatcher;
|
||||
293
node_modules/playwright-core/lib/server/dispatchers/frameDispatcher.js
generated
vendored
Normal file
293
node_modules/playwright-core/lib/server/dispatchers/frameDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FrameDispatcher = void 0;
|
||||
var _frames = require("../frames");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
|
||||
var _jsHandleDispatcher = require("./jsHandleDispatcher");
|
||||
var _networkDispatchers = require("./networkDispatchers");
|
||||
var _utils = require("../../utils");
|
||||
var _ariaSnapshot = require("../ariaSnapshot");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FrameDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, frame) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(frame);
|
||||
return result || new FrameDispatcher(scope, frame);
|
||||
}
|
||||
static fromNullable(scope, frame) {
|
||||
if (!frame) return;
|
||||
return FrameDispatcher.from(scope, frame);
|
||||
}
|
||||
constructor(scope, frame) {
|
||||
// Main frames are gc'ed separately from any other frames, so that
|
||||
// methods on Page that redirect to the main frame remain operational.
|
||||
// Note: we cannot check parentFrame() here because it may be null after the frame has been detached.
|
||||
(0, _utils.debugAssert)(frame._page.mainFrame(), 'Cannot determine whether the frame is a main frame');
|
||||
const gcBucket = frame._page.mainFrame() === frame ? 'MainFrame' : 'Frame';
|
||||
const pageDispatcher = (0, _dispatcher.existingDispatcher)(frame._page);
|
||||
super(pageDispatcher || scope, frame, 'Frame', {
|
||||
url: frame.url(),
|
||||
name: frame.name(),
|
||||
parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()),
|
||||
loadStates: Array.from(frame._firedLifecycleEvents)
|
||||
}, gcBucket);
|
||||
this._type_Frame = true;
|
||||
this._frame = void 0;
|
||||
this._browserContextDispatcher = void 0;
|
||||
this._browserContextDispatcher = scope;
|
||||
this._frame = frame;
|
||||
this.addObjectListener(_frames.Frame.Events.AddLifecycle, lifecycleEvent => {
|
||||
this._dispatchEvent('loadstate', {
|
||||
add: lifecycleEvent
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_frames.Frame.Events.RemoveLifecycle, lifecycleEvent => {
|
||||
this._dispatchEvent('loadstate', {
|
||||
remove: lifecycleEvent
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_frames.Frame.Events.InternalNavigation, event => {
|
||||
if (!event.isPublic) return;
|
||||
const params = {
|
||||
url: event.url,
|
||||
name: event.name,
|
||||
error: event.error ? event.error.message : undefined
|
||||
};
|
||||
if (event.newDocument) params.newDocument = {
|
||||
request: _networkDispatchers.RequestDispatcher.fromNullable(this._browserContextDispatcher, event.newDocument.request || null)
|
||||
};
|
||||
this._dispatchEvent('navigated', params);
|
||||
});
|
||||
}
|
||||
async goto(params, metadata) {
|
||||
return {
|
||||
response: _networkDispatchers.ResponseDispatcher.fromNullable(this._browserContextDispatcher, await this._frame.goto(metadata, params.url, params))
|
||||
};
|
||||
}
|
||||
async frameElement() {
|
||||
return {
|
||||
element: _elementHandlerDispatcher.ElementHandleDispatcher.from(this, await this._frame.frameElement())
|
||||
};
|
||||
}
|
||||
async evaluateExpression(params, metadata) {
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await this._frame.evaluateExpression(params.expression, {
|
||||
isFunction: params.isFunction
|
||||
}, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async evaluateExpressionHandle(params, metadata) {
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, await this._frame.evaluateExpressionHandle(params.expression, {
|
||||
isFunction: params.isFunction
|
||||
}, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async waitForSelector(params, metadata) {
|
||||
return {
|
||||
element: _elementHandlerDispatcher.ElementHandleDispatcher.fromNullable(this, await this._frame.waitForSelector(metadata, params.selector, params))
|
||||
};
|
||||
}
|
||||
async dispatchEvent(params, metadata) {
|
||||
return this._frame.dispatchEvent(metadata, params.selector, params.type, (0, _jsHandleDispatcher.parseArgument)(params.eventInit), params);
|
||||
}
|
||||
async evalOnSelector(params, metadata) {
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await this._frame.evalOnSelector(params.selector, !!params.strict, params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async evalOnSelectorAll(params, metadata) {
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await this._frame.evalOnSelectorAll(params.selector, params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async querySelector(params, metadata) {
|
||||
return {
|
||||
element: _elementHandlerDispatcher.ElementHandleDispatcher.fromNullable(this, await this._frame.querySelector(params.selector, params))
|
||||
};
|
||||
}
|
||||
async querySelectorAll(params, metadata) {
|
||||
const elements = await this._frame.querySelectorAll(params.selector);
|
||||
return {
|
||||
elements: elements.map(e => _elementHandlerDispatcher.ElementHandleDispatcher.from(this, e))
|
||||
};
|
||||
}
|
||||
async queryCount(params) {
|
||||
return {
|
||||
value: await this._frame.queryCount(params.selector)
|
||||
};
|
||||
}
|
||||
async content() {
|
||||
return {
|
||||
value: await this._frame.content()
|
||||
};
|
||||
}
|
||||
async setContent(params, metadata) {
|
||||
return await this._frame.setContent(metadata, params.html, params);
|
||||
}
|
||||
async addScriptTag(params, metadata) {
|
||||
return {
|
||||
element: _elementHandlerDispatcher.ElementHandleDispatcher.from(this, await this._frame.addScriptTag(params))
|
||||
};
|
||||
}
|
||||
async addStyleTag(params, metadata) {
|
||||
return {
|
||||
element: _elementHandlerDispatcher.ElementHandleDispatcher.from(this, await this._frame.addStyleTag(params))
|
||||
};
|
||||
}
|
||||
async click(params, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
return await this._frame.click(metadata, params.selector, params);
|
||||
}
|
||||
async dblclick(params, metadata) {
|
||||
return await this._frame.dblclick(metadata, params.selector, params);
|
||||
}
|
||||
async dragAndDrop(params, metadata) {
|
||||
return await this._frame.dragAndDrop(metadata, params.source, params.target, params);
|
||||
}
|
||||
async tap(params, metadata) {
|
||||
return await this._frame.tap(metadata, params.selector, params);
|
||||
}
|
||||
async fill(params, metadata) {
|
||||
return await this._frame.fill(metadata, params.selector, params.value, params);
|
||||
}
|
||||
async focus(params, metadata) {
|
||||
await this._frame.focus(metadata, params.selector, params);
|
||||
}
|
||||
async blur(params, metadata) {
|
||||
await this._frame.blur(metadata, params.selector, params);
|
||||
}
|
||||
async textContent(params, metadata) {
|
||||
const value = await this._frame.textContent(metadata, params.selector, params);
|
||||
return {
|
||||
value: value === null ? undefined : value
|
||||
};
|
||||
}
|
||||
async innerText(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.innerText(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async innerHTML(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.innerHTML(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async getAttribute(params, metadata) {
|
||||
const value = await this._frame.getAttribute(metadata, params.selector, params.name, params);
|
||||
return {
|
||||
value: value === null ? undefined : value
|
||||
};
|
||||
}
|
||||
async inputValue(params, metadata) {
|
||||
const value = await this._frame.inputValue(metadata, params.selector, params);
|
||||
return {
|
||||
value
|
||||
};
|
||||
}
|
||||
async isChecked(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.isChecked(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async isDisabled(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.isDisabled(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async isEditable(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.isEditable(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async isEnabled(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.isEnabled(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async isHidden(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.isHidden(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async isVisible(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.isVisible(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
async hover(params, metadata) {
|
||||
return await this._frame.hover(metadata, params.selector, params);
|
||||
}
|
||||
async selectOption(params, metadata) {
|
||||
const elements = (params.elements || []).map(e => e._elementHandle);
|
||||
return {
|
||||
values: await this._frame.selectOption(metadata, params.selector, elements, params.options || [], params)
|
||||
};
|
||||
}
|
||||
async setInputFiles(params, metadata) {
|
||||
return await this._frame.setInputFiles(metadata, params.selector, params);
|
||||
}
|
||||
async type(params, metadata) {
|
||||
return await this._frame.type(metadata, params.selector, params.text, params);
|
||||
}
|
||||
async press(params, metadata) {
|
||||
return await this._frame.press(metadata, params.selector, params.key, params);
|
||||
}
|
||||
async check(params, metadata) {
|
||||
return await this._frame.check(metadata, params.selector, params);
|
||||
}
|
||||
async uncheck(params, metadata) {
|
||||
return await this._frame.uncheck(metadata, params.selector, params);
|
||||
}
|
||||
async waitForTimeout(params, metadata) {
|
||||
return await this._frame.waitForTimeout(metadata, params.timeout);
|
||||
}
|
||||
async waitForFunction(params, metadata) {
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg), params))
|
||||
};
|
||||
}
|
||||
async title(params, metadata) {
|
||||
return {
|
||||
value: await this._frame.title()
|
||||
};
|
||||
}
|
||||
async highlight(params, metadata) {
|
||||
return await this._frame.highlight(params.selector);
|
||||
}
|
||||
async expect(params, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
let expectedValue = params.expectedValue ? (0, _jsHandleDispatcher.parseArgument)(params.expectedValue) : undefined;
|
||||
if (params.expression === 'to.match.aria' && expectedValue) expectedValue = (0, _ariaSnapshot.parseAriaSnapshot)(expectedValue);
|
||||
const result = await this._frame.expect(metadata, params.selector, {
|
||||
...params,
|
||||
expectedValue
|
||||
});
|
||||
if (result.received !== undefined) result.received = (0, _jsHandleDispatcher.serializeResult)(result.received);
|
||||
return result;
|
||||
}
|
||||
async ariaSnapshot(params, metadata) {
|
||||
return {
|
||||
snapshot: await this._frame.ariaSnapshot(metadata, params.selector, params)
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.FrameDispatcher = FrameDispatcher;
|
||||
97
node_modules/playwright-core/lib/server/dispatchers/jsHandleDispatcher.js
generated
vendored
Normal file
97
node_modules/playwright-core/lib/server/dispatchers/jsHandleDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.JSHandleDispatcher = void 0;
|
||||
exports.parseArgument = parseArgument;
|
||||
exports.parseValue = parseValue;
|
||||
exports.serializeResult = serializeResult;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
|
||||
var _serializers = require("../../protocol/serializers");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JSHandleDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, jsHandle) {
|
||||
// Do not call this directly, use createHandle() instead.
|
||||
super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', {
|
||||
preview: jsHandle.toString()
|
||||
});
|
||||
this._type_JSHandle = true;
|
||||
jsHandle._setPreviewCallback(preview => this._dispatchEvent('previewUpdated', {
|
||||
preview
|
||||
}));
|
||||
}
|
||||
async evaluateExpression(params) {
|
||||
return {
|
||||
value: serializeResult(await this._object.evaluateExpression(params.expression, {
|
||||
isFunction: params.isFunction
|
||||
}, parseArgument(params.arg)))
|
||||
};
|
||||
}
|
||||
async evaluateExpressionHandle(params) {
|
||||
const jsHandle = await this._object.evaluateExpressionHandle(params.expression, {
|
||||
isFunction: params.isFunction
|
||||
}, parseArgument(params.arg));
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle)
|
||||
};
|
||||
}
|
||||
async getProperty(params) {
|
||||
const jsHandle = await this._object.getProperty(params.name);
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle)
|
||||
};
|
||||
}
|
||||
async getPropertyList() {
|
||||
const map = await this._object.getProperties();
|
||||
const properties = [];
|
||||
for (const [name, value] of map) properties.push({
|
||||
name,
|
||||
value: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this.parentScope(), value)
|
||||
});
|
||||
return {
|
||||
properties
|
||||
};
|
||||
}
|
||||
async jsonValue() {
|
||||
return {
|
||||
value: serializeResult(await this._object.jsonValue())
|
||||
};
|
||||
}
|
||||
async dispose(_, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
this._object.dispose();
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Generic channel parser converts guids to JSHandleDispatchers,
|
||||
// and this function takes care of converting them into underlying JSHandles.
|
||||
exports.JSHandleDispatcher = JSHandleDispatcher;
|
||||
function parseArgument(arg) {
|
||||
return (0, _serializers.parseSerializedValue)(arg.value, arg.handles.map(a => a._object));
|
||||
}
|
||||
function parseValue(v) {
|
||||
return (0, _serializers.parseSerializedValue)(v, []);
|
||||
}
|
||||
function serializeResult(arg) {
|
||||
return (0, _serializers.serializeValue)(arg, value => ({
|
||||
fallThrough: value
|
||||
}));
|
||||
}
|
||||
59
node_modules/playwright-core/lib/server/dispatchers/jsonPipeDispatcher.js
generated
vendored
Normal file
59
node_modules/playwright-core/lib/server/dispatchers/jsonPipeDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.JsonPipeDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class JsonPipeDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope) {
|
||||
super(scope, {
|
||||
guid: 'jsonPipe@' + (0, _utils.createGuid)()
|
||||
}, 'JsonPipe', {});
|
||||
this._type_JsonPipe = true;
|
||||
}
|
||||
async send(params) {
|
||||
this.emit('message', params.message);
|
||||
}
|
||||
async close() {
|
||||
this.emit('close');
|
||||
if (!this._disposed) {
|
||||
this._dispatchEvent('closed', {});
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
dispatch(message) {
|
||||
if (!this._disposed) this._dispatchEvent('message', {
|
||||
message
|
||||
});
|
||||
}
|
||||
wasClosed(reason) {
|
||||
if (!this._disposed) {
|
||||
this._dispatchEvent('closed', {
|
||||
reason
|
||||
});
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
exports.JsonPipeDispatcher = JsonPipeDispatcher;
|
||||
413
node_modules/playwright-core/lib/server/dispatchers/localUtilsDispatcher.js
generated
vendored
Normal file
413
node_modules/playwright-core/lib/server/dispatchers/localUtilsDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,413 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.LocalUtilsDispatcher = void 0;
|
||||
exports.urlToWSEndpoint = urlToWSEndpoint;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _manualPromise = require("../../utils/manualPromise");
|
||||
var _utils = require("../../utils");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _zipBundle = require("../../zipBundle");
|
||||
var _zipFile = require("../../utils/zipFile");
|
||||
var _jsonPipeDispatcher = require("../dispatchers/jsonPipeDispatcher");
|
||||
var _transport = require("../transport");
|
||||
var _socksInterceptor = require("../socksInterceptor");
|
||||
var _userAgent = require("../../utils/userAgent");
|
||||
var _progress = require("../progress");
|
||||
var _network = require("../../utils/network");
|
||||
var _instrumentation = require("../../server/instrumentation");
|
||||
var _deviceDescriptors = require("../deviceDescriptors");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class LocalUtilsDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, playwright) {
|
||||
const localUtils = new _instrumentation.SdkObject(playwright, 'localUtils', 'localUtils');
|
||||
const deviceDescriptors = Object.entries(_deviceDescriptors.deviceDescriptors).map(([name, descriptor]) => ({
|
||||
name,
|
||||
descriptor
|
||||
}));
|
||||
super(scope, localUtils, 'LocalUtils', {
|
||||
deviceDescriptors
|
||||
});
|
||||
this._type_LocalUtils = void 0;
|
||||
this._harBackends = new Map();
|
||||
this._stackSessions = new Map();
|
||||
this._type_LocalUtils = true;
|
||||
}
|
||||
async zip(params) {
|
||||
const promise = new _manualPromise.ManualPromise();
|
||||
const zipFile = new _zipBundle.yazl.ZipFile();
|
||||
zipFile.on('error', error => promise.reject(error));
|
||||
const addFile = (file, name) => {
|
||||
try {
|
||||
if (_fs.default.statSync(file).isFile()) zipFile.addFile(file, name);
|
||||
} catch (e) {}
|
||||
};
|
||||
for (const entry of params.entries) addFile(entry.value, entry.name);
|
||||
|
||||
// Add stacks and the sources.
|
||||
const stackSession = params.stacksId ? this._stackSessions.get(params.stacksId) : undefined;
|
||||
if (stackSession !== null && stackSession !== void 0 && stackSession.callStacks.length) {
|
||||
await stackSession.writer;
|
||||
if (process.env.PW_LIVE_TRACE_STACKS) {
|
||||
zipFile.addFile(stackSession.file, 'trace.stacks');
|
||||
} else {
|
||||
const buffer = Buffer.from(JSON.stringify((0, _utils.serializeClientSideCallMetadata)(stackSession.callStacks)));
|
||||
zipFile.addBuffer(buffer, 'trace.stacks');
|
||||
}
|
||||
}
|
||||
|
||||
// Collect sources from stacks.
|
||||
if (params.includeSources) {
|
||||
const sourceFiles = new Set();
|
||||
for (const {
|
||||
stack
|
||||
} of (stackSession === null || stackSession === void 0 ? void 0 : stackSession.callStacks) || []) {
|
||||
if (!stack) continue;
|
||||
for (const {
|
||||
file
|
||||
} of stack) sourceFiles.add(file);
|
||||
}
|
||||
for (const sourceFile of sourceFiles) addFile(sourceFile, 'resources/src@' + (0, _utils.calculateSha1)(sourceFile) + '.txt');
|
||||
}
|
||||
if (params.mode === 'write') {
|
||||
// New file, just compress the entries.
|
||||
await _fs.default.promises.mkdir(_path.default.dirname(params.zipFile), {
|
||||
recursive: true
|
||||
});
|
||||
zipFile.end(undefined, () => {
|
||||
zipFile.outputStream.pipe(_fs.default.createWriteStream(params.zipFile)).on('close', () => promise.resolve()).on('error', error => promise.reject(error));
|
||||
});
|
||||
await promise;
|
||||
await this._deleteStackSession(params.stacksId);
|
||||
return;
|
||||
}
|
||||
|
||||
// File already exists. Repack and add new entries.
|
||||
const tempFile = params.zipFile + '.tmp';
|
||||
await _fs.default.promises.rename(params.zipFile, tempFile);
|
||||
_zipBundle.yauzl.open(tempFile, (err, inZipFile) => {
|
||||
if (err) {
|
||||
promise.reject(err);
|
||||
return;
|
||||
}
|
||||
(0, _utils.assert)(inZipFile);
|
||||
let pendingEntries = inZipFile.entryCount;
|
||||
inZipFile.on('entry', entry => {
|
||||
inZipFile.openReadStream(entry, (err, readStream) => {
|
||||
if (err) {
|
||||
promise.reject(err);
|
||||
return;
|
||||
}
|
||||
zipFile.addReadStream(readStream, entry.fileName);
|
||||
if (--pendingEntries === 0) {
|
||||
zipFile.end(undefined, () => {
|
||||
zipFile.outputStream.pipe(_fs.default.createWriteStream(params.zipFile)).on('close', () => {
|
||||
_fs.default.promises.unlink(tempFile).then(() => {
|
||||
promise.resolve();
|
||||
}).catch(error => promise.reject(error));
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
await promise;
|
||||
await this._deleteStackSession(params.stacksId);
|
||||
}
|
||||
async harOpen(params, metadata) {
|
||||
let harBackend;
|
||||
if (params.file.endsWith('.zip')) {
|
||||
const zipFile = new _zipFile.ZipFile(params.file);
|
||||
const entryNames = await zipFile.entries();
|
||||
const harEntryName = entryNames.find(e => e.endsWith('.har'));
|
||||
if (!harEntryName) return {
|
||||
error: 'Specified archive does not have a .har file'
|
||||
};
|
||||
const har = await zipFile.read(harEntryName);
|
||||
const harFile = JSON.parse(har.toString());
|
||||
harBackend = new HarBackend(harFile, null, zipFile);
|
||||
} else {
|
||||
const harFile = JSON.parse(await _fs.default.promises.readFile(params.file, 'utf-8'));
|
||||
harBackend = new HarBackend(harFile, _path.default.dirname(params.file), null);
|
||||
}
|
||||
this._harBackends.set(harBackend.id, harBackend);
|
||||
return {
|
||||
harId: harBackend.id
|
||||
};
|
||||
}
|
||||
async harLookup(params, metadata) {
|
||||
const harBackend = this._harBackends.get(params.harId);
|
||||
if (!harBackend) return {
|
||||
action: 'error',
|
||||
message: `Internal error: har was not opened`
|
||||
};
|
||||
return await harBackend.lookup(params.url, params.method, params.headers, params.postData, params.isNavigationRequest);
|
||||
}
|
||||
async harClose(params, metadata) {
|
||||
const harBackend = this._harBackends.get(params.harId);
|
||||
if (harBackend) {
|
||||
this._harBackends.delete(harBackend.id);
|
||||
harBackend.dispose();
|
||||
}
|
||||
}
|
||||
async harUnzip(params, metadata) {
|
||||
const dir = _path.default.dirname(params.zipFile);
|
||||
const zipFile = new _zipFile.ZipFile(params.zipFile);
|
||||
for (const entry of await zipFile.entries()) {
|
||||
const buffer = await zipFile.read(entry);
|
||||
if (entry === 'har.har') await _fs.default.promises.writeFile(params.harFile, buffer);else await _fs.default.promises.writeFile(_path.default.join(dir, entry), buffer);
|
||||
}
|
||||
zipFile.close();
|
||||
await _fs.default.promises.unlink(params.zipFile);
|
||||
}
|
||||
async connect(params, metadata) {
|
||||
const controller = new _progress.ProgressController(metadata, this._object);
|
||||
controller.setLogName('browser');
|
||||
return await controller.run(async progress => {
|
||||
var _params$exposeNetwork;
|
||||
const wsHeaders = {
|
||||
'User-Agent': (0, _userAgent.getUserAgent)(),
|
||||
'x-playwright-proxy': (_params$exposeNetwork = params.exposeNetwork) !== null && _params$exposeNetwork !== void 0 ? _params$exposeNetwork : '',
|
||||
...params.headers
|
||||
};
|
||||
const wsEndpoint = await urlToWSEndpoint(progress, params.wsEndpoint);
|
||||
const transport = await _transport.WebSocketTransport.connect(progress, wsEndpoint, wsHeaders, true, 'x-playwright-debug-log');
|
||||
const socksInterceptor = new _socksInterceptor.SocksInterceptor(transport, params.exposeNetwork, params.socksProxyRedirectPortForTest);
|
||||
const pipe = new _jsonPipeDispatcher.JsonPipeDispatcher(this);
|
||||
transport.onmessage = json => {
|
||||
if (socksInterceptor.interceptMessage(json)) return;
|
||||
const cb = () => {
|
||||
try {
|
||||
pipe.dispatch(json);
|
||||
} catch (e) {
|
||||
transport.close();
|
||||
}
|
||||
};
|
||||
if (params.slowMo) setTimeout(cb, params.slowMo);else cb();
|
||||
};
|
||||
pipe.on('message', message => {
|
||||
transport.send(message);
|
||||
});
|
||||
transport.onclose = reason => {
|
||||
socksInterceptor === null || socksInterceptor === void 0 || socksInterceptor.cleanup();
|
||||
pipe.wasClosed(reason);
|
||||
};
|
||||
pipe.on('close', () => transport.close());
|
||||
return {
|
||||
pipe,
|
||||
headers: transport.headers
|
||||
};
|
||||
}, params.timeout || 0);
|
||||
}
|
||||
async tracingStarted(params, metadata) {
|
||||
let tmpDir = undefined;
|
||||
if (!params.tracesDir) tmpDir = await _fs.default.promises.mkdtemp(_path.default.join(_os.default.tmpdir(), 'playwright-tracing-'));
|
||||
const traceStacksFile = _path.default.join(params.tracesDir || tmpDir, params.traceName + '.stacks');
|
||||
this._stackSessions.set(traceStacksFile, {
|
||||
callStacks: [],
|
||||
file: traceStacksFile,
|
||||
writer: Promise.resolve(),
|
||||
tmpDir
|
||||
});
|
||||
return {
|
||||
stacksId: traceStacksFile
|
||||
};
|
||||
}
|
||||
async traceDiscarded(params, metadata) {
|
||||
await this._deleteStackSession(params.stacksId);
|
||||
}
|
||||
async addStackToTracingNoReply(params, metadata) {
|
||||
for (const session of this._stackSessions.values()) {
|
||||
session.callStacks.push(params.callData);
|
||||
if (process.env.PW_LIVE_TRACE_STACKS) {
|
||||
session.writer = session.writer.then(() => {
|
||||
const buffer = Buffer.from(JSON.stringify((0, _utils.serializeClientSideCallMetadata)(session.callStacks)));
|
||||
return _fs.default.promises.writeFile(session.file, buffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
async _deleteStackSession(stacksId) {
|
||||
const session = stacksId ? this._stackSessions.get(stacksId) : undefined;
|
||||
if (!session) return;
|
||||
await session.writer;
|
||||
if (session.tmpDir) await (0, _utils.removeFolders)([session.tmpDir]);
|
||||
this._stackSessions.delete(stacksId);
|
||||
}
|
||||
}
|
||||
exports.LocalUtilsDispatcher = LocalUtilsDispatcher;
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
class HarBackend {
|
||||
constructor(harFile, baseDir, zipFile) {
|
||||
this.id = (0, _utils.createGuid)();
|
||||
this._harFile = void 0;
|
||||
this._zipFile = void 0;
|
||||
this._baseDir = void 0;
|
||||
this._harFile = harFile;
|
||||
this._baseDir = baseDir;
|
||||
this._zipFile = zipFile;
|
||||
}
|
||||
async lookup(url, method, headers, postData, isNavigationRequest) {
|
||||
let entry;
|
||||
try {
|
||||
entry = await this._harFindResponse(url, method, headers, postData);
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'error',
|
||||
message: 'HAR error: ' + e.message
|
||||
};
|
||||
}
|
||||
if (!entry) return {
|
||||
action: 'noentry'
|
||||
};
|
||||
|
||||
// If navigation is being redirected, restart it with the final url to ensure the document's url changes.
|
||||
if (entry.request.url !== url && isNavigationRequest) return {
|
||||
action: 'redirect',
|
||||
redirectURL: entry.request.url
|
||||
};
|
||||
const response = entry.response;
|
||||
try {
|
||||
const buffer = await this._loadContent(response.content);
|
||||
return {
|
||||
action: 'fulfill',
|
||||
status: response.status,
|
||||
headers: response.headers,
|
||||
body: buffer
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
action: 'error',
|
||||
message: e.message
|
||||
};
|
||||
}
|
||||
}
|
||||
async _loadContent(content) {
|
||||
const file = content._file;
|
||||
let buffer;
|
||||
if (file) {
|
||||
if (this._zipFile) buffer = await this._zipFile.read(file);else buffer = await _fs.default.promises.readFile(_path.default.resolve(this._baseDir, file));
|
||||
} else {
|
||||
buffer = Buffer.from(content.text || '', content.encoding === 'base64' ? 'base64' : 'utf-8');
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
async _harFindResponse(url, method, headers, postData) {
|
||||
const harLog = this._harFile.log;
|
||||
const visited = new Set();
|
||||
while (true) {
|
||||
const entries = [];
|
||||
for (const candidate of harLog.entries) {
|
||||
if (candidate.request.url !== url || candidate.request.method !== method) continue;
|
||||
if (method === 'POST' && postData && candidate.request.postData) {
|
||||
const buffer = await this._loadContent(candidate.request.postData);
|
||||
if (!buffer.equals(postData)) {
|
||||
const boundary = multipartBoundary(headers);
|
||||
if (!boundary) continue;
|
||||
const candidataBoundary = multipartBoundary(candidate.request.headers);
|
||||
if (!candidataBoundary) continue;
|
||||
// Try to match multipart/form-data ignroing boundary as it changes between requests.
|
||||
if (postData.toString().replaceAll(boundary, '') !== buffer.toString().replaceAll(candidataBoundary, '')) continue;
|
||||
}
|
||||
}
|
||||
entries.push(candidate);
|
||||
}
|
||||
if (!entries.length) return;
|
||||
let entry = entries[0];
|
||||
|
||||
// Disambiguate using headers - then one with most matching headers wins.
|
||||
if (entries.length > 1) {
|
||||
const list = [];
|
||||
for (const candidate of entries) {
|
||||
const matchingHeaders = countMatchingHeaders(candidate.request.headers, headers);
|
||||
list.push({
|
||||
candidate,
|
||||
matchingHeaders
|
||||
});
|
||||
}
|
||||
list.sort((a, b) => b.matchingHeaders - a.matchingHeaders);
|
||||
entry = list[0].candidate;
|
||||
}
|
||||
if (visited.has(entry)) throw new Error(`Found redirect cycle for ${url}`);
|
||||
visited.add(entry);
|
||||
|
||||
// Follow redirects.
|
||||
const locationHeader = entry.response.headers.find(h => h.name.toLowerCase() === 'location');
|
||||
if (redirectStatus.includes(entry.response.status) && locationHeader) {
|
||||
const locationURL = new URL(locationHeader.value, url);
|
||||
url = locationURL.toString();
|
||||
if ((entry.response.status === 301 || entry.response.status === 302) && method === 'POST' || entry.response.status === 303 && !['GET', 'HEAD'].includes(method)) {
|
||||
// HTTP-redirect fetch step 13 (https://fetch.spec.whatwg.org/#http-redirect-fetch)
|
||||
method = 'GET';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
var _this$_zipFile;
|
||||
(_this$_zipFile = this._zipFile) === null || _this$_zipFile === void 0 || _this$_zipFile.close();
|
||||
}
|
||||
}
|
||||
function countMatchingHeaders(harHeaders, headers) {
|
||||
const set = new Set(headers.map(h => h.name.toLowerCase() + ':' + h.value));
|
||||
let matches = 0;
|
||||
for (const h of harHeaders) {
|
||||
if (set.has(h.name.toLowerCase() + ':' + h.value)) ++matches;
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
async function urlToWSEndpoint(progress, endpointURL) {
|
||||
var _progress$timeUntilDe;
|
||||
if (endpointURL.startsWith('ws')) return endpointURL;
|
||||
progress === null || progress === void 0 || progress.log(`<ws preparing> retrieving websocket url from ${endpointURL}`);
|
||||
const fetchUrl = new URL(endpointURL);
|
||||
if (!fetchUrl.pathname.endsWith('/')) fetchUrl.pathname += '/';
|
||||
fetchUrl.pathname += 'json';
|
||||
const json = await (0, _network.fetchData)({
|
||||
url: fetchUrl.toString(),
|
||||
method: 'GET',
|
||||
timeout: (_progress$timeUntilDe = progress === null || progress === void 0 ? void 0 : progress.timeUntilDeadline()) !== null && _progress$timeUntilDe !== void 0 ? _progress$timeUntilDe : 30_000,
|
||||
headers: {
|
||||
'User-Agent': (0, _userAgent.getUserAgent)()
|
||||
}
|
||||
}, async (params, response) => {
|
||||
return new Error(`Unexpected status ${response.statusCode} when connecting to ${fetchUrl.toString()}.\n` + `This does not look like a Playwright server, try connecting via ws://.`);
|
||||
});
|
||||
progress === null || progress === void 0 || progress.throwIfAborted();
|
||||
const wsUrl = new URL(endpointURL);
|
||||
let wsEndpointPath = JSON.parse(json).wsEndpointPath;
|
||||
if (wsEndpointPath.startsWith('/')) wsEndpointPath = wsEndpointPath.substring(1);
|
||||
if (!wsUrl.pathname.endsWith('/')) wsUrl.pathname += '/';
|
||||
wsUrl.pathname += wsEndpointPath;
|
||||
wsUrl.protocol = wsUrl.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
return wsUrl.toString();
|
||||
}
|
||||
function multipartBoundary(headers) {
|
||||
const contentType = headers.find(h => h.name.toLowerCase() === 'content-type');
|
||||
if (!(contentType !== null && contentType !== void 0 && contentType.value.includes('multipart/form-data'))) return undefined;
|
||||
const boundary = contentType.value.match(/boundary=(\S+)/);
|
||||
if (boundary) return boundary[1];
|
||||
return undefined;
|
||||
}
|
||||
221
node_modules/playwright-core/lib/server/dispatchers/networkDispatchers.js
generated
vendored
Normal file
221
node_modules/playwright-core/lib/server/dispatchers/networkDispatchers.js
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WebSocketDispatcher = exports.RouteDispatcher = exports.ResponseDispatcher = exports.RequestDispatcher = exports.APIRequestContextDispatcher = void 0;
|
||||
var _network = require("../network");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _tracingDispatcher = require("./tracingDispatcher");
|
||||
var _frameDispatcher = require("./frameDispatcher");
|
||||
var _pageDispatcher = require("./pageDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class RequestDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, request) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(request);
|
||||
return result || new RequestDispatcher(scope, request);
|
||||
}
|
||||
static fromNullable(scope, request) {
|
||||
return request ? RequestDispatcher.from(scope, request) : undefined;
|
||||
}
|
||||
constructor(scope, request) {
|
||||
var _request$frame;
|
||||
const postData = request.postDataBuffer();
|
||||
// Always try to attach request to the page, if not, frame.
|
||||
const frame = request.frame();
|
||||
const page = (_request$frame = request.frame()) === null || _request$frame === void 0 ? void 0 : _request$frame._page;
|
||||
const pageDispatcher = page ? (0, _dispatcher.existingDispatcher)(page) : null;
|
||||
const frameDispatcher = frame ? _frameDispatcher.FrameDispatcher.from(scope, frame) : null;
|
||||
super(pageDispatcher || frameDispatcher || scope, request, 'Request', {
|
||||
frame: _frameDispatcher.FrameDispatcher.fromNullable(scope, request.frame()),
|
||||
serviceWorker: _pageDispatcher.WorkerDispatcher.fromNullable(scope, request.serviceWorker()),
|
||||
url: request.url(),
|
||||
resourceType: request.resourceType(),
|
||||
method: request.method(),
|
||||
postData: postData === null ? undefined : postData,
|
||||
headers: request.headers(),
|
||||
isNavigationRequest: request.isNavigationRequest(),
|
||||
redirectedFrom: RequestDispatcher.fromNullable(scope, request.redirectedFrom())
|
||||
});
|
||||
this._type_Request = void 0;
|
||||
this._browserContextDispatcher = void 0;
|
||||
this._type_Request = true;
|
||||
this._browserContextDispatcher = scope;
|
||||
}
|
||||
async rawRequestHeaders(params) {
|
||||
return {
|
||||
headers: await this._object.rawRequestHeaders()
|
||||
};
|
||||
}
|
||||
async response() {
|
||||
return {
|
||||
response: ResponseDispatcher.fromNullable(this._browserContextDispatcher, await this._object.response())
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.RequestDispatcher = RequestDispatcher;
|
||||
class ResponseDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, response) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(response);
|
||||
const requestDispatcher = RequestDispatcher.from(scope, response.request());
|
||||
return result || new ResponseDispatcher(requestDispatcher, response);
|
||||
}
|
||||
static fromNullable(scope, response) {
|
||||
return response ? ResponseDispatcher.from(scope, response) : undefined;
|
||||
}
|
||||
constructor(scope, response) {
|
||||
super(scope, response, 'Response', {
|
||||
// TODO: responses in popups can point to non-reported requests.
|
||||
request: scope,
|
||||
url: response.url(),
|
||||
status: response.status(),
|
||||
statusText: response.statusText(),
|
||||
headers: response.headers(),
|
||||
timing: response.timing(),
|
||||
fromServiceWorker: response.fromServiceWorker()
|
||||
});
|
||||
this._type_Response = true;
|
||||
}
|
||||
async body() {
|
||||
return {
|
||||
binary: await this._object.body()
|
||||
};
|
||||
}
|
||||
async securityDetails() {
|
||||
return {
|
||||
value: (await this._object.securityDetails()) || undefined
|
||||
};
|
||||
}
|
||||
async serverAddr() {
|
||||
return {
|
||||
value: (await this._object.serverAddr()) || undefined
|
||||
};
|
||||
}
|
||||
async rawResponseHeaders(params) {
|
||||
return {
|
||||
headers: await this._object.rawResponseHeaders()
|
||||
};
|
||||
}
|
||||
async sizes(params) {
|
||||
return {
|
||||
sizes: await this._object.sizes()
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.ResponseDispatcher = ResponseDispatcher;
|
||||
class RouteDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, route) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(route);
|
||||
return result || new RouteDispatcher(scope, route);
|
||||
}
|
||||
constructor(scope, route) {
|
||||
super(scope, route, 'Route', {
|
||||
// Context route can point to a non-reported request, so we send the request in the initializer.
|
||||
request: scope
|
||||
});
|
||||
this._type_Route = true;
|
||||
}
|
||||
async continue(params, metadata) {
|
||||
await this._object.continue({
|
||||
url: params.url,
|
||||
method: params.method,
|
||||
headers: params.headers,
|
||||
postData: params.postData,
|
||||
isFallback: params.isFallback
|
||||
});
|
||||
}
|
||||
async fulfill(params, metadata) {
|
||||
await this._object.fulfill(params);
|
||||
}
|
||||
async abort(params, metadata) {
|
||||
await this._object.abort(params.errorCode || 'failed');
|
||||
}
|
||||
async redirectNavigationRequest(params) {
|
||||
await this._object.redirectNavigationRequest(params.url);
|
||||
}
|
||||
}
|
||||
exports.RouteDispatcher = RouteDispatcher;
|
||||
class WebSocketDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, webSocket) {
|
||||
super(scope, webSocket, 'WebSocket', {
|
||||
url: webSocket.url()
|
||||
});
|
||||
this._type_EventTarget = true;
|
||||
this._type_WebSocket = true;
|
||||
this.addObjectListener(_network.WebSocket.Events.FrameSent, event => this._dispatchEvent('frameSent', event));
|
||||
this.addObjectListener(_network.WebSocket.Events.FrameReceived, event => this._dispatchEvent('frameReceived', event));
|
||||
this.addObjectListener(_network.WebSocket.Events.SocketError, error => this._dispatchEvent('socketError', {
|
||||
error
|
||||
}));
|
||||
this.addObjectListener(_network.WebSocket.Events.Close, () => this._dispatchEvent('close', {}));
|
||||
}
|
||||
}
|
||||
exports.WebSocketDispatcher = WebSocketDispatcher;
|
||||
class APIRequestContextDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, request) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(request);
|
||||
return result || new APIRequestContextDispatcher(scope, request);
|
||||
}
|
||||
static fromNullable(scope, request) {
|
||||
return request ? APIRequestContextDispatcher.from(scope, request) : undefined;
|
||||
}
|
||||
constructor(parentScope, request) {
|
||||
// We will reparent these to the context below.
|
||||
const tracing = _tracingDispatcher.TracingDispatcher.from(parentScope, request.tracing());
|
||||
super(parentScope, request, 'APIRequestContext', {
|
||||
tracing
|
||||
});
|
||||
this._type_APIRequestContext = true;
|
||||
this.adopt(tracing);
|
||||
}
|
||||
async storageState() {
|
||||
return this._object.storageState();
|
||||
}
|
||||
async dispose(params, metadata) {
|
||||
metadata.potentiallyClosesScope = true;
|
||||
await this._object.dispose(params);
|
||||
this._dispose();
|
||||
}
|
||||
async fetch(params, metadata) {
|
||||
const fetchResponse = await this._object.fetch(params, metadata);
|
||||
return {
|
||||
response: {
|
||||
url: fetchResponse.url,
|
||||
status: fetchResponse.status,
|
||||
statusText: fetchResponse.statusText,
|
||||
headers: fetchResponse.headers,
|
||||
fetchUid: fetchResponse.fetchUid
|
||||
}
|
||||
};
|
||||
}
|
||||
async fetchResponseBody(params) {
|
||||
return {
|
||||
binary: this._object.fetchResponses.get(params.fetchUid)
|
||||
};
|
||||
}
|
||||
async fetchLog(params) {
|
||||
const log = this._object.fetchLog.get(params.fetchUid) || [];
|
||||
return {
|
||||
log
|
||||
};
|
||||
}
|
||||
async disposeAPIResponse(params) {
|
||||
this._object.disposeResponse(params.fetchUid);
|
||||
}
|
||||
}
|
||||
exports.APIRequestContextDispatcher = APIRequestContextDispatcher;
|
||||
367
node_modules/playwright-core/lib/server/dispatchers/pageDispatcher.js
generated
vendored
Normal file
367
node_modules/playwright-core/lib/server/dispatchers/pageDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,367 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WorkerDispatcher = exports.PageDispatcher = exports.BindingCallDispatcher = void 0;
|
||||
var _page = require("../page");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _errors = require("../errors");
|
||||
var _frameDispatcher = require("./frameDispatcher");
|
||||
var _networkDispatchers = require("./networkDispatchers");
|
||||
var _jsHandleDispatcher = require("./jsHandleDispatcher");
|
||||
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
|
||||
var _artifactDispatcher = require("./artifactDispatcher");
|
||||
var _utils = require("../../utils");
|
||||
var _webSocketRouteDispatcher = require("./webSocketRouteDispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PageDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(parentScope, page) {
|
||||
return PageDispatcher.fromNullable(parentScope, page);
|
||||
}
|
||||
static fromNullable(parentScope, page) {
|
||||
if (!page) return undefined;
|
||||
const result = (0, _dispatcher.existingDispatcher)(page);
|
||||
return result || new PageDispatcher(parentScope, page);
|
||||
}
|
||||
constructor(parentScope, page) {
|
||||
// TODO: theoretically, there could be more than one frame already.
|
||||
// If we split pageCreated and pageReady, there should be no main frame during pageCreated.
|
||||
|
||||
// We will reparent it to the page below using adopt.
|
||||
const mainFrame = _frameDispatcher.FrameDispatcher.from(parentScope, page.mainFrame());
|
||||
super(parentScope, page, 'Page', {
|
||||
mainFrame,
|
||||
viewportSize: page.viewportSize() || undefined,
|
||||
isClosed: page.isClosed(),
|
||||
opener: PageDispatcher.fromNullable(parentScope, page.opener())
|
||||
});
|
||||
this._type_EventTarget = true;
|
||||
this._type_Page = true;
|
||||
this._page = void 0;
|
||||
this._subscriptions = new Set();
|
||||
this._webSocketInterceptionPatterns = [];
|
||||
this.adopt(mainFrame);
|
||||
this._page = page;
|
||||
this.addObjectListener(_page.Page.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
this.addObjectListener(_page.Page.Events.Crash, () => this._dispatchEvent('crash'));
|
||||
this.addObjectListener(_page.Page.Events.Download, download => {
|
||||
// Artifact can outlive the page, so bind to the context scope.
|
||||
this._dispatchEvent('download', {
|
||||
url: download.url,
|
||||
suggestedFilename: download.suggestedFilename(),
|
||||
artifact: _artifactDispatcher.ArtifactDispatcher.from(parentScope, download.artifact)
|
||||
});
|
||||
});
|
||||
this.addObjectListener(_page.Page.Events.FileChooser, fileChooser => this._dispatchEvent('fileChooser', {
|
||||
element: _elementHandlerDispatcher.ElementHandleDispatcher.from(mainFrame, fileChooser.element()),
|
||||
isMultiple: fileChooser.isMultiple()
|
||||
}));
|
||||
this.addObjectListener(_page.Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
|
||||
this.addObjectListener(_page.Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
|
||||
this.addObjectListener(_page.Page.Events.LocatorHandlerTriggered, uid => this._dispatchEvent('locatorHandlerTriggered', {
|
||||
uid
|
||||
}));
|
||||
this.addObjectListener(_page.Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', {
|
||||
webSocket: new _networkDispatchers.WebSocketDispatcher(this, webSocket)
|
||||
}));
|
||||
this.addObjectListener(_page.Page.Events.Worker, worker => this._dispatchEvent('worker', {
|
||||
worker: new WorkerDispatcher(this, worker)
|
||||
}));
|
||||
this.addObjectListener(_page.Page.Events.Video, artifact => this._dispatchEvent('video', {
|
||||
artifact: _artifactDispatcher.ArtifactDispatcher.from(parentScope, artifact)
|
||||
}));
|
||||
if (page._video) this._dispatchEvent('video', {
|
||||
artifact: _artifactDispatcher.ArtifactDispatcher.from(this.parentScope(), page._video)
|
||||
});
|
||||
// Ensure client knows about all frames.
|
||||
const frames = page._frameManager.frames();
|
||||
for (let i = 1; i < frames.length; i++) this._onFrameAttached(frames[i]);
|
||||
}
|
||||
page() {
|
||||
return this._page;
|
||||
}
|
||||
async setDefaultNavigationTimeoutNoReply(params, metadata) {
|
||||
this._page.setDefaultNavigationTimeout(params.timeout);
|
||||
}
|
||||
async setDefaultTimeoutNoReply(params, metadata) {
|
||||
this._page.setDefaultTimeout(params.timeout);
|
||||
}
|
||||
async exposeBinding(params, metadata) {
|
||||
await this._page.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => {
|
||||
// When reusing the context, we might have some bindings called late enough,
|
||||
// after context and page dispatchers have been disposed.
|
||||
if (this._disposed) return;
|
||||
const binding = new BindingCallDispatcher(this, params.name, !!params.needsHandle, source, args);
|
||||
this._dispatchEvent('bindingCall', {
|
||||
binding
|
||||
});
|
||||
return binding.promise();
|
||||
});
|
||||
}
|
||||
async setExtraHTTPHeaders(params, metadata) {
|
||||
await this._page.setExtraHTTPHeaders(params.headers);
|
||||
}
|
||||
async reload(params, metadata) {
|
||||
return {
|
||||
response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.reload(metadata, params))
|
||||
};
|
||||
}
|
||||
async goBack(params, metadata) {
|
||||
return {
|
||||
response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goBack(metadata, params))
|
||||
};
|
||||
}
|
||||
async goForward(params, metadata) {
|
||||
return {
|
||||
response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params))
|
||||
};
|
||||
}
|
||||
async requestGC(params, metadata) {
|
||||
await this._page.requestGC();
|
||||
}
|
||||
async registerLocatorHandler(params, metadata) {
|
||||
const uid = this._page.registerLocatorHandler(params.selector, params.noWaitAfter);
|
||||
return {
|
||||
uid
|
||||
};
|
||||
}
|
||||
async resolveLocatorHandlerNoReply(params, metadata) {
|
||||
this._page.resolveLocatorHandler(params.uid, params.remove);
|
||||
}
|
||||
async unregisterLocatorHandler(params, metadata) {
|
||||
this._page.unregisterLocatorHandler(params.uid);
|
||||
}
|
||||
async emulateMedia(params, metadata) {
|
||||
await this._page.emulateMedia({
|
||||
media: params.media,
|
||||
colorScheme: params.colorScheme,
|
||||
reducedMotion: params.reducedMotion,
|
||||
forcedColors: params.forcedColors
|
||||
});
|
||||
}
|
||||
async setViewportSize(params, metadata) {
|
||||
await this._page.setViewportSize(params.viewportSize);
|
||||
}
|
||||
async addInitScript(params, metadata) {
|
||||
await this._page.addInitScript(params.source);
|
||||
}
|
||||
async setNetworkInterceptionPatterns(params, metadata) {
|
||||
if (!params.patterns.length) {
|
||||
await this._page.setClientRequestInterceptor(undefined);
|
||||
return;
|
||||
}
|
||||
const urlMatchers = params.patterns.map(pattern => pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob);
|
||||
await this._page.setClientRequestInterceptor((route, request) => {
|
||||
const matchesSome = urlMatchers.some(urlMatch => (0, _utils.urlMatches)(this._page._browserContext._options.baseURL, request.url(), urlMatch));
|
||||
if (!matchesSome) return false;
|
||||
this._dispatchEvent('route', {
|
||||
route: _networkDispatchers.RouteDispatcher.from(_networkDispatchers.RequestDispatcher.from(this.parentScope(), request), route)
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
async setWebSocketInterceptionPatterns(params, metadata) {
|
||||
this._webSocketInterceptionPatterns = params.patterns;
|
||||
if (params.patterns.length) await _webSocketRouteDispatcher.WebSocketRouteDispatcher.installIfNeeded(this.parentScope(), this._page);
|
||||
}
|
||||
async expectScreenshot(params, metadata) {
|
||||
const mask = (params.mask || []).map(({
|
||||
frame,
|
||||
selector
|
||||
}) => ({
|
||||
frame: frame._object,
|
||||
selector
|
||||
}));
|
||||
const locator = params.locator ? {
|
||||
frame: params.locator.frame._object,
|
||||
selector: params.locator.selector
|
||||
} : undefined;
|
||||
return await this._page.expectScreenshot(metadata, {
|
||||
...params,
|
||||
locator,
|
||||
mask
|
||||
});
|
||||
}
|
||||
async screenshot(params, metadata) {
|
||||
const mask = (params.mask || []).map(({
|
||||
frame,
|
||||
selector
|
||||
}) => ({
|
||||
frame: frame._object,
|
||||
selector
|
||||
}));
|
||||
return {
|
||||
binary: await this._page.screenshot(metadata, {
|
||||
...params,
|
||||
mask
|
||||
})
|
||||
};
|
||||
}
|
||||
async close(params, metadata) {
|
||||
if (!params.runBeforeUnload) metadata.potentiallyClosesScope = true;
|
||||
await this._page.close(metadata, params);
|
||||
}
|
||||
async updateSubscription(params) {
|
||||
if (params.event === 'fileChooser') await this._page.setFileChooserIntercepted(params.enabled);
|
||||
if (params.enabled) this._subscriptions.add(params.event);else this._subscriptions.delete(params.event);
|
||||
}
|
||||
async keyboardDown(params, metadata) {
|
||||
await this._page.keyboard.down(params.key);
|
||||
}
|
||||
async keyboardUp(params, metadata) {
|
||||
await this._page.keyboard.up(params.key);
|
||||
}
|
||||
async keyboardInsertText(params, metadata) {
|
||||
await this._page.keyboard.insertText(params.text);
|
||||
}
|
||||
async keyboardType(params, metadata) {
|
||||
await this._page.keyboard.type(params.text, params);
|
||||
}
|
||||
async keyboardPress(params, metadata) {
|
||||
await this._page.keyboard.press(params.key, params);
|
||||
}
|
||||
async mouseMove(params, metadata) {
|
||||
await this._page.mouse.move(params.x, params.y, params, metadata);
|
||||
}
|
||||
async mouseDown(params, metadata) {
|
||||
await this._page.mouse.down(params, metadata);
|
||||
}
|
||||
async mouseUp(params, metadata) {
|
||||
await this._page.mouse.up(params, metadata);
|
||||
}
|
||||
async mouseClick(params, metadata) {
|
||||
await this._page.mouse.click(params.x, params.y, params, metadata);
|
||||
}
|
||||
async mouseWheel(params, metadata) {
|
||||
await this._page.mouse.wheel(params.deltaX, params.deltaY);
|
||||
}
|
||||
async touchscreenTap(params, metadata) {
|
||||
await this._page.touchscreen.tap(params.x, params.y, metadata);
|
||||
}
|
||||
async accessibilitySnapshot(params, metadata) {
|
||||
const rootAXNode = await this._page.accessibility.snapshot({
|
||||
interestingOnly: params.interestingOnly,
|
||||
root: params.root ? params.root._elementHandle : undefined
|
||||
});
|
||||
return {
|
||||
rootAXNode: rootAXNode || undefined
|
||||
};
|
||||
}
|
||||
async pdf(params, metadata) {
|
||||
if (!this._page.pdf) throw new Error('PDF generation is only supported for Headless Chromium');
|
||||
const buffer = await this._page.pdf(params);
|
||||
return {
|
||||
pdf: buffer
|
||||
};
|
||||
}
|
||||
async bringToFront(params, metadata) {
|
||||
await this._page.bringToFront();
|
||||
}
|
||||
async startJSCoverage(params, metadata) {
|
||||
const coverage = this._page.coverage;
|
||||
await coverage.startJSCoverage(params);
|
||||
}
|
||||
async stopJSCoverage(params, metadata) {
|
||||
const coverage = this._page.coverage;
|
||||
return await coverage.stopJSCoverage();
|
||||
}
|
||||
async startCSSCoverage(params, metadata) {
|
||||
const coverage = this._page.coverage;
|
||||
await coverage.startCSSCoverage(params);
|
||||
}
|
||||
async stopCSSCoverage(params, metadata) {
|
||||
const coverage = this._page.coverage;
|
||||
return await coverage.stopCSSCoverage();
|
||||
}
|
||||
_onFrameAttached(frame) {
|
||||
this._dispatchEvent('frameAttached', {
|
||||
frame: _frameDispatcher.FrameDispatcher.from(this.parentScope(), frame)
|
||||
});
|
||||
}
|
||||
_onFrameDetached(frame) {
|
||||
this._dispatchEvent('frameDetached', {
|
||||
frame: _frameDispatcher.FrameDispatcher.from(this.parentScope(), frame)
|
||||
});
|
||||
}
|
||||
_onDispose() {
|
||||
// Avoid protocol calls for the closed page.
|
||||
if (!this._page.isClosedOrClosingOrCrashed()) this._page.setClientRequestInterceptor(undefined).catch(() => {});
|
||||
}
|
||||
}
|
||||
exports.PageDispatcher = PageDispatcher;
|
||||
class WorkerDispatcher extends _dispatcher.Dispatcher {
|
||||
static fromNullable(scope, worker) {
|
||||
if (!worker) return undefined;
|
||||
const result = (0, _dispatcher.existingDispatcher)(worker);
|
||||
return result || new WorkerDispatcher(scope, worker);
|
||||
}
|
||||
constructor(scope, worker) {
|
||||
super(scope, worker, 'Worker', {
|
||||
url: worker.url()
|
||||
});
|
||||
this._type_Worker = true;
|
||||
this.addObjectListener(_page.Worker.Events.Close, () => this._dispatchEvent('close'));
|
||||
}
|
||||
async evaluateExpression(params, metadata) {
|
||||
return {
|
||||
value: (0, _jsHandleDispatcher.serializeResult)(await this._object.evaluateExpression(params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
async evaluateExpressionHandle(params, metadata) {
|
||||
return {
|
||||
handle: _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, (0, _jsHandleDispatcher.parseArgument)(params.arg)))
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.WorkerDispatcher = WorkerDispatcher;
|
||||
class BindingCallDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, name, needsHandle, source, args) {
|
||||
super(scope, {
|
||||
guid: 'bindingCall@' + (0, _utils.createGuid)()
|
||||
}, 'BindingCall', {
|
||||
frame: _frameDispatcher.FrameDispatcher.from(scope.parentScope(), source.frame),
|
||||
name,
|
||||
args: needsHandle ? undefined : args.map(_jsHandleDispatcher.serializeResult),
|
||||
handle: needsHandle ? _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(scope, args[0]) : undefined
|
||||
});
|
||||
this._type_BindingCall = true;
|
||||
this._resolve = void 0;
|
||||
this._reject = void 0;
|
||||
this._promise = void 0;
|
||||
this._promise = new Promise((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
});
|
||||
}
|
||||
promise() {
|
||||
return this._promise;
|
||||
}
|
||||
async resolve(params, metadata) {
|
||||
this._resolve((0, _jsHandleDispatcher.parseArgument)(params.result));
|
||||
this._dispose();
|
||||
}
|
||||
async reject(params, metadata) {
|
||||
this._reject((0, _errors.parseError)(params.error));
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
exports.BindingCallDispatcher = BindingCallDispatcher;
|
||||
107
node_modules/playwright-core/lib/server/dispatchers/playwrightDispatcher.js
generated
vendored
Normal file
107
node_modules/playwright-core/lib/server/dispatchers/playwrightDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.PlaywrightDispatcher = void 0;
|
||||
var _fetch = require("../fetch");
|
||||
var _socksProxy = require("../../common/socksProxy");
|
||||
var _androidDispatcher = require("./androidDispatcher");
|
||||
var _browserTypeDispatcher = require("./browserTypeDispatcher");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _electronDispatcher = require("./electronDispatcher");
|
||||
var _localUtilsDispatcher = require("./localUtilsDispatcher");
|
||||
var _networkDispatchers = require("./networkDispatchers");
|
||||
var _selectorsDispatcher = require("./selectorsDispatcher");
|
||||
var _browserDispatcher = require("./browserDispatcher");
|
||||
var _utils = require("../../utils");
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PlaywrightDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, playwright, socksProxy, preLaunchedBrowser, prelaunchedAndroidDevice) {
|
||||
const browserDispatcher = preLaunchedBrowser ? new _browserDispatcher.ConnectedBrowserDispatcher(scope, preLaunchedBrowser) : undefined;
|
||||
const android = new _androidDispatcher.AndroidDispatcher(scope, playwright.android);
|
||||
const prelaunchedAndroidDeviceDispatcher = prelaunchedAndroidDevice ? new _androidDispatcher.AndroidDeviceDispatcher(android, prelaunchedAndroidDevice) : undefined;
|
||||
super(scope, playwright, 'Playwright', {
|
||||
chromium: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.chromium),
|
||||
firefox: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.firefox),
|
||||
webkit: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.webkit),
|
||||
bidiChromium: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidiChromium),
|
||||
bidiFirefox: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidiFirefox),
|
||||
android,
|
||||
electron: new _electronDispatcher.ElectronDispatcher(scope, playwright.electron),
|
||||
utils: playwright.options.isServer ? undefined : new _localUtilsDispatcher.LocalUtilsDispatcher(scope, playwright),
|
||||
selectors: new _selectorsDispatcher.SelectorsDispatcher(scope, (browserDispatcher === null || browserDispatcher === void 0 ? void 0 : browserDispatcher.selectors) || playwright.selectors),
|
||||
preLaunchedBrowser: browserDispatcher,
|
||||
preConnectedAndroidDevice: prelaunchedAndroidDeviceDispatcher,
|
||||
socksSupport: socksProxy ? new SocksSupportDispatcher(scope, socksProxy) : undefined
|
||||
});
|
||||
this._type_Playwright = void 0;
|
||||
this._browserDispatcher = void 0;
|
||||
this._type_Playwright = true;
|
||||
this._browserDispatcher = browserDispatcher;
|
||||
}
|
||||
async newRequest(params) {
|
||||
const request = new _fetch.GlobalAPIRequestContext(this._object, params);
|
||||
return {
|
||||
request: _networkDispatchers.APIRequestContextDispatcher.from(this.parentScope(), request)
|
||||
};
|
||||
}
|
||||
async cleanup() {
|
||||
var _this$_browserDispatc;
|
||||
// Cleanup contexts upon disconnect.
|
||||
await ((_this$_browserDispatc = this._browserDispatcher) === null || _this$_browserDispatc === void 0 ? void 0 : _this$_browserDispatc.cleanupContexts());
|
||||
}
|
||||
}
|
||||
exports.PlaywrightDispatcher = PlaywrightDispatcher;
|
||||
class SocksSupportDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, socksProxy) {
|
||||
super(scope, {
|
||||
guid: 'socksSupport@' + (0, _utils.createGuid)()
|
||||
}, 'SocksSupport', {});
|
||||
this._type_SocksSupport = void 0;
|
||||
this._socksProxy = void 0;
|
||||
this._socksListeners = void 0;
|
||||
this._type_SocksSupport = true;
|
||||
this._socksProxy = socksProxy;
|
||||
this._socksListeners = [_eventsHelper.eventsHelper.addEventListener(socksProxy, _socksProxy.SocksProxy.Events.SocksRequested, payload => this._dispatchEvent('socksRequested', payload)), _eventsHelper.eventsHelper.addEventListener(socksProxy, _socksProxy.SocksProxy.Events.SocksData, payload => this._dispatchEvent('socksData', payload)), _eventsHelper.eventsHelper.addEventListener(socksProxy, _socksProxy.SocksProxy.Events.SocksClosed, payload => this._dispatchEvent('socksClosed', payload))];
|
||||
}
|
||||
async socksConnected(params) {
|
||||
var _this$_socksProxy;
|
||||
(_this$_socksProxy = this._socksProxy) === null || _this$_socksProxy === void 0 || _this$_socksProxy.socketConnected(params);
|
||||
}
|
||||
async socksFailed(params) {
|
||||
var _this$_socksProxy2;
|
||||
(_this$_socksProxy2 = this._socksProxy) === null || _this$_socksProxy2 === void 0 || _this$_socksProxy2.socketFailed(params);
|
||||
}
|
||||
async socksData(params) {
|
||||
var _this$_socksProxy3;
|
||||
(_this$_socksProxy3 = this._socksProxy) === null || _this$_socksProxy3 === void 0 || _this$_socksProxy3.sendSocketData(params);
|
||||
}
|
||||
async socksError(params) {
|
||||
var _this$_socksProxy4;
|
||||
(_this$_socksProxy4 = this._socksProxy) === null || _this$_socksProxy4 === void 0 || _this$_socksProxy4.sendSocketError(params);
|
||||
}
|
||||
async socksEnd(params) {
|
||||
var _this$_socksProxy5;
|
||||
(_this$_socksProxy5 = this._socksProxy) === null || _this$_socksProxy5 === void 0 || _this$_socksProxy5.sendSocketEnd(params);
|
||||
}
|
||||
_onDispose() {
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._socksListeners);
|
||||
}
|
||||
}
|
||||
36
node_modules/playwright-core/lib/server/dispatchers/selectorsDispatcher.js
generated
vendored
Normal file
36
node_modules/playwright-core/lib/server/dispatchers/selectorsDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.SelectorsDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class SelectorsDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, selectors) {
|
||||
super(scope, selectors, 'Selectors', {});
|
||||
this._type_Selectors = true;
|
||||
}
|
||||
async register(params) {
|
||||
await this._object.register(params.name, params.source, params.contentScript);
|
||||
}
|
||||
async setTestIdAttributeName(params) {
|
||||
this._object.setTestIdAttributeName(params.testIdAttributeName);
|
||||
}
|
||||
}
|
||||
exports.SelectorsDispatcher = SelectorsDispatcher;
|
||||
62
node_modules/playwright-core/lib/server/dispatchers/streamDispatcher.js
generated
vendored
Normal file
62
node_modules/playwright-core/lib/server/dispatchers/streamDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.StreamDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _utils = require("../../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class StreamDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, stream) {
|
||||
super(scope, {
|
||||
guid: 'stream@' + (0, _utils.createGuid)(),
|
||||
stream
|
||||
}, 'Stream', {});
|
||||
// In Node v12.9.0+ we can use readableEnded.
|
||||
this._type_Stream = true;
|
||||
this._ended = false;
|
||||
stream.once('end', () => this._ended = true);
|
||||
stream.once('error', () => this._ended = true);
|
||||
}
|
||||
async read(params) {
|
||||
const stream = this._object.stream;
|
||||
if (this._ended) return {
|
||||
binary: Buffer.from('')
|
||||
};
|
||||
if (!stream.readableLength) {
|
||||
const readyPromise = new _utils.ManualPromise();
|
||||
const done = () => readyPromise.resolve();
|
||||
stream.on('readable', done);
|
||||
stream.on('end', done);
|
||||
stream.on('error', done);
|
||||
await readyPromise;
|
||||
stream.off('readable', done);
|
||||
stream.off('end', done);
|
||||
stream.off('error', done);
|
||||
}
|
||||
const buffer = stream.read(Math.min(stream.readableLength, params.size || stream.readableLength));
|
||||
return {
|
||||
binary: buffer || Buffer.from('')
|
||||
};
|
||||
}
|
||||
async close() {
|
||||
this._object.stream.destroy();
|
||||
}
|
||||
}
|
||||
exports.StreamDispatcher = StreamDispatcher;
|
||||
64
node_modules/playwright-core/lib/server/dispatchers/tracingDispatcher.js
generated
vendored
Normal file
64
node_modules/playwright-core/lib/server/dispatchers/tracingDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TracingDispatcher = void 0;
|
||||
var _artifactDispatcher = require("./artifactDispatcher");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class TracingDispatcher extends _dispatcher.Dispatcher {
|
||||
static from(scope, tracing) {
|
||||
const result = (0, _dispatcher.existingDispatcher)(tracing);
|
||||
return result || new TracingDispatcher(scope, tracing);
|
||||
}
|
||||
constructor(scope, tracing) {
|
||||
super(scope, tracing, 'Tracing', {});
|
||||
this._type_Tracing = true;
|
||||
}
|
||||
async tracingStart(params) {
|
||||
await this._object.start(params);
|
||||
}
|
||||
async tracingStartChunk(params) {
|
||||
return await this._object.startChunk(params);
|
||||
}
|
||||
async tracingGroup(params, metadata) {
|
||||
const {
|
||||
name,
|
||||
location
|
||||
} = params;
|
||||
await this._object.group(name, location, metadata);
|
||||
}
|
||||
async tracingGroupEnd(params) {
|
||||
await this._object.groupEnd();
|
||||
}
|
||||
async tracingStopChunk(params) {
|
||||
const {
|
||||
artifact,
|
||||
entries
|
||||
} = await this._object.stopChunk(params);
|
||||
return {
|
||||
artifact: artifact ? _artifactDispatcher.ArtifactDispatcher.from(this, artifact) : undefined,
|
||||
entries
|
||||
};
|
||||
}
|
||||
async tracingStop(params) {
|
||||
await this._object.stop();
|
||||
}
|
||||
}
|
||||
exports.TracingDispatcher = TracingDispatcher;
|
||||
189
node_modules/playwright-core/lib/server/dispatchers/webSocketRouteDispatcher.js
generated
vendored
Normal file
189
node_modules/playwright-core/lib/server/dispatchers/webSocketRouteDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WebSocketRouteDispatcher = void 0;
|
||||
var _page = require("../page");
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var _utils = require("../../utils");
|
||||
var _pageDispatcher = require("./pageDispatcher");
|
||||
var webSocketMockSource = _interopRequireWildcard(require("../../generated/webSocketMockSource"));
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var _class;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
const kBindingInstalledSymbol = Symbol('webSocketRouteBindingInstalled');
|
||||
const kInitScriptInstalledSymbol = Symbol('webSocketRouteInitScriptInstalled');
|
||||
class WebSocketRouteDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, id, url, frame) {
|
||||
super(scope, {
|
||||
guid: 'webSocketRoute@' + (0, _utils.createGuid)()
|
||||
}, 'WebSocketRoute', {
|
||||
url
|
||||
});
|
||||
this._type_WebSocketRoute = true;
|
||||
this._id = void 0;
|
||||
this._frame = void 0;
|
||||
this._id = id;
|
||||
this._frame = frame;
|
||||
this._eventListeners.push(
|
||||
// When the frame navigates or detaches, there will be no more communication
|
||||
// from the mock websocket, so pretend like it was closed.
|
||||
_eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.InternalFrameNavigatedToNewDocument, frame => {
|
||||
if (frame === this._frame) this._executionContextGone();
|
||||
}), _eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.FrameDetached, frame => {
|
||||
if (frame === this._frame) this._executionContextGone();
|
||||
}), _eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.Close, () => this._executionContextGone()), _eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.Crash, () => this._executionContextGone()));
|
||||
WebSocketRouteDispatcher._idToDispatcher.set(this._id, this);
|
||||
scope._dispatchEvent('webSocketRoute', {
|
||||
webSocketRoute: this
|
||||
});
|
||||
}
|
||||
static async installIfNeeded(contextDispatcher, target) {
|
||||
const context = target instanceof _page.Page ? target.context() : target;
|
||||
if (!context[kBindingInstalledSymbol]) {
|
||||
context[kBindingInstalledSymbol] = true;
|
||||
await context.exposeBinding('__pwWebSocketBinding', false, (source, payload) => {
|
||||
if (payload.type === 'onCreate') {
|
||||
const pageDispatcher = _pageDispatcher.PageDispatcher.fromNullable(contextDispatcher, source.page);
|
||||
let scope;
|
||||
if (pageDispatcher && matchesPattern(pageDispatcher, context._options.baseURL, payload.url)) scope = pageDispatcher;else if (matchesPattern(contextDispatcher, context._options.baseURL, payload.url)) scope = contextDispatcher;
|
||||
if (scope) {
|
||||
new WebSocketRouteDispatcher(scope, payload.id, payload.url, source.frame);
|
||||
} else {
|
||||
const request = {
|
||||
id: payload.id,
|
||||
type: 'passthrough'
|
||||
};
|
||||
source.frame.evaluateExpression(`globalThis.__pwWebSocketDispatch(${JSON.stringify(request)})`).catch(() => {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
const dispatcher = WebSocketRouteDispatcher._idToDispatcher.get(payload.id);
|
||||
if (payload.type === 'onMessageFromPage') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('messageFromPage', {
|
||||
message: payload.data.data,
|
||||
isBase64: payload.data.isBase64
|
||||
});
|
||||
if (payload.type === 'onMessageFromServer') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('messageFromServer', {
|
||||
message: payload.data.data,
|
||||
isBase64: payload.data.isBase64
|
||||
});
|
||||
if (payload.type === 'onClosePage') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('closePage', {
|
||||
code: payload.code,
|
||||
reason: payload.reason,
|
||||
wasClean: payload.wasClean
|
||||
});
|
||||
if (payload.type === 'onCloseServer') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('closeServer', {
|
||||
code: payload.code,
|
||||
reason: payload.reason,
|
||||
wasClean: payload.wasClean
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!target[kInitScriptInstalledSymbol]) {
|
||||
target[kInitScriptInstalledSymbol] = true;
|
||||
await target.addInitScript(`
|
||||
(() => {
|
||||
const module = {};
|
||||
${webSocketMockSource.source}
|
||||
(module.exports.inject())(globalThis);
|
||||
})();
|
||||
`);
|
||||
}
|
||||
}
|
||||
async connect(params) {
|
||||
await this._evaluateAPIRequest({
|
||||
id: this._id,
|
||||
type: 'connect'
|
||||
});
|
||||
}
|
||||
async ensureOpened(params) {
|
||||
await this._evaluateAPIRequest({
|
||||
id: this._id,
|
||||
type: 'ensureOpened'
|
||||
});
|
||||
}
|
||||
async sendToPage(params) {
|
||||
await this._evaluateAPIRequest({
|
||||
id: this._id,
|
||||
type: 'sendToPage',
|
||||
data: {
|
||||
data: params.message,
|
||||
isBase64: params.isBase64
|
||||
}
|
||||
});
|
||||
}
|
||||
async sendToServer(params) {
|
||||
await this._evaluateAPIRequest({
|
||||
id: this._id,
|
||||
type: 'sendToServer',
|
||||
data: {
|
||||
data: params.message,
|
||||
isBase64: params.isBase64
|
||||
}
|
||||
});
|
||||
}
|
||||
async closePage(params) {
|
||||
await this._evaluateAPIRequest({
|
||||
id: this._id,
|
||||
type: 'closePage',
|
||||
code: params.code,
|
||||
reason: params.reason,
|
||||
wasClean: params.wasClean
|
||||
});
|
||||
}
|
||||
async closeServer(params) {
|
||||
await this._evaluateAPIRequest({
|
||||
id: this._id,
|
||||
type: 'closeServer',
|
||||
code: params.code,
|
||||
reason: params.reason,
|
||||
wasClean: params.wasClean
|
||||
});
|
||||
}
|
||||
async _evaluateAPIRequest(request) {
|
||||
await this._frame.evaluateExpression(`globalThis.__pwWebSocketDispatch(${JSON.stringify(request)})`).catch(() => {});
|
||||
}
|
||||
_onDispose() {
|
||||
WebSocketRouteDispatcher._idToDispatcher.delete(this._id);
|
||||
}
|
||||
_executionContextGone() {
|
||||
// We could enter here after being disposed upon page closure:
|
||||
// - first from the recursive dispose inintiated by PageDispatcher;
|
||||
// - then from our own page.on('close') listener.
|
||||
if (!this._disposed) {
|
||||
this._dispatchEvent('closePage', {
|
||||
wasClean: true
|
||||
});
|
||||
this._dispatchEvent('closeServer', {
|
||||
wasClean: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.WebSocketRouteDispatcher = WebSocketRouteDispatcher;
|
||||
_class = WebSocketRouteDispatcher;
|
||||
WebSocketRouteDispatcher._idToDispatcher = new Map();
|
||||
function matchesPattern(dispatcher, baseURL, url) {
|
||||
for (const pattern of dispatcher._webSocketInterceptionPatterns || []) {
|
||||
const urlMatch = pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob;
|
||||
if ((0, _utils.urlMatches)(baseURL, url, urlMatch)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
58
node_modules/playwright-core/lib/server/dispatchers/writableStreamDispatcher.js
generated
vendored
Normal file
58
node_modules/playwright-core/lib/server/dispatchers/writableStreamDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.WritableStreamDispatcher = void 0;
|
||||
var _dispatcher = require("./dispatcher");
|
||||
var fs = _interopRequireWildcard(require("fs"));
|
||||
var _utils = require("../../utils");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class WritableStreamDispatcher extends _dispatcher.Dispatcher {
|
||||
constructor(scope, streamOrDirectory, lastModifiedMs) {
|
||||
super(scope, {
|
||||
guid: 'writableStream@' + (0, _utils.createGuid)(),
|
||||
streamOrDirectory
|
||||
}, 'WritableStream', {});
|
||||
this._type_WritableStream = true;
|
||||
this._lastModifiedMs = void 0;
|
||||
this._lastModifiedMs = lastModifiedMs;
|
||||
}
|
||||
async write(params) {
|
||||
if (typeof this._object.streamOrDirectory === 'string') throw new Error('Cannot write to a directory');
|
||||
const stream = this._object.streamOrDirectory;
|
||||
await new Promise((fulfill, reject) => {
|
||||
stream.write(params.binary, error => {
|
||||
if (error) reject(error);else fulfill();
|
||||
});
|
||||
});
|
||||
}
|
||||
async close() {
|
||||
if (typeof this._object.streamOrDirectory === 'string') throw new Error('Cannot close a directory');
|
||||
const stream = this._object.streamOrDirectory;
|
||||
await new Promise(fulfill => stream.end(fulfill));
|
||||
if (this._lastModifiedMs) await fs.promises.utimes(this.path(), new Date(this._lastModifiedMs), new Date(this._lastModifiedMs));
|
||||
}
|
||||
path() {
|
||||
if (typeof this._object.streamOrDirectory === 'string') return this._object.streamOrDirectory;
|
||||
return this._object.streamOrDirectory.path;
|
||||
}
|
||||
}
|
||||
exports.WritableStreamDispatcher = WritableStreamDispatcher;
|
||||
888
node_modules/playwright-core/lib/server/dom.js
generated
vendored
Normal file
888
node_modules/playwright-core/lib/server/dom.js
generated
vendored
Normal file
@@ -0,0 +1,888 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.NonRecoverableDOMError = exports.FrameExecutionContext = exports.ElementHandle = void 0;
|
||||
exports.assertDone = assertDone;
|
||||
exports.isNonRecoverableDOMError = isNonRecoverableDOMError;
|
||||
exports.kUnableToAdoptErrorMessage = void 0;
|
||||
exports.throwRetargetableDOMError = throwRetargetableDOMError;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var injectedScriptSource = _interopRequireWildcard(require("../generated/injectedScriptSource"));
|
||||
var _protocolError = require("./protocolError");
|
||||
var js = _interopRequireWildcard(require("./javascript"));
|
||||
var _progress = require("./progress");
|
||||
var _utils = require("../utils");
|
||||
var _fileUploadUtils = require("./fileUploadUtils");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class NonRecoverableDOMError extends Error {}
|
||||
exports.NonRecoverableDOMError = NonRecoverableDOMError;
|
||||
function isNonRecoverableDOMError(error) {
|
||||
return error instanceof NonRecoverableDOMError;
|
||||
}
|
||||
class FrameExecutionContext extends js.ExecutionContext {
|
||||
constructor(delegate, frame, world) {
|
||||
super(frame, delegate, world || 'content-script');
|
||||
this.frame = void 0;
|
||||
this._injectedScriptPromise = void 0;
|
||||
this.world = void 0;
|
||||
this.frame = frame;
|
||||
this.world = world;
|
||||
}
|
||||
adoptIfNeeded(handle) {
|
||||
if (handle instanceof ElementHandle && handle._context !== this) return this.frame._page._delegate.adoptElementHandle(handle, this);
|
||||
return null;
|
||||
}
|
||||
async evaluate(pageFunction, arg) {
|
||||
return js.evaluate(this, true /* returnByValue */, pageFunction, arg);
|
||||
}
|
||||
async evaluateHandle(pageFunction, arg) {
|
||||
return js.evaluate(this, false /* returnByValue */, pageFunction, arg);
|
||||
}
|
||||
async evaluateExpression(expression, options, arg) {
|
||||
return js.evaluateExpression(this, expression, {
|
||||
...options,
|
||||
returnByValue: true
|
||||
}, arg);
|
||||
}
|
||||
async evaluateExpressionHandle(expression, options, arg) {
|
||||
return js.evaluateExpression(this, expression, {
|
||||
...options,
|
||||
returnByValue: false
|
||||
}, arg);
|
||||
}
|
||||
createHandle(remoteObject) {
|
||||
if (this.frame._page._delegate.isElementHandle(remoteObject)) return new ElementHandle(this, remoteObject.objectId);
|
||||
return super.createHandle(remoteObject);
|
||||
}
|
||||
injectedScript() {
|
||||
if (!this._injectedScriptPromise) {
|
||||
const custom = [];
|
||||
const selectorsRegistry = this.frame._page.context().selectors();
|
||||
for (const [name, {
|
||||
source
|
||||
}] of selectorsRegistry._engines) custom.push(`{ name: '${name}', engine: (${source}) }`);
|
||||
const sdkLanguage = this.frame.attribution.playwright.options.sdkLanguage;
|
||||
const source = `
|
||||
(() => {
|
||||
const module = {};
|
||||
${injectedScriptSource.source}
|
||||
return new (module.exports.InjectedScript())(
|
||||
globalThis,
|
||||
${(0, _utils.isUnderTest)()},
|
||||
"${sdkLanguage}",
|
||||
${JSON.stringify(selectorsRegistry.testIdAttributeName())},
|
||||
${this.frame._page._delegate.rafCountForStablePosition()},
|
||||
"${this.frame._page._browserContext._browser.options.name}",
|
||||
[${custom.join(',\n')}]
|
||||
);
|
||||
})();
|
||||
`;
|
||||
this._injectedScriptPromise = this.rawEvaluateHandle(source).then(objectId => new js.JSHandle(this, 'object', 'InjectedScript', objectId));
|
||||
}
|
||||
return this._injectedScriptPromise;
|
||||
}
|
||||
}
|
||||
exports.FrameExecutionContext = FrameExecutionContext;
|
||||
class ElementHandle extends js.JSHandle {
|
||||
constructor(context, objectId) {
|
||||
super(context, 'node', undefined, objectId);
|
||||
this.__elementhandle = true;
|
||||
this._page = void 0;
|
||||
this._frame = void 0;
|
||||
this._page = context.frame._page;
|
||||
this._frame = context.frame;
|
||||
this._initializePreview().catch(e => {});
|
||||
}
|
||||
async _initializePreview() {
|
||||
const utility = await this._context.injectedScript();
|
||||
this._setPreview(await utility.evaluate((injected, e) => 'JSHandle@' + injected.previewNode(e), this));
|
||||
}
|
||||
asElement() {
|
||||
return this;
|
||||
}
|
||||
async evaluateInUtility(pageFunction, arg) {
|
||||
try {
|
||||
const utility = await this._frame._utilityContext();
|
||||
return await utility.evaluate(pageFunction, [await utility.injectedScript(), this, arg]);
|
||||
} catch (e) {
|
||||
if (js.isJavaScriptErrorInEvaluate(e) || (0, _protocolError.isSessionClosedError)(e)) throw e;
|
||||
return 'error:notconnected';
|
||||
}
|
||||
}
|
||||
async evaluateHandleInUtility(pageFunction, arg) {
|
||||
try {
|
||||
const utility = await this._frame._utilityContext();
|
||||
return await utility.evaluateHandle(pageFunction, [await utility.injectedScript(), this, arg]);
|
||||
} catch (e) {
|
||||
if (js.isJavaScriptErrorInEvaluate(e) || (0, _protocolError.isSessionClosedError)(e)) throw e;
|
||||
return 'error:notconnected';
|
||||
}
|
||||
}
|
||||
async ownerFrame() {
|
||||
const frameId = await this._page._delegate.getOwnerFrame(this);
|
||||
if (!frameId) return null;
|
||||
const frame = this._page._frameManager.frame(frameId);
|
||||
if (frame) return frame;
|
||||
for (const page of this._page._browserContext.pages()) {
|
||||
const frame = page._frameManager.frame(frameId);
|
||||
if (frame) return frame;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async isIframeElement() {
|
||||
return this.evaluateInUtility(([injected, node]) => node && (node.nodeName === 'IFRAME' || node.nodeName === 'FRAME'), {});
|
||||
}
|
||||
async contentFrame() {
|
||||
const isFrameElement = throwRetargetableDOMError(await this.isIframeElement());
|
||||
if (!isFrameElement) return null;
|
||||
return this._page._delegate.getContentFrame(this);
|
||||
}
|
||||
async getAttribute(metadata, name) {
|
||||
return this._frame.getAttribute(metadata, ':scope', name, {}, this);
|
||||
}
|
||||
async inputValue(metadata) {
|
||||
return this._frame.inputValue(metadata, ':scope', {}, this);
|
||||
}
|
||||
async textContent(metadata) {
|
||||
return this._frame.textContent(metadata, ':scope', {}, this);
|
||||
}
|
||||
async innerText(metadata) {
|
||||
return this._frame.innerText(metadata, ':scope', {}, this);
|
||||
}
|
||||
async innerHTML(metadata) {
|
||||
return this._frame.innerHTML(metadata, ':scope', {}, this);
|
||||
}
|
||||
async dispatchEvent(metadata, type, eventInit = {}) {
|
||||
return this._frame.dispatchEvent(metadata, ':scope', type, eventInit, {}, this);
|
||||
}
|
||||
async _scrollRectIntoViewIfNeeded(rect) {
|
||||
return await this._page._delegate.scrollRectIntoViewIfNeeded(this, rect);
|
||||
}
|
||||
async _waitAndScrollIntoViewIfNeeded(progress, waitForVisible) {
|
||||
const result = await this._retryAction(progress, 'scroll into view', async () => {
|
||||
progress.log(` waiting for element to be stable`);
|
||||
const waitResult = await this.evaluateInUtility(async ([injected, node, {
|
||||
waitForVisible
|
||||
}]) => {
|
||||
return await injected.checkElementStates(node, waitForVisible ? ['visible', 'stable'] : ['stable']);
|
||||
}, {
|
||||
waitForVisible
|
||||
});
|
||||
if (waitResult) return waitResult;
|
||||
return await this._scrollRectIntoViewIfNeeded();
|
||||
}, {});
|
||||
assertDone(throwRetargetableDOMError(result));
|
||||
}
|
||||
async scrollIntoViewIfNeeded(metadata, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(progress => this._waitAndScrollIntoViewIfNeeded(progress, false /* waitForVisible */), this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async _clickablePoint() {
|
||||
const intersectQuadWithViewport = quad => {
|
||||
return quad.map(point => ({
|
||||
x: Math.min(Math.max(point.x, 0), metrics.width),
|
||||
y: Math.min(Math.max(point.y, 0), metrics.height)
|
||||
}));
|
||||
};
|
||||
const computeQuadArea = quad => {
|
||||
// Compute sum of all directed areas of adjacent triangles
|
||||
// https://en.wikipedia.org/wiki/Polygon#Simple_polygons
|
||||
let area = 0;
|
||||
for (let i = 0; i < quad.length; ++i) {
|
||||
const p1 = quad[i];
|
||||
const p2 = quad[(i + 1) % quad.length];
|
||||
area += (p1.x * p2.y - p2.x * p1.y) / 2;
|
||||
}
|
||||
return Math.abs(area);
|
||||
};
|
||||
const [quads, metrics] = await Promise.all([this._page._delegate.getContentQuads(this), this._page.mainFrame()._utilityContext().then(utility => utility.evaluate(() => ({
|
||||
width: innerWidth,
|
||||
height: innerHeight
|
||||
})))]);
|
||||
if (quads === 'error:notconnected') return quads;
|
||||
if (!quads || !quads.length) return 'error:notvisible';
|
||||
|
||||
// Allow 1x1 elements. Compensate for rounding errors by comparing with 0.99 instead.
|
||||
const filtered = quads.map(quad => intersectQuadWithViewport(quad)).filter(quad => computeQuadArea(quad) > 0.99);
|
||||
if (!filtered.length) return 'error:notinviewport';
|
||||
if (this._page._browserContext._browser.options.name === 'firefox') {
|
||||
// Firefox internally uses integer coordinates, so 8.x is converted to 8 or 9 when clicking.
|
||||
//
|
||||
// This does not work nicely for small elements. For example, 1x1 square with corners
|
||||
// (8;8) and (9;9) is targeted when clicking at (8;8) but not when clicking at (9;9).
|
||||
// So, clicking at (8.x;8.y) will sometimes click at (9;9) and miss the target.
|
||||
//
|
||||
// Therefore, we try to find an integer point within a quad to make sure we click inside the element.
|
||||
for (const quad of filtered) {
|
||||
const integerPoint = findIntegerPointInsideQuad(quad);
|
||||
if (integerPoint) return integerPoint;
|
||||
}
|
||||
}
|
||||
// Return the middle point of the first quad.
|
||||
return quadMiddlePoint(filtered[0]);
|
||||
}
|
||||
async _offsetPoint(offset) {
|
||||
const [box, border] = await Promise.all([this.boundingBox(), this.evaluateInUtility(([injected, node]) => injected.getElementBorderWidth(node), {}).catch(e => {})]);
|
||||
if (!box || !border) return 'error:notvisible';
|
||||
if (border === 'error:notconnected') return border;
|
||||
// Make point relative to the padding box to align with offsetX/offsetY.
|
||||
return {
|
||||
x: box.x + border.left + offset.x,
|
||||
y: box.y + border.top + offset.y
|
||||
};
|
||||
}
|
||||
async _retryAction(progress, actionName, action, options) {
|
||||
let retry = 0;
|
||||
// We progressively wait longer between retries, up to 500ms.
|
||||
const waitTime = [0, 20, 100, 100, 500];
|
||||
while (progress.isRunning()) {
|
||||
if (retry) {
|
||||
progress.log(`retrying ${actionName} action${options.trial ? ' (trial run)' : ''}`);
|
||||
const timeout = waitTime[Math.min(retry - 1, waitTime.length - 1)];
|
||||
if (timeout) {
|
||||
progress.log(` waiting ${timeout}ms`);
|
||||
const result = await this.evaluateInUtility(([injected, node, timeout]) => new Promise(f => setTimeout(f, timeout)), timeout);
|
||||
if (result === 'error:notconnected') return result;
|
||||
}
|
||||
} else {
|
||||
progress.log(`attempting ${actionName} action${options.trial ? ' (trial run)' : ''}`);
|
||||
}
|
||||
if (!options.skipActionPreChecks && !options.force) await this._frame._page.performActionPreChecks(progress);
|
||||
const result = await action(retry);
|
||||
++retry;
|
||||
if (result === 'error:notvisible') {
|
||||
if (options.force) throw new NonRecoverableDOMError('Element is not visible');
|
||||
progress.log(' element is not visible');
|
||||
continue;
|
||||
}
|
||||
if (result === 'error:notinviewport') {
|
||||
if (options.force) throw new NonRecoverableDOMError('Element is outside of the viewport');
|
||||
progress.log(' element is outside of the viewport');
|
||||
continue;
|
||||
}
|
||||
if (result === 'error:optionsnotfound') {
|
||||
progress.log(' did not find some options');
|
||||
continue;
|
||||
}
|
||||
if (typeof result === 'object' && 'hitTargetDescription' in result) {
|
||||
progress.log(` ${result.hitTargetDescription} intercepts pointer events`);
|
||||
continue;
|
||||
}
|
||||
if (typeof result === 'object' && 'missingState' in result) {
|
||||
progress.log(` element is not ${result.missingState}`);
|
||||
continue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return 'done';
|
||||
}
|
||||
async _retryPointerAction(progress, actionName, waitForEnabled, action, options) {
|
||||
// Note: do not perform locator handlers checkpoint to avoid moving the mouse in the middle of a drag operation.
|
||||
const skipActionPreChecks = actionName === 'move and up';
|
||||
return await this._retryAction(progress, actionName, async retry => {
|
||||
// By default, we scroll with protocol method to reveal the action point.
|
||||
// However, that might not work to scroll from under position:sticky elements
|
||||
// that overlay the target element. To fight this, we cycle through different
|
||||
// scroll alignments. This works in most scenarios.
|
||||
const scrollOptions = [undefined, {
|
||||
block: 'end',
|
||||
inline: 'end'
|
||||
}, {
|
||||
block: 'center',
|
||||
inline: 'center'
|
||||
}, {
|
||||
block: 'start',
|
||||
inline: 'start'
|
||||
}];
|
||||
const forceScrollOptions = scrollOptions[retry % scrollOptions.length];
|
||||
return await this._performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options);
|
||||
}, {
|
||||
...options,
|
||||
skipActionPreChecks
|
||||
});
|
||||
}
|
||||
async _performPointerAction(progress, actionName, waitForEnabled, action, forceScrollOptions, options) {
|
||||
const {
|
||||
force = false,
|
||||
position
|
||||
} = options;
|
||||
const doScrollIntoView = async () => {
|
||||
if (forceScrollOptions) {
|
||||
return await this.evaluateInUtility(([injected, node, options]) => {
|
||||
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) node.scrollIntoView(options);
|
||||
return 'done';
|
||||
}, forceScrollOptions);
|
||||
}
|
||||
return await this._scrollRectIntoViewIfNeeded(position ? {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
width: 0,
|
||||
height: 0
|
||||
} : undefined);
|
||||
};
|
||||
if (this._frame.parentFrame()) {
|
||||
// Best-effort scroll to make sure any iframes containing this element are scrolled
|
||||
// into view and visible, so they are not throttled.
|
||||
// See https://github.com/microsoft/playwright/issues/27196 for an example.
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
await doScrollIntoView().catch(() => {});
|
||||
}
|
||||
if (options.__testHookBeforeStable) await options.__testHookBeforeStable();
|
||||
if (!force) {
|
||||
const elementStates = waitForEnabled ? ['visible', 'enabled', 'stable'] : ['visible', 'stable'];
|
||||
progress.log(` waiting for element to be ${waitForEnabled ? 'visible, enabled and stable' : 'visible and stable'}`);
|
||||
const result = await this.evaluateInUtility(async ([injected, node, {
|
||||
elementStates
|
||||
}]) => {
|
||||
return await injected.checkElementStates(node, elementStates);
|
||||
}, {
|
||||
elementStates
|
||||
});
|
||||
if (result) return result;
|
||||
progress.log(` element is ${waitForEnabled ? 'visible, enabled and stable' : 'visible and stable'}`);
|
||||
}
|
||||
if (options.__testHookAfterStable) await options.__testHookAfterStable();
|
||||
progress.log(' scrolling into view if needed');
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
const scrolled = await doScrollIntoView();
|
||||
if (scrolled !== 'done') return scrolled;
|
||||
progress.log(' done scrolling');
|
||||
const maybePoint = position ? await this._offsetPoint(position) : await this._clickablePoint();
|
||||
if (typeof maybePoint === 'string') return maybePoint;
|
||||
const point = roundPoint(maybePoint);
|
||||
progress.metadata.point = point;
|
||||
await this.instrumentation.onBeforeInputAction(this, progress.metadata);
|
||||
let hitTargetInterceptionHandle;
|
||||
if (force) {
|
||||
progress.log(` forcing action`);
|
||||
} else {
|
||||
if (options.__testHookBeforeHitTarget) await options.__testHookBeforeHitTarget();
|
||||
const frameCheckResult = await this._checkFrameIsHitTarget(point);
|
||||
if (frameCheckResult === 'error:notconnected' || 'hitTargetDescription' in frameCheckResult) return frameCheckResult;
|
||||
const hitPoint = frameCheckResult.framePoint;
|
||||
const actionType = actionName === 'move and up' ? 'drag' : actionName === 'hover' || actionName === 'tap' ? actionName : 'mouse';
|
||||
const handle = await this.evaluateHandleInUtility(([injected, node, {
|
||||
actionType,
|
||||
hitPoint,
|
||||
trial
|
||||
}]) => injected.setupHitTargetInterceptor(node, actionType, hitPoint, trial), {
|
||||
actionType,
|
||||
hitPoint,
|
||||
trial: !!options.trial
|
||||
});
|
||||
if (handle === 'error:notconnected') return handle;
|
||||
if (!handle._objectId) {
|
||||
const error = handle.rawValue();
|
||||
if (error === 'error:notconnected') return error;
|
||||
return {
|
||||
hitTargetDescription: error
|
||||
};
|
||||
}
|
||||
hitTargetInterceptionHandle = handle;
|
||||
progress.cleanupWhenAborted(() => {
|
||||
// Do not await here, just in case the renderer is stuck (e.g. on alert)
|
||||
// and we won't be able to cleanup.
|
||||
hitTargetInterceptionHandle.evaluate(h => h.stop()).catch(e => {});
|
||||
hitTargetInterceptionHandle.dispose();
|
||||
});
|
||||
}
|
||||
const actionResult = await this._page._frameManager.waitForSignalsCreatedBy(progress, options.waitAfter === true, async () => {
|
||||
if (options.__testHookBeforePointerAction) await options.__testHookBeforePointerAction();
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
let restoreModifiers;
|
||||
if (options && options.modifiers) restoreModifiers = await this._page.keyboard.ensureModifiers(options.modifiers);
|
||||
progress.log(` performing ${actionName} action`);
|
||||
await action(point);
|
||||
if (restoreModifiers) await this._page.keyboard.ensureModifiers(restoreModifiers);
|
||||
if (hitTargetInterceptionHandle) {
|
||||
const stopHitTargetInterception = this._frame.raceAgainstEvaluationStallingEvents(() => {
|
||||
return hitTargetInterceptionHandle.evaluate(h => h.stop());
|
||||
}).catch(e => 'done').finally(() => {
|
||||
var _hitTargetInterceptio;
|
||||
(_hitTargetInterceptio = hitTargetInterceptionHandle) === null || _hitTargetInterceptio === void 0 || _hitTargetInterceptio.dispose();
|
||||
});
|
||||
if (options.waitAfter !== false) {
|
||||
// When noWaitAfter is passed, we do not want to accidentally stall on
|
||||
// non-committed navigation blocking the evaluate.
|
||||
const hitTargetResult = await stopHitTargetInterception;
|
||||
if (hitTargetResult !== 'done') return hitTargetResult;
|
||||
}
|
||||
}
|
||||
progress.log(` ${options.trial ? 'trial ' : ''}${actionName} action done`);
|
||||
progress.log(' waiting for scheduled navigations to finish');
|
||||
if (options.__testHookAfterPointerAction) await options.__testHookAfterPointerAction();
|
||||
return 'done';
|
||||
});
|
||||
if (actionResult !== 'done') return actionResult;
|
||||
progress.log(' navigations have finished');
|
||||
return 'done';
|
||||
}
|
||||
async _markAsTargetElement(metadata) {
|
||||
if (!metadata.id) return;
|
||||
await this.evaluateInUtility(([injected, node, callId]) => {
|
||||
if (node.nodeType === 1 /* Node.ELEMENT_NODE */) injected.markTargetElements(new Set([node]), callId);
|
||||
}, metadata.id);
|
||||
}
|
||||
async hover(metadata, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._hover(progress, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
_hover(progress, options) {
|
||||
return this._retryPointerAction(progress, 'hover', false /* waitForEnabled */, point => this._page.mouse.move(point.x, point.y), {
|
||||
...options,
|
||||
waitAfter: 'disabled'
|
||||
});
|
||||
}
|
||||
async click(metadata, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._click(progress, {
|
||||
...options,
|
||||
waitAfter: !options.noWaitAfter
|
||||
});
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
_click(progress, options) {
|
||||
return this._retryPointerAction(progress, 'click', true /* waitForEnabled */, point => this._page.mouse.click(point.x, point.y, options), options);
|
||||
}
|
||||
async dblclick(metadata, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._dblclick(progress, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
_dblclick(progress, options) {
|
||||
return this._retryPointerAction(progress, 'dblclick', true /* waitForEnabled */, point => this._page.mouse.dblclick(point.x, point.y, options), {
|
||||
...options,
|
||||
waitAfter: 'disabled'
|
||||
});
|
||||
}
|
||||
async tap(metadata, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._tap(progress, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
_tap(progress, options) {
|
||||
return this._retryPointerAction(progress, 'tap', true /* waitForEnabled */, point => this._page.touchscreen.tap(point.x, point.y), {
|
||||
...options,
|
||||
waitAfter: 'disabled'
|
||||
});
|
||||
}
|
||||
async selectOption(metadata, elements, values, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._selectOption(progress, elements, values, options);
|
||||
return throwRetargetableDOMError(result);
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async _selectOption(progress, elements, values, options) {
|
||||
let resultingOptions = [];
|
||||
await this._retryAction(progress, 'select option', async () => {
|
||||
await this.instrumentation.onBeforeInputAction(this, progress.metadata);
|
||||
if (!options.force) progress.log(` waiting for element to be visible and enabled`);
|
||||
const optionsToSelect = [...elements, ...values];
|
||||
const result = await this.evaluateInUtility(async ([injected, node, {
|
||||
optionsToSelect,
|
||||
force
|
||||
}]) => {
|
||||
if (!force) {
|
||||
const checkResult = await injected.checkElementStates(node, ['visible', 'enabled']);
|
||||
if (checkResult) return checkResult;
|
||||
}
|
||||
return injected.selectOptions(node, optionsToSelect);
|
||||
}, {
|
||||
optionsToSelect,
|
||||
force: options.force
|
||||
});
|
||||
if (Array.isArray(result)) {
|
||||
progress.log(' selected specified option(s)');
|
||||
resultingOptions = result;
|
||||
return 'done';
|
||||
}
|
||||
return result;
|
||||
}, options);
|
||||
return resultingOptions;
|
||||
}
|
||||
async fill(metadata, value, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._fill(progress, value, options);
|
||||
assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async _fill(progress, value, options) {
|
||||
progress.log(` fill("${value}")`);
|
||||
return await this._retryAction(progress, 'fill', async () => {
|
||||
await this.instrumentation.onBeforeInputAction(this, progress.metadata);
|
||||
if (!options.force) progress.log(' waiting for element to be visible, enabled and editable');
|
||||
const result = await this.evaluateInUtility(async ([injected, node, {
|
||||
value,
|
||||
force
|
||||
}]) => {
|
||||
if (!force) {
|
||||
const checkResult = await injected.checkElementStates(node, ['visible', 'enabled', 'editable']);
|
||||
if (checkResult) return checkResult;
|
||||
}
|
||||
return injected.fill(node, value);
|
||||
}, {
|
||||
value,
|
||||
force: options.force
|
||||
});
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
if (result === 'needsinput') {
|
||||
if (value) await this._page.keyboard.insertText(value);else await this._page.keyboard.press('Delete');
|
||||
return 'done';
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}, options);
|
||||
}
|
||||
async selectText(metadata, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
const result = await this._retryAction(progress, 'selectText', async () => {
|
||||
if (!options.force) progress.log(' waiting for element to be visible');
|
||||
return await this.evaluateInUtility(async ([injected, node, {
|
||||
force
|
||||
}]) => {
|
||||
if (!force) {
|
||||
const checkResult = await injected.checkElementStates(node, ['visible']);
|
||||
if (checkResult) return checkResult;
|
||||
}
|
||||
return injected.selectText(node);
|
||||
}, {
|
||||
force: options.force
|
||||
});
|
||||
}, options);
|
||||
assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async setInputFiles(metadata, params) {
|
||||
const inputFileItems = await (0, _fileUploadUtils.prepareFilesForUpload)(this._frame, params);
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._setInputFiles(progress, inputFileItems);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(params));
|
||||
}
|
||||
async _setInputFiles(progress, items) {
|
||||
const {
|
||||
filePayloads,
|
||||
localPaths,
|
||||
localDirectory
|
||||
} = items;
|
||||
const multiple = filePayloads && filePayloads.length > 1 || localPaths && localPaths.length > 1;
|
||||
const result = await this.evaluateHandleInUtility(([injected, node, {
|
||||
multiple,
|
||||
directoryUpload
|
||||
}]) => {
|
||||
const element = injected.retarget(node, 'follow-label');
|
||||
if (!element) return;
|
||||
if (element.tagName !== 'INPUT') throw injected.createStacklessError('Node is not an HTMLInputElement');
|
||||
const inputElement = element;
|
||||
if (multiple && !inputElement.multiple && !inputElement.webkitdirectory) throw injected.createStacklessError('Non-multiple file input can only accept single file');
|
||||
if (directoryUpload && !inputElement.webkitdirectory) throw injected.createStacklessError('File input does not support directories, pass individual files instead');
|
||||
if (!directoryUpload && inputElement.webkitdirectory) throw injected.createStacklessError('[webkitdirectory] input requires passing a path to a directory');
|
||||
return inputElement;
|
||||
}, {
|
||||
multiple,
|
||||
directoryUpload: !!localDirectory
|
||||
});
|
||||
if (result === 'error:notconnected' || !result.asElement()) return 'error:notconnected';
|
||||
const retargeted = result.asElement();
|
||||
await this.instrumentation.onBeforeInputAction(this, progress.metadata);
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
if (localPaths || localDirectory) {
|
||||
const localPathsOrDirectory = localDirectory ? [localDirectory] : localPaths;
|
||||
await Promise.all(localPathsOrDirectory.map(localPath => _fs.default.promises.access(localPath, _fs.default.constants.F_OK)));
|
||||
// Browsers traverse the given directory asynchronously and we want to ensure all files are uploaded.
|
||||
const waitForInputEvent = localDirectory ? this.evaluate(node => new Promise(fulfill => {
|
||||
node.addEventListener('input', fulfill, {
|
||||
once: true
|
||||
});
|
||||
})).catch(() => {}) : Promise.resolve();
|
||||
await this._page._delegate.setInputFilePaths(retargeted, localPathsOrDirectory);
|
||||
await waitForInputEvent;
|
||||
} else {
|
||||
await this._page._delegate.setInputFiles(retargeted, filePayloads);
|
||||
}
|
||||
return 'done';
|
||||
}
|
||||
async focus(metadata) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
await controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._focus(progress);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, 0);
|
||||
}
|
||||
async _focus(progress, resetSelectionIfNotFocused) {
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
return await this.evaluateInUtility(([injected, node, resetSelectionIfNotFocused]) => injected.focusNode(node, resetSelectionIfNotFocused), resetSelectionIfNotFocused);
|
||||
}
|
||||
async _blur(progress) {
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
return await this.evaluateInUtility(([injected, node]) => injected.blurNode(node), {});
|
||||
}
|
||||
async type(metadata, text, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._type(progress, text, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async _type(progress, text, options) {
|
||||
progress.log(`elementHandle.type("${text}")`);
|
||||
await this.instrumentation.onBeforeInputAction(this, progress.metadata);
|
||||
const result = await this._focus(progress, true /* resetSelectionIfNotFocused */);
|
||||
if (result !== 'done') return result;
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
await this._page.keyboard.type(text, options);
|
||||
return 'done';
|
||||
}
|
||||
async press(metadata, key, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
await this._markAsTargetElement(metadata);
|
||||
const result = await this._press(progress, key, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async _press(progress, key, options) {
|
||||
progress.log(`elementHandle.press("${key}")`);
|
||||
await this.instrumentation.onBeforeInputAction(this, progress.metadata);
|
||||
return this._page._frameManager.waitForSignalsCreatedBy(progress, !options.noWaitAfter, async () => {
|
||||
const result = await this._focus(progress, true /* resetSelectionIfNotFocused */);
|
||||
if (result !== 'done') return result;
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
await this._page.keyboard.press(key, options);
|
||||
return 'done';
|
||||
});
|
||||
}
|
||||
async check(metadata, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
const result = await this._setChecked(progress, true, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async uncheck(metadata, options) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
const result = await this._setChecked(progress, false, options);
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async _setChecked(progress, state, options) {
|
||||
const isChecked = async () => {
|
||||
const result = await this.evaluateInUtility(([injected, node]) => injected.elementState(node, 'checked'), {});
|
||||
return throwRetargetableDOMError(result);
|
||||
};
|
||||
await this._markAsTargetElement(progress.metadata);
|
||||
if ((await isChecked()) === state) return 'done';
|
||||
const result = await this._click(progress, {
|
||||
...options,
|
||||
waitAfter: 'disabled'
|
||||
});
|
||||
if (result !== 'done') return result;
|
||||
if (options.trial) return 'done';
|
||||
if ((await isChecked()) !== state) throw new NonRecoverableDOMError('Clicking the checkbox did not change its state');
|
||||
return 'done';
|
||||
}
|
||||
async boundingBox() {
|
||||
return this._page._delegate.getBoundingBox(this);
|
||||
}
|
||||
async ariaSnapshot() {
|
||||
return await this.evaluateInUtility(([injected, element]) => injected.ariaSnapshot(element), {});
|
||||
}
|
||||
async screenshot(metadata, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(progress => this._page._screenshotter.screenshotElement(progress, this, options), this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async querySelector(selector, options) {
|
||||
return this._frame.selectors.query(selector, options, this);
|
||||
}
|
||||
async querySelectorAll(selector) {
|
||||
return this._frame.selectors.queryAll(selector, this);
|
||||
}
|
||||
async evalOnSelector(selector, strict, expression, isFunction, arg) {
|
||||
return this._frame.evalOnSelector(selector, strict, expression, isFunction, arg, this);
|
||||
}
|
||||
async evalOnSelectorAll(selector, expression, isFunction, arg) {
|
||||
return this._frame.evalOnSelectorAll(selector, expression, isFunction, arg, this);
|
||||
}
|
||||
async isVisible(metadata) {
|
||||
return this._frame.isVisible(metadata, ':scope', {}, this);
|
||||
}
|
||||
async isHidden(metadata) {
|
||||
return this._frame.isHidden(metadata, ':scope', {}, this);
|
||||
}
|
||||
async isEnabled(metadata) {
|
||||
return this._frame.isEnabled(metadata, ':scope', {}, this);
|
||||
}
|
||||
async isDisabled(metadata) {
|
||||
return this._frame.isDisabled(metadata, ':scope', {}, this);
|
||||
}
|
||||
async isEditable(metadata) {
|
||||
return this._frame.isEditable(metadata, ':scope', {}, this);
|
||||
}
|
||||
async isChecked(metadata) {
|
||||
return this._frame.isChecked(metadata, ':scope', {}, this);
|
||||
}
|
||||
async waitForElementState(metadata, state, options = {}) {
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
return controller.run(async progress => {
|
||||
const actionName = `wait for ${state}`;
|
||||
const result = await this._retryAction(progress, actionName, async () => {
|
||||
return await this.evaluateInUtility(async ([injected, node, state]) => {
|
||||
return (await injected.checkElementStates(node, [state])) || 'done';
|
||||
}, state);
|
||||
}, {});
|
||||
assertDone(throwRetargetableDOMError(result));
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
}
|
||||
async waitForSelector(metadata, selector, options = {}) {
|
||||
return this._frame.waitForSelector(metadata, selector, options, this);
|
||||
}
|
||||
async _adoptTo(context) {
|
||||
if (this._context !== context) {
|
||||
const adopted = await this._page._delegate.adoptElementHandle(this, context);
|
||||
this.dispose();
|
||||
return adopted;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
async _checkFrameIsHitTarget(point) {
|
||||
let frame = this._frame;
|
||||
const data = [];
|
||||
while (frame.parentFrame()) {
|
||||
const frameElement = await frame.frameElement();
|
||||
const box = await frameElement.boundingBox();
|
||||
const style = await frameElement.evaluateInUtility(([injected, iframe]) => injected.describeIFrameStyle(iframe), {}).catch(e => 'error:notconnected');
|
||||
if (!box || style === 'error:notconnected') return 'error:notconnected';
|
||||
if (style === 'transformed') {
|
||||
// We cannot translate coordinates when iframe has any transform applied.
|
||||
// The best we can do right now is to skip the hitPoint check,
|
||||
// and solely rely on the event interceptor.
|
||||
return {
|
||||
framePoint: undefined
|
||||
};
|
||||
}
|
||||
// Translate from viewport coordinates to frame coordinates.
|
||||
const pointInFrame = {
|
||||
x: point.x - box.x - style.left,
|
||||
y: point.y - box.y - style.top
|
||||
};
|
||||
data.push({
|
||||
frame,
|
||||
frameElement,
|
||||
pointInFrame
|
||||
});
|
||||
frame = frame.parentFrame();
|
||||
}
|
||||
// Add main frame.
|
||||
data.push({
|
||||
frame,
|
||||
frameElement: null,
|
||||
pointInFrame: point
|
||||
});
|
||||
for (let i = data.length - 1; i > 0; i--) {
|
||||
const element = data[i - 1].frameElement;
|
||||
const point = data[i].pointInFrame;
|
||||
// Hit target in the parent frame should hit the child frame element.
|
||||
const hitTargetResult = await element.evaluateInUtility(([injected, element, hitPoint]) => {
|
||||
return injected.expectHitTarget(hitPoint, element);
|
||||
}, point);
|
||||
if (hitTargetResult !== 'done') return hitTargetResult;
|
||||
}
|
||||
return {
|
||||
framePoint: data[0].pointInFrame
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.ElementHandle = ElementHandle;
|
||||
function throwRetargetableDOMError(result) {
|
||||
if (result === 'error:notconnected') throw new Error('Element is not attached to the DOM');
|
||||
return result;
|
||||
}
|
||||
function assertDone(result) {
|
||||
// This function converts 'done' to void and ensures typescript catches unhandled errors.
|
||||
}
|
||||
function roundPoint(point) {
|
||||
return {
|
||||
x: (point.x * 100 | 0) / 100,
|
||||
y: (point.y * 100 | 0) / 100
|
||||
};
|
||||
}
|
||||
function quadMiddlePoint(quad) {
|
||||
const result = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
for (const point of quad) {
|
||||
result.x += point.x / 4;
|
||||
result.y += point.y / 4;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function triangleArea(p1, p2, p3) {
|
||||
return Math.abs(p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y)) / 2;
|
||||
}
|
||||
function isPointInsideQuad(point, quad) {
|
||||
const area1 = triangleArea(point, quad[0], quad[1]) + triangleArea(point, quad[1], quad[2]) + triangleArea(point, quad[2], quad[3]) + triangleArea(point, quad[3], quad[0]);
|
||||
const area2 = triangleArea(quad[0], quad[1], quad[2]) + triangleArea(quad[1], quad[2], quad[3]);
|
||||
// Check that point is inside the quad.
|
||||
if (Math.abs(area1 - area2) > 0.1) return false;
|
||||
// Check that point is not on the right/bottom edge, because clicking
|
||||
// there does not actually click the element.
|
||||
return point.x < Math.max(quad[0].x, quad[1].x, quad[2].x, quad[3].x) && point.y < Math.max(quad[0].y, quad[1].y, quad[2].y, quad[3].y);
|
||||
}
|
||||
function findIntegerPointInsideQuad(quad) {
|
||||
// Try all four rounding directions of the middle point.
|
||||
const point = quadMiddlePoint(quad);
|
||||
point.x = Math.floor(point.x);
|
||||
point.y = Math.floor(point.y);
|
||||
if (isPointInsideQuad(point, quad)) return point;
|
||||
point.x += 1;
|
||||
if (isPointInsideQuad(point, quad)) return point;
|
||||
point.y += 1;
|
||||
if (isPointInsideQuad(point, quad)) return point;
|
||||
point.x -= 1;
|
||||
if (isPointInsideQuad(point, quad)) return point;
|
||||
}
|
||||
const kUnableToAdoptErrorMessage = exports.kUnableToAdoptErrorMessage = 'Unable to adopt element handle from a different document';
|
||||
60
node_modules/playwright-core/lib/server/download.js
generated
vendored
Normal file
60
node_modules/playwright-core/lib/server/download.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Download = void 0;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _page = require("./page");
|
||||
var _utils = require("../utils");
|
||||
var _artifact = require("./artifact");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Download {
|
||||
constructor(page, downloadsPath, uuid, url, suggestedFilename) {
|
||||
this.artifact = void 0;
|
||||
this.url = void 0;
|
||||
this._page = void 0;
|
||||
this._suggestedFilename = void 0;
|
||||
const unaccessibleErrorMessage = page._browserContext._options.acceptDownloads === 'deny' ? 'Pass { acceptDownloads: true } when you are creating your browser context.' : undefined;
|
||||
this.artifact = new _artifact.Artifact(page, _path.default.join(downloadsPath, uuid), unaccessibleErrorMessage, () => {
|
||||
return this._page._browserContext.cancelDownload(uuid);
|
||||
});
|
||||
this._page = page;
|
||||
this.url = url;
|
||||
this._suggestedFilename = suggestedFilename;
|
||||
page._browserContext._downloads.add(this);
|
||||
if (suggestedFilename !== undefined) this._fireDownloadEvent();
|
||||
}
|
||||
page() {
|
||||
return this._page;
|
||||
}
|
||||
_filenameSuggested(suggestedFilename) {
|
||||
(0, _utils.assert)(this._suggestedFilename === undefined);
|
||||
this._suggestedFilename = suggestedFilename;
|
||||
this._fireDownloadEvent();
|
||||
}
|
||||
suggestedFilename() {
|
||||
return this._suggestedFilename;
|
||||
}
|
||||
_fireDownloadEvent() {
|
||||
this._page.instrumentation.onDownload(this._page, this);
|
||||
this._page.emit(_page.Page.Events.Download, this);
|
||||
}
|
||||
}
|
||||
exports.Download = Download;
|
||||
296
node_modules/playwright-core/lib/server/electron/electron.js
generated
vendored
Normal file
296
node_modules/playwright-core/lib/server/electron/electron.js
generated
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ElectronApplication = exports.Electron = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _crBrowser = require("../chromium/crBrowser");
|
||||
var _crConnection = require("../chromium/crConnection");
|
||||
var _crExecutionContext = require("../chromium/crExecutionContext");
|
||||
var js = _interopRequireWildcard(require("../javascript"));
|
||||
var _timeoutSettings = require("../../common/timeoutSettings");
|
||||
var _utils = require("../../utils");
|
||||
var _transport = require("../transport");
|
||||
var _processLauncher = require("../../utils/processLauncher");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _progress = require("../progress");
|
||||
var _helper = require("../helper");
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var readline = _interopRequireWildcard(require("readline"));
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _instrumentation = require("../instrumentation");
|
||||
var _crProtocolHelper = require("../chromium/crProtocolHelper");
|
||||
var _console = require("../console");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const ARTIFACTS_FOLDER = _path.default.join(_os.default.tmpdir(), 'playwright-artifacts-');
|
||||
class ElectronApplication extends _instrumentation.SdkObject {
|
||||
constructor(parent, browser, nodeConnection, process) {
|
||||
super(parent, 'electron-app');
|
||||
this._browserContext = void 0;
|
||||
this._nodeConnection = void 0;
|
||||
this._nodeSession = void 0;
|
||||
this._nodeExecutionContext = void 0;
|
||||
this._nodeElectronHandlePromise = new _utils.ManualPromise();
|
||||
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||
this._process = void 0;
|
||||
this._process = process;
|
||||
this._browserContext = browser._defaultContext;
|
||||
this._nodeConnection = nodeConnection;
|
||||
this._nodeSession = nodeConnection.rootSession;
|
||||
this._nodeSession.on('Runtime.executionContextCreated', async event => {
|
||||
if (!event.context.auxData || !event.context.auxData.isDefault) return;
|
||||
const crExecutionContext = new _crExecutionContext.CRExecutionContext(this._nodeSession, event.context);
|
||||
this._nodeExecutionContext = new js.ExecutionContext(this, crExecutionContext, 'electron');
|
||||
const {
|
||||
result: remoteObject
|
||||
} = await crExecutionContext._client.send('Runtime.evaluate', {
|
||||
expression: `require('electron')`,
|
||||
contextId: event.context.id,
|
||||
// Needed after Electron 28 to get access to require: https://github.com/microsoft/playwright/issues/28048
|
||||
includeCommandLineAPI: true
|
||||
});
|
||||
this._nodeElectronHandlePromise.resolve(new js.JSHandle(this._nodeExecutionContext, 'object', 'ElectronModule', remoteObject.objectId));
|
||||
});
|
||||
this._nodeSession.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
||||
const appClosePromise = new Promise(f => this.once(ElectronApplication.Events.Close, f));
|
||||
this._browserContext.setCustomCloseHandler(async () => {
|
||||
await this._browserContext.stopVideoRecording();
|
||||
const electronHandle = await this._nodeElectronHandlePromise;
|
||||
await electronHandle.evaluate(({
|
||||
app
|
||||
}) => app.quit()).catch(() => {});
|
||||
this._nodeConnection.close();
|
||||
await appClosePromise;
|
||||
});
|
||||
}
|
||||
async _onConsoleAPI(event) {
|
||||
if (event.executionContextId === 0) {
|
||||
// DevTools protocol stores the last 1000 console messages. These
|
||||
// messages are always reported even for removed execution contexts. In
|
||||
// this case, they are marked with executionContextId = 0 and are
|
||||
// reported upon enabling Runtime agent.
|
||||
//
|
||||
// Ignore these messages since:
|
||||
// - there's no execution context we can use to operate with message
|
||||
// arguments
|
||||
// - these messages are reported before Playwright clients can subscribe
|
||||
// to the 'console'
|
||||
// page event.
|
||||
//
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/3865
|
||||
return;
|
||||
}
|
||||
if (!this._nodeExecutionContext) return;
|
||||
const args = event.args.map(arg => this._nodeExecutionContext.createHandle(arg));
|
||||
const message = new _console.ConsoleMessage(null, event.type, undefined, args, (0, _crProtocolHelper.toConsoleMessageLocation)(event.stackTrace));
|
||||
this.emit(ElectronApplication.Events.Console, message);
|
||||
}
|
||||
async initialize() {
|
||||
await this._nodeSession.send('Runtime.enable', {});
|
||||
// Delay loading the app until browser is started and the browser targets are configured to auto-attach.
|
||||
await this._nodeSession.send('Runtime.evaluate', {
|
||||
expression: '__playwright_run()'
|
||||
});
|
||||
}
|
||||
process() {
|
||||
return this._process;
|
||||
}
|
||||
context() {
|
||||
return this._browserContext;
|
||||
}
|
||||
async close() {
|
||||
// This will call BrowserContext.setCustomCloseHandler.
|
||||
await this._browserContext.close({
|
||||
reason: 'Application exited'
|
||||
});
|
||||
}
|
||||
async browserWindow(page) {
|
||||
// Assume CRPage as Electron is always Chromium.
|
||||
const targetId = page._delegate._targetId;
|
||||
const electronHandle = await this._nodeElectronHandlePromise;
|
||||
return await electronHandle.evaluateHandle(({
|
||||
BrowserWindow,
|
||||
webContents
|
||||
}, targetId) => {
|
||||
const wc = webContents.fromDevToolsTargetId(targetId);
|
||||
return BrowserWindow.fromWebContents(wc);
|
||||
}, targetId);
|
||||
}
|
||||
}
|
||||
exports.ElectronApplication = ElectronApplication;
|
||||
ElectronApplication.Events = {
|
||||
Close: 'close',
|
||||
Console: 'console'
|
||||
};
|
||||
class Electron extends _instrumentation.SdkObject {
|
||||
constructor(playwright) {
|
||||
super(playwright, 'electron');
|
||||
}
|
||||
async launch(options) {
|
||||
const {
|
||||
args = []
|
||||
} = options;
|
||||
const controller = new _progress.ProgressController((0, _instrumentation.serverSideCallMetadata)(), this);
|
||||
controller.setLogName('browser');
|
||||
return controller.run(async progress => {
|
||||
let app = undefined;
|
||||
// --remote-debugging-port=0 must be the last playwright's argument, loader.ts relies on it.
|
||||
let electronArguments = ['--inspect=0', '--remote-debugging-port=0', ...args];
|
||||
if (_os.default.platform() === 'linux') {
|
||||
const runningAsRoot = process.geteuid && process.geteuid() === 0;
|
||||
if (runningAsRoot && electronArguments.indexOf('--no-sandbox') === -1) electronArguments.unshift('--no-sandbox');
|
||||
}
|
||||
const artifactsDir = await _fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
const browserLogsCollector = new _debugLogger.RecentLogsCollector();
|
||||
const env = options.env ? (0, _processLauncher.envArrayToObject)(options.env) : process.env;
|
||||
let command;
|
||||
if (options.executablePath) {
|
||||
command = options.executablePath;
|
||||
} else {
|
||||
try {
|
||||
// By default we fallback to the Electron App executable path.
|
||||
// 'electron/index.js' resolves to the actual Electron App.
|
||||
command = require('electron/index.js');
|
||||
} catch (error) {
|
||||
if ((error === null || error === void 0 ? void 0 : error.code) === 'MODULE_NOT_FOUND') {
|
||||
throw new Error('\n' + (0, _utils.wrapInASCIIBox)(['Electron executablePath not found!', 'Please install it using `npm install -D electron` or set the executablePath to your Electron executable.'].join('\n'), 1));
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
// Only use our own loader for non-packaged apps.
|
||||
// Packaged apps might have their own command line handling.
|
||||
electronArguments.unshift('-r', require.resolve('./loader'));
|
||||
}
|
||||
let shell = false;
|
||||
if (process.platform === 'win32') {
|
||||
// On Windows in order to run .cmd files, shell: true is required.
|
||||
// https://github.com/nodejs/node/issues/52554
|
||||
shell = true;
|
||||
// On Windows, we need to quote the executable path due to shell: true.
|
||||
command = `"${command}"`;
|
||||
// On Windows, we need to quote the arguments due to shell: true.
|
||||
electronArguments = electronArguments.map(arg => `"${arg}"`);
|
||||
}
|
||||
|
||||
// When debugging Playwright test that runs Electron, NODE_OPTIONS
|
||||
// will make the debugger attach to Electron's Node. But Playwright
|
||||
// also needs to attach to drive the automation. Disable external debugging.
|
||||
delete env.NODE_OPTIONS;
|
||||
const {
|
||||
launchedProcess,
|
||||
gracefullyClose,
|
||||
kill
|
||||
} = await (0, _processLauncher.launchProcess)({
|
||||
command,
|
||||
args: electronArguments,
|
||||
env,
|
||||
log: message => {
|
||||
progress.log(message);
|
||||
browserLogsCollector.log(message);
|
||||
},
|
||||
shell,
|
||||
stdio: 'pipe',
|
||||
cwd: options.cwd,
|
||||
tempDirectories: [artifactsDir],
|
||||
attemptToGracefullyClose: () => app.close(),
|
||||
handleSIGINT: true,
|
||||
handleSIGTERM: true,
|
||||
handleSIGHUP: true,
|
||||
onExit: () => {
|
||||
var _app;
|
||||
return (_app = app) === null || _app === void 0 ? void 0 : _app.emit(ElectronApplication.Events.Close);
|
||||
}
|
||||
});
|
||||
|
||||
// All waitForLines must be started immediately.
|
||||
// Otherwise the lines might come before we are ready.
|
||||
const waitForXserverError = new Promise(async (resolve, reject) => {
|
||||
waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => reject(new Error(['Unable to open X display!', `================================`, 'Most likely this is because there is no X server available.', "Use 'xvfb-run' on Linux to launch your tests with an emulated display server.", "For example: 'xvfb-run npm run test:e2e'", `================================`, progress.metadata.log].join('\n')))).catch(() => {});
|
||||
});
|
||||
const nodeMatchPromise = waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
|
||||
const chromeMatchPromise = waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
|
||||
const debuggerDisconnectPromise = waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./);
|
||||
const nodeMatch = await nodeMatchPromise;
|
||||
const nodeTransport = await _transport.WebSocketTransport.connect(progress, nodeMatch[1]);
|
||||
const nodeConnection = new _crConnection.CRConnection(nodeTransport, _helper.helper.debugProtocolLogger(), browserLogsCollector);
|
||||
|
||||
// Immediately release exiting process under debug.
|
||||
debuggerDisconnectPromise.then(() => {
|
||||
nodeTransport.close();
|
||||
}).catch(() => {});
|
||||
const chromeMatch = await Promise.race([chromeMatchPromise, waitForXserverError]);
|
||||
const chromeTransport = await _transport.WebSocketTransport.connect(progress, chromeMatch[1]);
|
||||
const browserProcess = {
|
||||
onclose: undefined,
|
||||
process: launchedProcess,
|
||||
close: gracefullyClose,
|
||||
kill
|
||||
};
|
||||
const contextOptions = {
|
||||
...options,
|
||||
noDefaultViewport: true
|
||||
};
|
||||
const browserOptions = {
|
||||
name: 'electron',
|
||||
isChromium: true,
|
||||
headful: true,
|
||||
persistent: contextOptions,
|
||||
browserProcess,
|
||||
protocolLogger: _helper.helper.debugProtocolLogger(),
|
||||
browserLogsCollector,
|
||||
artifactsDir,
|
||||
downloadsPath: artifactsDir,
|
||||
tracesDir: options.tracesDir || artifactsDir,
|
||||
originalLaunchOptions: {}
|
||||
};
|
||||
(0, _browserContext.validateBrowserContextOptions)(contextOptions, browserOptions);
|
||||
const browser = await _crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
|
||||
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
|
||||
await app.initialize();
|
||||
return app;
|
||||
}, _timeoutSettings.TimeoutSettings.launchTimeout(options));
|
||||
}
|
||||
}
|
||||
exports.Electron = Electron;
|
||||
function waitForLine(progress, process, regex) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stderr
|
||||
});
|
||||
const failError = new Error('Process failed to launch!');
|
||||
const listeners = [_eventsHelper.eventsHelper.addEventListener(rl, 'line', onLine), _eventsHelper.eventsHelper.addEventListener(rl, 'close', reject.bind(null, failError)), _eventsHelper.eventsHelper.addEventListener(process, 'exit', reject.bind(null, failError)),
|
||||
// It is Ok to remove error handler because we did not create process and there is another listener.
|
||||
_eventsHelper.eventsHelper.addEventListener(process, 'error', reject.bind(null, failError))];
|
||||
progress.cleanupWhenAborted(cleanup);
|
||||
function onLine(line) {
|
||||
const match = line.match(regex);
|
||||
if (!match) return;
|
||||
cleanup();
|
||||
resolve(match);
|
||||
}
|
||||
function cleanup() {
|
||||
_eventsHelper.eventsHelper.removeEventListeners(listeners);
|
||||
}
|
||||
});
|
||||
}
|
||||
57
node_modules/playwright-core/lib/server/electron/loader.js
generated
vendored
Normal file
57
node_modules/playwright-core/lib/server/electron/loader.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {
|
||||
app
|
||||
} = require('electron');
|
||||
const {
|
||||
chromiumSwitches
|
||||
} = require('../chromium/chromiumSwitches');
|
||||
|
||||
// Always pass user arguments first, see https://github.com/microsoft/playwright/issues/16614 and
|
||||
// https://github.com/microsoft/playwright/issues/29198.
|
||||
// [Electron, -r, loader.js[, --no-sandbox>], --inspect=0, --remote-debugging-port=0, ...args]
|
||||
process.argv.splice(1, process.argv.indexOf('--remote-debugging-port=0'));
|
||||
for (const arg of chromiumSwitches) {
|
||||
const match = arg.match(/--([^=]*)=?(.*)/);
|
||||
app.commandLine.appendSwitch(match[1], match[2]);
|
||||
}
|
||||
|
||||
// Defer ready event.
|
||||
const originalWhenReady = app.whenReady();
|
||||
const originalEmit = app.emit.bind(app);
|
||||
let readyEventArgs;
|
||||
app.emit = (event, ...args) => {
|
||||
if (event === 'ready') {
|
||||
readyEventArgs = args;
|
||||
return app.listenerCount('ready') > 0;
|
||||
}
|
||||
return originalEmit(event, ...args);
|
||||
};
|
||||
let isReady = false;
|
||||
let whenReadyCallback;
|
||||
const whenReadyPromise = new Promise(f => whenReadyCallback = f);
|
||||
app.isReady = () => isReady;
|
||||
app.whenReady = () => whenReadyPromise;
|
||||
globalThis.__playwright_run = async () => {
|
||||
// Wait for app to be ready to avoid browser initialization races.
|
||||
const event = await originalWhenReady;
|
||||
isReady = true;
|
||||
whenReadyCallback(event);
|
||||
originalEmit('ready', ...readyEventArgs);
|
||||
};
|
||||
68
node_modules/playwright-core/lib/server/errors.js
generated
vendored
Normal file
68
node_modules/playwright-core/lib/server/errors.js
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.TimeoutError = exports.TargetClosedError = void 0;
|
||||
exports.isTargetClosedError = isTargetClosedError;
|
||||
exports.parseError = parseError;
|
||||
exports.serializeError = serializeError;
|
||||
var _utils = require("../utils");
|
||||
var _serializers = require("../protocol/serializers");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class CustomError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
}
|
||||
class TimeoutError extends CustomError {}
|
||||
exports.TimeoutError = TimeoutError;
|
||||
class TargetClosedError extends CustomError {
|
||||
constructor(cause, logs) {
|
||||
super((cause || 'Target page, context or browser has been closed') + (logs || ''));
|
||||
}
|
||||
}
|
||||
exports.TargetClosedError = TargetClosedError;
|
||||
function isTargetClosedError(error) {
|
||||
return error instanceof TargetClosedError || error.name === 'TargetClosedError';
|
||||
}
|
||||
function serializeError(e) {
|
||||
if ((0, _utils.isError)(e)) return {
|
||||
error: {
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
name: e.name
|
||||
}
|
||||
};
|
||||
return {
|
||||
value: (0, _serializers.serializeValue)(e, value => ({
|
||||
fallThrough: value
|
||||
}))
|
||||
};
|
||||
}
|
||||
function parseError(error) {
|
||||
if (!error.error) {
|
||||
if (error.value === undefined) throw new Error('Serialized error must have either an error or a value');
|
||||
return (0, _serializers.parseSerializedValue)(error.value, undefined);
|
||||
}
|
||||
const e = new Error(error.error.message);
|
||||
e.stack = error.error.stack || '';
|
||||
e.name = error.error.name;
|
||||
return e;
|
||||
}
|
||||
669
node_modules/playwright-core/lib/server/fetch.js
generated
vendored
Normal file
669
node_modules/playwright-core/lib/server/fetch.js
generated
vendored
Normal file
@@ -0,0 +1,669 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.GlobalAPIRequestContext = exports.BrowserContextAPIRequestContext = exports.APIRequestContext = void 0;
|
||||
exports.createProxyAgent = createProxyAgent;
|
||||
var _http = _interopRequireDefault(require("http"));
|
||||
var _https = _interopRequireDefault(require("https"));
|
||||
var _stream = require("stream");
|
||||
var _zlib = _interopRequireDefault(require("zlib"));
|
||||
var _timeoutSettings = require("../common/timeoutSettings");
|
||||
var _userAgent = require("../utils/userAgent");
|
||||
var _utils = require("../utils");
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
var _browserContext = require("./browserContext");
|
||||
var _cookieStore = require("./cookieStore");
|
||||
var _formData = require("./formData");
|
||||
var _happyEyeballs = require("../utils/happy-eyeballs");
|
||||
var _instrumentation = require("./instrumentation");
|
||||
var _progress = require("./progress");
|
||||
var _tracing = require("./trace/recorder/tracing");
|
||||
var _socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
|
||||
var _tls = require("tls");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class APIRequestContext extends _instrumentation.SdkObject {
|
||||
static findResponseBody(guid) {
|
||||
for (const request of APIRequestContext.allInstances) {
|
||||
const body = request.fetchResponses.get(guid);
|
||||
if (body) return body;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
constructor(parent) {
|
||||
super(parent, 'request-context');
|
||||
this.fetchResponses = new Map();
|
||||
this.fetchLog = new Map();
|
||||
this._activeProgressControllers = new Set();
|
||||
this._closeReason = void 0;
|
||||
APIRequestContext.allInstances.add(this);
|
||||
}
|
||||
_disposeImpl() {
|
||||
APIRequestContext.allInstances.delete(this);
|
||||
this.fetchResponses.clear();
|
||||
this.fetchLog.clear();
|
||||
this.emit(APIRequestContext.Events.Dispose);
|
||||
}
|
||||
disposeResponse(fetchUid) {
|
||||
this.fetchResponses.delete(fetchUid);
|
||||
this.fetchLog.delete(fetchUid);
|
||||
}
|
||||
_storeResponseBody(body) {
|
||||
const uid = (0, _utils.createGuid)();
|
||||
this.fetchResponses.set(uid, body);
|
||||
return uid;
|
||||
}
|
||||
async fetch(params, metadata) {
|
||||
var _params$method;
|
||||
const defaults = this._defaultOptions();
|
||||
const headers = {
|
||||
'user-agent': defaults.userAgent,
|
||||
'accept': '*/*',
|
||||
'accept-encoding': 'gzip,deflate,br'
|
||||
};
|
||||
if (defaults.extraHTTPHeaders) {
|
||||
for (const {
|
||||
name,
|
||||
value
|
||||
} of defaults.extraHTTPHeaders) setHeader(headers, name, value);
|
||||
}
|
||||
if (params.headers) {
|
||||
for (const {
|
||||
name,
|
||||
value
|
||||
} of params.headers) setHeader(headers, name, value);
|
||||
}
|
||||
const requestUrl = new URL((0, _utils.constructURLBasedOnBaseURL)(defaults.baseURL, params.url));
|
||||
if (params.encodedParams) {
|
||||
requestUrl.search = params.encodedParams;
|
||||
} else if (params.params) {
|
||||
for (const {
|
||||
name,
|
||||
value
|
||||
} of params.params) requestUrl.searchParams.append(name, value);
|
||||
}
|
||||
const credentials = this._getHttpCredentials(requestUrl);
|
||||
if ((credentials === null || credentials === void 0 ? void 0 : credentials.send) === 'always') setBasicAuthorizationHeader(headers, credentials);
|
||||
const method = ((_params$method = params.method) === null || _params$method === void 0 ? void 0 : _params$method.toUpperCase()) || 'GET';
|
||||
const proxy = defaults.proxy;
|
||||
let agent;
|
||||
// We skip 'per-context' in order to not break existing users. 'per-context' was previously used to
|
||||
// workaround an upstream Chromium bug. Can be removed in the future.
|
||||
if (proxy && proxy.server !== 'per-context' && !shouldBypassProxy(requestUrl, proxy.bypass)) agent = createProxyAgent(proxy);
|
||||
const timeout = defaults.timeoutSettings.timeout(params);
|
||||
const deadline = timeout && (0, _utils.monotonicTime)() + timeout;
|
||||
const options = {
|
||||
method,
|
||||
headers,
|
||||
agent,
|
||||
maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
|
||||
timeout,
|
||||
deadline,
|
||||
...(0, _socksClientCertificatesInterceptor.getMatchingTLSOptionsForOrigin)(this._defaultOptions().clientCertificates, requestUrl.origin),
|
||||
__testHookLookup: params.__testHookLookup
|
||||
};
|
||||
// rejectUnauthorized = undefined is treated as true in Node.js 12.
|
||||
if (params.ignoreHTTPSErrors || defaults.ignoreHTTPSErrors) options.rejectUnauthorized = false;
|
||||
const postData = serializePostData(params, headers);
|
||||
if (postData) setHeader(headers, 'content-length', String(postData.byteLength));
|
||||
const controller = new _progress.ProgressController(metadata, this);
|
||||
const fetchResponse = await controller.run(progress => {
|
||||
return this._sendRequestWithRetries(progress, requestUrl, options, postData, params.maxRetries);
|
||||
});
|
||||
const fetchUid = this._storeResponseBody(fetchResponse.body);
|
||||
this.fetchLog.set(fetchUid, controller.metadata.log);
|
||||
if (params.failOnStatusCode && (fetchResponse.status < 200 || fetchResponse.status >= 400)) {
|
||||
let responseText = '';
|
||||
if (fetchResponse.body.byteLength) {
|
||||
let text = fetchResponse.body.toString('utf8');
|
||||
if (text.length > 1000) text = text.substring(0, 997) + '...';
|
||||
responseText = `\nResponse text:\n${text}`;
|
||||
}
|
||||
throw new Error(`${fetchResponse.status} ${fetchResponse.statusText}${responseText}`);
|
||||
}
|
||||
return {
|
||||
...fetchResponse,
|
||||
fetchUid
|
||||
};
|
||||
}
|
||||
_parseSetCookieHeader(responseUrl, setCookie) {
|
||||
if (!setCookie) return [];
|
||||
const url = new URL(responseUrl);
|
||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
|
||||
const defaultPath = '/' + url.pathname.substr(1).split('/').slice(0, -1).join('/');
|
||||
const cookies = [];
|
||||
for (const header of setCookie) {
|
||||
// Decode cookie value?
|
||||
const cookie = parseCookie(header);
|
||||
if (!cookie) continue;
|
||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3
|
||||
if (!cookie.domain) cookie.domain = url.hostname;else (0, _utils.assert)(cookie.domain.startsWith('.') || !cookie.domain.includes('.'));
|
||||
if (!(0, _cookieStore.domainMatches)(url.hostname, cookie.domain)) continue;
|
||||
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.4
|
||||
if (!cookie.path || !cookie.path.startsWith('/')) cookie.path = defaultPath;
|
||||
cookies.push(cookie);
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
async _updateRequestCookieHeader(url, headers) {
|
||||
if (getHeader(headers, 'cookie') !== undefined) return;
|
||||
const cookies = await this._cookies(url);
|
||||
if (cookies.length) {
|
||||
const valueArray = cookies.map(c => `${c.name}=${c.value}`);
|
||||
setHeader(headers, 'cookie', valueArray.join('; '));
|
||||
}
|
||||
}
|
||||
async _sendRequestWithRetries(progress, url, options, postData, maxRetries) {
|
||||
var _maxRetries;
|
||||
(_maxRetries = maxRetries) !== null && _maxRetries !== void 0 ? _maxRetries : maxRetries = 0;
|
||||
let backoff = 250;
|
||||
for (let i = 0; i <= maxRetries; i++) {
|
||||
try {
|
||||
return await this._sendRequest(progress, url, options, postData);
|
||||
} catch (e) {
|
||||
e = (0, _socksClientCertificatesInterceptor.rewriteOpenSSLErrorIfNeeded)(e);
|
||||
if (maxRetries === 0) throw e;
|
||||
if (i === maxRetries || options.deadline && (0, _utils.monotonicTime)() + backoff > options.deadline) throw new Error(`Failed after ${i + 1} attempt(s): ${e}`);
|
||||
// Retry on connection reset only.
|
||||
if (e.code !== 'ECONNRESET') throw e;
|
||||
progress.log(` Received ECONNRESET, will retry after ${backoff}ms.`);
|
||||
await new Promise(f => setTimeout(f, backoff));
|
||||
backoff *= 2;
|
||||
}
|
||||
}
|
||||
throw new Error('Unreachable');
|
||||
}
|
||||
async _sendRequest(progress, url, options, postData) {
|
||||
var _getHeader;
|
||||
await this._updateRequestCookieHeader(url, options.headers);
|
||||
const requestCookies = ((_getHeader = getHeader(options.headers, 'cookie')) === null || _getHeader === void 0 ? void 0 : _getHeader.split(';').map(p => {
|
||||
const [name, value] = p.split('=').map(v => v.trim());
|
||||
return {
|
||||
name,
|
||||
value
|
||||
};
|
||||
})) || [];
|
||||
const requestEvent = {
|
||||
url,
|
||||
method: options.method,
|
||||
headers: options.headers,
|
||||
cookies: requestCookies,
|
||||
postData
|
||||
};
|
||||
this.emit(APIRequestContext.Events.Request, requestEvent);
|
||||
return new Promise((fulfill, reject) => {
|
||||
const requestConstructor = (url.protocol === 'https:' ? _https.default : _http.default).request;
|
||||
// If we have a proxy agent already, do not override it.
|
||||
const agent = options.agent || (url.protocol === 'https:' ? _happyEyeballs.httpsHappyEyeballsAgent : _happyEyeballs.httpHappyEyeballsAgent);
|
||||
const requestOptions = {
|
||||
...options,
|
||||
agent
|
||||
};
|
||||
const startAt = (0, _utils.monotonicTime)();
|
||||
let reusedSocketAt;
|
||||
let dnsLookupAt;
|
||||
let tcpConnectionAt;
|
||||
let tlsHandshakeAt;
|
||||
let requestFinishAt;
|
||||
let serverIPAddress;
|
||||
let serverPort;
|
||||
let securityDetails;
|
||||
const listeners = [];
|
||||
const request = requestConstructor(url, requestOptions, async response => {
|
||||
const responseAt = (0, _utils.monotonicTime)();
|
||||
const notifyRequestFinished = body => {
|
||||
var _tlsHandshakeAt;
|
||||
const endAt = (0, _utils.monotonicTime)();
|
||||
// spec: http://www.softwareishard.com/blog/har-12-spec/#timings
|
||||
const connectEnd = (_tlsHandshakeAt = tlsHandshakeAt) !== null && _tlsHandshakeAt !== void 0 ? _tlsHandshakeAt : tcpConnectionAt;
|
||||
const timings = {
|
||||
send: requestFinishAt - startAt,
|
||||
wait: responseAt - requestFinishAt,
|
||||
receive: endAt - responseAt,
|
||||
dns: dnsLookupAt ? dnsLookupAt - startAt : -1,
|
||||
connect: connectEnd ? connectEnd - startAt : -1,
|
||||
// "If [ssl] is defined then the time is also included in the connect field "
|
||||
ssl: tlsHandshakeAt ? tlsHandshakeAt - tcpConnectionAt : -1,
|
||||
blocked: reusedSocketAt ? reusedSocketAt - startAt : -1
|
||||
};
|
||||
const requestFinishedEvent = {
|
||||
requestEvent,
|
||||
httpVersion: response.httpVersion,
|
||||
statusCode: response.statusCode || 0,
|
||||
statusMessage: response.statusMessage || '',
|
||||
headers: response.headers,
|
||||
rawHeaders: response.rawHeaders,
|
||||
cookies,
|
||||
body,
|
||||
timings,
|
||||
serverIPAddress,
|
||||
serverPort,
|
||||
securityDetails
|
||||
};
|
||||
this.emit(APIRequestContext.Events.RequestFinished, requestFinishedEvent);
|
||||
};
|
||||
progress.log(`← ${response.statusCode} ${response.statusMessage}`);
|
||||
for (const [name, value] of Object.entries(response.headers)) progress.log(` ${name}: ${value}`);
|
||||
const cookies = this._parseSetCookieHeader(response.url || url.toString(), response.headers['set-cookie']);
|
||||
if (cookies.length) {
|
||||
try {
|
||||
await this._addCookies(cookies);
|
||||
} catch (e) {
|
||||
// Cookie value is limited by 4096 characters in the browsers. If setCookies failed,
|
||||
// we try setting each cookie individually just in case only some of them are bad.
|
||||
await Promise.all(cookies.map(c => this._addCookies([c]).catch(() => {})));
|
||||
}
|
||||
}
|
||||
if (redirectStatus.includes(response.statusCode) && options.maxRedirects >= 0) {
|
||||
var _response$headers$loc;
|
||||
if (!options.maxRedirects) {
|
||||
reject(new Error('Max redirect count exceeded'));
|
||||
request.destroy();
|
||||
return;
|
||||
}
|
||||
const headers = {
|
||||
...options.headers
|
||||
};
|
||||
removeHeader(headers, `cookie`);
|
||||
|
||||
// HTTP-redirect fetch step 13 (https://fetch.spec.whatwg.org/#http-redirect-fetch)
|
||||
const status = response.statusCode;
|
||||
let method = options.method;
|
||||
if ((status === 301 || status === 302) && method === 'POST' || status === 303 && !['GET', 'HEAD'].includes(method)) {
|
||||
method = 'GET';
|
||||
postData = undefined;
|
||||
removeHeader(headers, `content-encoding`);
|
||||
removeHeader(headers, `content-language`);
|
||||
removeHeader(headers, `content-length`);
|
||||
removeHeader(headers, `content-location`);
|
||||
removeHeader(headers, `content-type`);
|
||||
}
|
||||
const redirectOptions = {
|
||||
method,
|
||||
headers,
|
||||
agent: options.agent,
|
||||
maxRedirects: options.maxRedirects - 1,
|
||||
timeout: options.timeout,
|
||||
deadline: options.deadline,
|
||||
...(0, _socksClientCertificatesInterceptor.getMatchingTLSOptionsForOrigin)(this._defaultOptions().clientCertificates, url.origin),
|
||||
__testHookLookup: options.__testHookLookup
|
||||
};
|
||||
// rejectUnauthorized = undefined is treated as true in node 12.
|
||||
if (options.rejectUnauthorized === false) redirectOptions.rejectUnauthorized = false;
|
||||
|
||||
// HTTP-redirect fetch step 4: If locationURL is null, then return response.
|
||||
// Best-effort UTF-8 decoding, per spec it's US-ASCII only, but browsers are more lenient.
|
||||
// Node.js parses it as Latin1 via std::v8::String, so we convert it to UTF-8.
|
||||
const locationHeaderValue = Buffer.from((_response$headers$loc = response.headers.location) !== null && _response$headers$loc !== void 0 ? _response$headers$loc : '', 'latin1').toString('utf8');
|
||||
if (locationHeaderValue) {
|
||||
let locationURL;
|
||||
try {
|
||||
locationURL = new URL(locationHeaderValue, url);
|
||||
} catch (error) {
|
||||
reject(new Error(`uri requested responds with an invalid redirect URL: ${locationHeaderValue}`));
|
||||
request.destroy();
|
||||
return;
|
||||
}
|
||||
if (headers['host']) headers['host'] = locationURL.host;
|
||||
notifyRequestFinished();
|
||||
fulfill(this._sendRequest(progress, locationURL, redirectOptions, postData));
|
||||
request.destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (response.statusCode === 401 && !getHeader(options.headers, 'authorization')) {
|
||||
const auth = response.headers['www-authenticate'];
|
||||
const credentials = this._getHttpCredentials(url);
|
||||
if (auth !== null && auth !== void 0 && auth.trim().startsWith('Basic') && credentials) {
|
||||
setBasicAuthorizationHeader(options.headers, credentials);
|
||||
notifyRequestFinished();
|
||||
fulfill(this._sendRequest(progress, url, options, postData));
|
||||
request.destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
response.on('aborted', () => reject(new Error('aborted')));
|
||||
const chunks = [];
|
||||
const notifyBodyFinished = () => {
|
||||
const body = Buffer.concat(chunks);
|
||||
notifyRequestFinished(body);
|
||||
fulfill({
|
||||
url: response.url || url.toString(),
|
||||
status: response.statusCode || 0,
|
||||
statusText: response.statusMessage || '',
|
||||
headers: toHeadersArray(response.rawHeaders),
|
||||
body
|
||||
});
|
||||
};
|
||||
let body = response;
|
||||
let transform;
|
||||
const encoding = response.headers['content-encoding'];
|
||||
if (encoding === 'gzip' || encoding === 'x-gzip') {
|
||||
transform = _zlib.default.createGunzip({
|
||||
flush: _zlib.default.constants.Z_SYNC_FLUSH,
|
||||
finishFlush: _zlib.default.constants.Z_SYNC_FLUSH
|
||||
});
|
||||
} else if (encoding === 'br') {
|
||||
transform = _zlib.default.createBrotliDecompress({
|
||||
flush: _zlib.default.constants.BROTLI_OPERATION_FLUSH,
|
||||
finishFlush: _zlib.default.constants.BROTLI_OPERATION_FLUSH
|
||||
});
|
||||
} else if (encoding === 'deflate') {
|
||||
transform = _zlib.default.createInflate();
|
||||
}
|
||||
if (transform) {
|
||||
// Brotli and deflate decompressors throw if the input stream is empty.
|
||||
const emptyStreamTransform = new SafeEmptyStreamTransform(notifyBodyFinished);
|
||||
body = (0, _stream.pipeline)(response, emptyStreamTransform, transform, e => {
|
||||
if (e) reject(new Error(`failed to decompress '${encoding}' encoding: ${e.message}`));
|
||||
});
|
||||
body.on('error', e => reject(new Error(`failed to decompress '${encoding}' encoding: ${e}`)));
|
||||
} else {
|
||||
body.on('error', reject);
|
||||
}
|
||||
body.on('data', chunk => chunks.push(chunk));
|
||||
body.on('end', notifyBodyFinished);
|
||||
});
|
||||
request.on('error', reject);
|
||||
listeners.push(_utils.eventsHelper.addEventListener(this, APIRequestContext.Events.Dispose, () => {
|
||||
reject(new Error('Request context disposed.'));
|
||||
request.destroy();
|
||||
}));
|
||||
request.on('close', () => _utils.eventsHelper.removeEventListeners(listeners));
|
||||
request.on('socket', socket => {
|
||||
var _tcpConnectionAt, _tcpConnectionAt3;
|
||||
if (request.reusedSocket) {
|
||||
reusedSocketAt = (0, _utils.monotonicTime)();
|
||||
return;
|
||||
}
|
||||
|
||||
// happy eyeballs don't emit lookup and connect events, so we use our custom ones
|
||||
const happyEyeBallsTimings = (0, _happyEyeballs.timingForSocket)(socket);
|
||||
dnsLookupAt = happyEyeBallsTimings.dnsLookupAt;
|
||||
(_tcpConnectionAt = tcpConnectionAt) !== null && _tcpConnectionAt !== void 0 ? _tcpConnectionAt : tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt;
|
||||
|
||||
// non-happy-eyeballs sockets
|
||||
listeners.push(_utils.eventsHelper.addEventListener(socket, 'lookup', () => {
|
||||
dnsLookupAt = (0, _utils.monotonicTime)();
|
||||
}), _utils.eventsHelper.addEventListener(socket, 'connect', () => {
|
||||
var _tcpConnectionAt2;
|
||||
(_tcpConnectionAt2 = tcpConnectionAt) !== null && _tcpConnectionAt2 !== void 0 ? _tcpConnectionAt2 : tcpConnectionAt = (0, _utils.monotonicTime)();
|
||||
}), _utils.eventsHelper.addEventListener(socket, 'secureConnect', () => {
|
||||
tlsHandshakeAt = (0, _utils.monotonicTime)();
|
||||
if (socket instanceof _tls.TLSSocket) {
|
||||
var _socket$getProtocol;
|
||||
const peerCertificate = socket.getPeerCertificate();
|
||||
securityDetails = {
|
||||
protocol: (_socket$getProtocol = socket.getProtocol()) !== null && _socket$getProtocol !== void 0 ? _socket$getProtocol : undefined,
|
||||
subjectName: peerCertificate.subject.CN,
|
||||
validFrom: new Date(peerCertificate.valid_from).getTime() / 1000,
|
||||
validTo: new Date(peerCertificate.valid_to).getTime() / 1000,
|
||||
issuer: peerCertificate.issuer.CN
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
// when using socks proxy, having the socket means the connection got established
|
||||
if (agent instanceof _utilsBundle.SocksProxyAgent) (_tcpConnectionAt3 = tcpConnectionAt) !== null && _tcpConnectionAt3 !== void 0 ? _tcpConnectionAt3 : tcpConnectionAt = (0, _utils.monotonicTime)();
|
||||
serverIPAddress = socket.remoteAddress;
|
||||
serverPort = socket.remotePort;
|
||||
});
|
||||
request.on('finish', () => {
|
||||
requestFinishAt = (0, _utils.monotonicTime)();
|
||||
});
|
||||
|
||||
// http proxy
|
||||
request.on('proxyConnect', () => {
|
||||
var _tcpConnectionAt4;
|
||||
(_tcpConnectionAt4 = tcpConnectionAt) !== null && _tcpConnectionAt4 !== void 0 ? _tcpConnectionAt4 : tcpConnectionAt = (0, _utils.monotonicTime)();
|
||||
});
|
||||
progress.log(`→ ${options.method} ${url.toString()}`);
|
||||
if (options.headers) {
|
||||
for (const [name, value] of Object.entries(options.headers)) progress.log(` ${name}: ${value}`);
|
||||
}
|
||||
if (options.deadline) {
|
||||
const rejectOnTimeout = () => {
|
||||
reject(new Error(`Request timed out after ${options.timeout}ms`));
|
||||
request.destroy();
|
||||
};
|
||||
const remaining = options.deadline - (0, _utils.monotonicTime)();
|
||||
if (remaining <= 0) {
|
||||
rejectOnTimeout();
|
||||
return;
|
||||
}
|
||||
request.setTimeout(remaining, rejectOnTimeout);
|
||||
}
|
||||
if (postData) request.write(postData);
|
||||
request.end();
|
||||
});
|
||||
}
|
||||
_getHttpCredentials(url) {
|
||||
var _this$_defaultOptions, _this$_defaultOptions2;
|
||||
if (!((_this$_defaultOptions = this._defaultOptions().httpCredentials) !== null && _this$_defaultOptions !== void 0 && _this$_defaultOptions.origin) || url.origin.toLowerCase() === ((_this$_defaultOptions2 = this._defaultOptions().httpCredentials) === null || _this$_defaultOptions2 === void 0 || (_this$_defaultOptions2 = _this$_defaultOptions2.origin) === null || _this$_defaultOptions2 === void 0 ? void 0 : _this$_defaultOptions2.toLowerCase())) return this._defaultOptions().httpCredentials;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
exports.APIRequestContext = APIRequestContext;
|
||||
APIRequestContext.Events = {
|
||||
Dispose: 'dispose',
|
||||
Request: 'request',
|
||||
RequestFinished: 'requestfinished'
|
||||
};
|
||||
APIRequestContext.allInstances = new Set();
|
||||
class SafeEmptyStreamTransform extends _stream.Transform {
|
||||
constructor(onEmptyStreamCallback) {
|
||||
super();
|
||||
this._receivedSomeData = false;
|
||||
this._onEmptyStreamCallback = void 0;
|
||||
this._onEmptyStreamCallback = onEmptyStreamCallback;
|
||||
}
|
||||
_transform(chunk, encoding, callback) {
|
||||
this._receivedSomeData = true;
|
||||
callback(null, chunk);
|
||||
}
|
||||
_flush(callback) {
|
||||
if (this._receivedSomeData) callback(null);else this._onEmptyStreamCallback();
|
||||
}
|
||||
}
|
||||
class BrowserContextAPIRequestContext extends APIRequestContext {
|
||||
constructor(context) {
|
||||
super(context);
|
||||
this._context = void 0;
|
||||
this._context = context;
|
||||
context.once(_browserContext.BrowserContext.Events.Close, () => this._disposeImpl());
|
||||
}
|
||||
tracing() {
|
||||
return this._context.tracing;
|
||||
}
|
||||
async dispose(options) {
|
||||
this._closeReason = options.reason;
|
||||
this.fetchResponses.clear();
|
||||
}
|
||||
_defaultOptions() {
|
||||
return {
|
||||
userAgent: this._context._options.userAgent || this._context._browser.userAgent(),
|
||||
extraHTTPHeaders: this._context._options.extraHTTPHeaders,
|
||||
httpCredentials: this._context._options.httpCredentials,
|
||||
proxy: this._context._options.proxy || this._context._browser.options.proxy,
|
||||
timeoutSettings: this._context._timeoutSettings,
|
||||
ignoreHTTPSErrors: this._context._options.ignoreHTTPSErrors,
|
||||
baseURL: this._context._options.baseURL,
|
||||
clientCertificates: this._context._options.clientCertificates
|
||||
};
|
||||
}
|
||||
async _addCookies(cookies) {
|
||||
await this._context.addCookies(cookies);
|
||||
}
|
||||
async _cookies(url) {
|
||||
return await this._context.cookies(url.toString());
|
||||
}
|
||||
async storageState() {
|
||||
return this._context.storageState();
|
||||
}
|
||||
}
|
||||
exports.BrowserContextAPIRequestContext = BrowserContextAPIRequestContext;
|
||||
class GlobalAPIRequestContext extends APIRequestContext {
|
||||
constructor(playwright, options) {
|
||||
super(playwright);
|
||||
this._cookieStore = new _cookieStore.CookieStore();
|
||||
this._options = void 0;
|
||||
this._origins = void 0;
|
||||
this._tracing = void 0;
|
||||
this.attribution.context = this;
|
||||
const timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
||||
if (options.timeout !== undefined) timeoutSettings.setDefaultTimeout(options.timeout);
|
||||
const proxy = options.proxy;
|
||||
if (proxy !== null && proxy !== void 0 && proxy.server) {
|
||||
let url = proxy === null || proxy === void 0 ? void 0 : proxy.server.trim();
|
||||
if (!/^\w+:\/\//.test(url)) url = 'http://' + url;
|
||||
proxy.server = url;
|
||||
}
|
||||
if (options.storageState) {
|
||||
this._origins = options.storageState.origins;
|
||||
this._cookieStore.addCookies(options.storageState.cookies || []);
|
||||
}
|
||||
(0, _browserContext.verifyClientCertificates)(options.clientCertificates);
|
||||
this._options = {
|
||||
baseURL: options.baseURL,
|
||||
userAgent: options.userAgent || (0, _userAgent.getUserAgent)(),
|
||||
extraHTTPHeaders: options.extraHTTPHeaders,
|
||||
ignoreHTTPSErrors: !!options.ignoreHTTPSErrors,
|
||||
httpCredentials: options.httpCredentials,
|
||||
clientCertificates: options.clientCertificates,
|
||||
proxy,
|
||||
timeoutSettings
|
||||
};
|
||||
this._tracing = new _tracing.Tracing(this, options.tracesDir);
|
||||
}
|
||||
tracing() {
|
||||
return this._tracing;
|
||||
}
|
||||
async dispose(options) {
|
||||
this._closeReason = options.reason;
|
||||
await this._tracing.flush();
|
||||
await this._tracing.deleteTmpTracesDir();
|
||||
this._disposeImpl();
|
||||
}
|
||||
_defaultOptions() {
|
||||
return this._options;
|
||||
}
|
||||
async _addCookies(cookies) {
|
||||
this._cookieStore.addCookies(cookies);
|
||||
}
|
||||
async _cookies(url) {
|
||||
return this._cookieStore.cookies(url);
|
||||
}
|
||||
async storageState() {
|
||||
return {
|
||||
cookies: this._cookieStore.allCookies(),
|
||||
origins: this._origins || []
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.GlobalAPIRequestContext = GlobalAPIRequestContext;
|
||||
function createProxyAgent(proxy) {
|
||||
var _proxyURL$protocol;
|
||||
const proxyURL = new URL(proxy.server);
|
||||
if ((_proxyURL$protocol = proxyURL.protocol) !== null && _proxyURL$protocol !== void 0 && _proxyURL$protocol.startsWith('socks')) return new _utilsBundle.SocksProxyAgent(proxyURL);
|
||||
if (proxy.username) proxyURL.username = proxy.username;
|
||||
if (proxy.password) proxyURL.password = proxy.password;
|
||||
// TODO: We should use HttpProxyAgent conditional on proxyURL.protocol instead of always using CONNECT method.
|
||||
return new _utilsBundle.HttpsProxyAgent(proxyURL);
|
||||
}
|
||||
function toHeadersArray(rawHeaders) {
|
||||
const result = [];
|
||||
for (let i = 0; i < rawHeaders.length; i += 2) result.push({
|
||||
name: rawHeaders[i],
|
||||
value: rawHeaders[i + 1]
|
||||
});
|
||||
return result;
|
||||
}
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
function parseCookie(header) {
|
||||
const raw = (0, _cookieStore.parseRawCookie)(header);
|
||||
if (!raw) return null;
|
||||
const cookie = {
|
||||
domain: '',
|
||||
path: '',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
||||
// The cookie-sending behavior if SameSite is not specified is SameSite=Lax.
|
||||
sameSite: 'Lax',
|
||||
...raw
|
||||
};
|
||||
return cookie;
|
||||
}
|
||||
function serializePostData(params, headers) {
|
||||
(0, _utils.assert)((params.postData ? 1 : 0) + (params.jsonData ? 1 : 0) + (params.formData ? 1 : 0) + (params.multipartData ? 1 : 0) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
|
||||
if (params.jsonData !== undefined) {
|
||||
setHeader(headers, 'content-type', 'application/json', true);
|
||||
return Buffer.from(params.jsonData, 'utf8');
|
||||
} else if (params.formData) {
|
||||
const searchParams = new URLSearchParams();
|
||||
for (const {
|
||||
name,
|
||||
value
|
||||
} of params.formData) searchParams.append(name, value);
|
||||
setHeader(headers, 'content-type', 'application/x-www-form-urlencoded', true);
|
||||
return Buffer.from(searchParams.toString(), 'utf8');
|
||||
} else if (params.multipartData) {
|
||||
const formData = new _formData.MultipartFormData();
|
||||
for (const field of params.multipartData) {
|
||||
if (field.file) formData.addFileField(field.name, field.file);else if (field.value) formData.addField(field.name, field.value);
|
||||
}
|
||||
setHeader(headers, 'content-type', formData.contentTypeHeader(), true);
|
||||
return formData.finish();
|
||||
} else if (params.postData !== undefined) {
|
||||
setHeader(headers, 'content-type', 'application/octet-stream', true);
|
||||
return params.postData;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function setHeader(headers, name, value, keepExisting = false) {
|
||||
const existing = Object.entries(headers).find(pair => pair[0].toLowerCase() === name.toLowerCase());
|
||||
if (!existing) headers[name] = value;else if (!keepExisting) headers[existing[0]] = value;
|
||||
}
|
||||
function getHeader(headers, name) {
|
||||
const existing = Object.entries(headers).find(pair => pair[0].toLowerCase() === name.toLowerCase());
|
||||
return existing ? existing[1] : undefined;
|
||||
}
|
||||
function removeHeader(headers, name) {
|
||||
delete headers[name];
|
||||
}
|
||||
function shouldBypassProxy(url, bypass) {
|
||||
if (!bypass) return false;
|
||||
const domains = bypass.split(',').map(s => {
|
||||
s = s.trim();
|
||||
if (!s.startsWith('.')) s = '.' + s;
|
||||
return s;
|
||||
});
|
||||
const domain = '.' + url.hostname;
|
||||
return domains.some(d => domain.endsWith(d));
|
||||
}
|
||||
function setBasicAuthorizationHeader(headers, credentials) {
|
||||
const {
|
||||
username,
|
||||
password
|
||||
} = credentials;
|
||||
const encoded = Buffer.from(`${username || ''}:${password || ''}`).toString('base64');
|
||||
setHeader(headers, 'authorization', `Basic ${encoded}`);
|
||||
}
|
||||
42
node_modules/playwright-core/lib/server/fileChooser.js
generated
vendored
Normal file
42
node_modules/playwright-core/lib/server/fileChooser.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FileChooser = void 0;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FileChooser {
|
||||
constructor(page, elementHandle, isMultiple) {
|
||||
this._page = void 0;
|
||||
this._elementHandle = void 0;
|
||||
this._isMultiple = void 0;
|
||||
this._page = page;
|
||||
this._elementHandle = elementHandle;
|
||||
this._isMultiple = isMultiple;
|
||||
}
|
||||
element() {
|
||||
return this._elementHandle;
|
||||
}
|
||||
isMultiple() {
|
||||
return this._isMultiple;
|
||||
}
|
||||
page() {
|
||||
return this._page;
|
||||
}
|
||||
}
|
||||
exports.FileChooser = FileChooser;
|
||||
75
node_modules/playwright-core/lib/server/fileUploadUtils.js
generated
vendored
Normal file
75
node_modules/playwright-core/lib/server/fileUploadUtils.js
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.prepareFilesForUpload = prepareFilesForUpload;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _utils = require("../utils");
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function filesExceedUploadLimit(files) {
|
||||
const sizes = await Promise.all(files.map(async file => (await _fs.default.promises.stat(file)).size));
|
||||
return sizes.reduce((total, size) => total + size, 0) >= _utils.fileUploadSizeLimit;
|
||||
}
|
||||
async function prepareFilesForUpload(frame, params) {
|
||||
var _fileBuffers;
|
||||
const {
|
||||
payloads,
|
||||
streams,
|
||||
directoryStream
|
||||
} = params;
|
||||
let {
|
||||
localPaths,
|
||||
localDirectory
|
||||
} = params;
|
||||
if ([payloads, localPaths, localDirectory, streams, directoryStream].filter(Boolean).length !== 1) throw new Error('Exactly one of payloads, localPaths and streams must be provided');
|
||||
if (streams) localPaths = streams.map(c => c.path());
|
||||
if (directoryStream) localDirectory = directoryStream.path();
|
||||
if (localPaths) {
|
||||
for (const p of localPaths) (0, _utils.assert)(_path.default.isAbsolute(p) && _path.default.resolve(p) === p, 'Paths provided to localPaths must be absolute and fully resolved.');
|
||||
}
|
||||
let fileBuffers = payloads;
|
||||
if (!frame._page._browserContext._browser._isCollocatedWithServer) {
|
||||
// If the browser is on a different machine read files into buffers.
|
||||
if (localPaths) {
|
||||
if (await filesExceedUploadLimit(localPaths)) throw new Error('Cannot transfer files larger than 50Mb to a browser not co-located with the server');
|
||||
fileBuffers = await Promise.all(localPaths.map(async item => {
|
||||
return {
|
||||
name: _path.default.basename(item),
|
||||
buffer: await _fs.default.promises.readFile(item),
|
||||
lastModifiedMs: (await _fs.default.promises.stat(item)).mtimeMs
|
||||
};
|
||||
}));
|
||||
localPaths = undefined;
|
||||
}
|
||||
}
|
||||
const filePayloads = (_fileBuffers = fileBuffers) === null || _fileBuffers === void 0 ? void 0 : _fileBuffers.map(payload => ({
|
||||
name: payload.name,
|
||||
mimeType: payload.mimeType || _utilsBundle.mime.getType(payload.name) || 'application/octet-stream',
|
||||
buffer: payload.buffer.toString('base64'),
|
||||
lastModifiedMs: payload.lastModifiedMs
|
||||
}));
|
||||
return {
|
||||
localPaths,
|
||||
localDirectory,
|
||||
filePayloads
|
||||
};
|
||||
}
|
||||
216
node_modules/playwright-core/lib/server/firefox/ffAccessibility.js
generated
vendored
Normal file
216
node_modules/playwright-core/lib/server/firefox/ffAccessibility.js
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getAccessibilityTree = getAccessibilityTree;
|
||||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
async function getAccessibilityTree(session, needle) {
|
||||
const objectId = needle ? needle._objectId : undefined;
|
||||
const {
|
||||
tree
|
||||
} = await session.send('Accessibility.getFullAXTree', {
|
||||
objectId
|
||||
});
|
||||
const axNode = new FFAXNode(tree);
|
||||
return {
|
||||
tree: axNode,
|
||||
needle: needle ? axNode._findNeedle() : null
|
||||
};
|
||||
}
|
||||
const FFRoleToARIARole = new Map(Object.entries({
|
||||
'pushbutton': 'button',
|
||||
'checkbutton': 'checkbox',
|
||||
'editcombobox': 'combobox',
|
||||
'content deletion': 'deletion',
|
||||
'footnote': 'doc-footnote',
|
||||
'non-native document': 'document',
|
||||
'grouping': 'group',
|
||||
'graphic': 'img',
|
||||
'content insertion': 'insertion',
|
||||
'animation': 'marquee',
|
||||
'flat equation': 'math',
|
||||
'menupopup': 'menu',
|
||||
'check menu item': 'menuitemcheckbox',
|
||||
'radio menu item': 'menuitemradio',
|
||||
'listbox option': 'option',
|
||||
'radiobutton': 'radio',
|
||||
'statusbar': 'status',
|
||||
'pagetab': 'tab',
|
||||
'pagetablist': 'tablist',
|
||||
'propertypage': 'tabpanel',
|
||||
'entry': 'textbox',
|
||||
'outline': 'tree',
|
||||
'tree table': 'treegrid',
|
||||
'outlineitem': 'treeitem'
|
||||
}));
|
||||
class FFAXNode {
|
||||
constructor(payload) {
|
||||
this._children = void 0;
|
||||
this._payload = void 0;
|
||||
this._editable = void 0;
|
||||
this._richlyEditable = void 0;
|
||||
this._focusable = void 0;
|
||||
this._expanded = void 0;
|
||||
this._name = void 0;
|
||||
this._role = void 0;
|
||||
this._cachedHasFocusableChild = void 0;
|
||||
this._payload = payload;
|
||||
this._children = (payload.children || []).map(x => new FFAXNode(x));
|
||||
this._editable = !!payload.editable;
|
||||
this._richlyEditable = this._editable && payload.tag !== 'textarea' && payload.tag !== 'input';
|
||||
this._focusable = !!payload.focusable;
|
||||
this._expanded = !!payload.expanded;
|
||||
this._name = this._payload.name;
|
||||
this._role = this._payload.role;
|
||||
}
|
||||
_isPlainTextField() {
|
||||
if (this._richlyEditable) return false;
|
||||
if (this._editable) return true;
|
||||
return this._role === 'entry';
|
||||
}
|
||||
_isTextOnlyObject() {
|
||||
const role = this._role;
|
||||
return role === 'text leaf' || role === 'text' || role === 'statictext';
|
||||
}
|
||||
_hasFocusableChild() {
|
||||
if (this._cachedHasFocusableChild === undefined) {
|
||||
this._cachedHasFocusableChild = false;
|
||||
for (const child of this._children) {
|
||||
if (child._focusable || child._hasFocusableChild()) {
|
||||
this._cachedHasFocusableChild = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._cachedHasFocusableChild;
|
||||
}
|
||||
children() {
|
||||
return this._children;
|
||||
}
|
||||
_findNeedle() {
|
||||
if (this._payload.foundObject) return this;
|
||||
for (const child of this._children) {
|
||||
const found = child._findNeedle();
|
||||
if (found) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
isLeafNode() {
|
||||
if (!this._children.length) return true;
|
||||
// These types of objects may have children that we use as internal
|
||||
// implementation details, but we want to expose them as leaves to platform
|
||||
// accessibility APIs because screen readers might be confused if they find
|
||||
// any children.
|
||||
if (this._isPlainTextField() || this._isTextOnlyObject()) return true;
|
||||
// Roles whose children are only presentational according to the ARIA and
|
||||
// HTML5 Specs should be hidden from screen readers.
|
||||
// (Note that whilst ARIA buttons can have only presentational children, HTML5
|
||||
// buttons are allowed to have content.)
|
||||
switch (this._role) {
|
||||
case 'graphic':
|
||||
case 'scrollbar':
|
||||
case 'slider':
|
||||
case 'separator':
|
||||
case 'progressbar':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Here and below: Android heuristics
|
||||
if (this._hasFocusableChild()) return false;
|
||||
if (this._focusable && this._role !== 'document' && this._name) return true;
|
||||
if (this._role === 'heading' && this._name) return true;
|
||||
return false;
|
||||
}
|
||||
isControl() {
|
||||
switch (this._role) {
|
||||
case 'checkbutton':
|
||||
case 'check menu item':
|
||||
case 'check rich option':
|
||||
case 'combobox':
|
||||
case 'combobox option':
|
||||
case 'color chooser':
|
||||
case 'listbox':
|
||||
case 'listbox option':
|
||||
case 'listbox rich option':
|
||||
case 'popup menu':
|
||||
case 'menupopup':
|
||||
case 'menuitem':
|
||||
case 'menubar':
|
||||
case 'button':
|
||||
case 'pushbutton':
|
||||
case 'radiobutton':
|
||||
case 'radio menuitem':
|
||||
case 'scrollbar':
|
||||
case 'slider':
|
||||
case 'spinbutton':
|
||||
case 'switch':
|
||||
case 'pagetab':
|
||||
case 'entry':
|
||||
case 'tree table':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
isInteresting(insideControl) {
|
||||
if (this._focusable || this._richlyEditable) return true;
|
||||
// If it's not focusable but has a control role, then it's interesting.
|
||||
if (this.isControl()) return true;
|
||||
// A non focusable child of a control is not interesting
|
||||
if (insideControl) return false;
|
||||
return this.isLeafNode() && !!this._name.trim();
|
||||
}
|
||||
serialize() {
|
||||
const node = {
|
||||
role: FFRoleToARIARole.get(this._role) || this._role,
|
||||
name: this._name || ''
|
||||
};
|
||||
const userStringProperties = ['name', 'description', 'roledescription', 'valuetext', 'keyshortcuts'];
|
||||
for (const userStringProperty of userStringProperties) {
|
||||
if (!(userStringProperty in this._payload)) continue;
|
||||
node[userStringProperty] = this._payload[userStringProperty];
|
||||
}
|
||||
const booleanProperties = ['disabled', 'expanded', 'focused', 'modal', 'multiline', 'multiselectable', 'readonly', 'required', 'selected'];
|
||||
for (const booleanProperty of booleanProperties) {
|
||||
if (this._role === 'document' && booleanProperty === 'focused') continue; // document focusing is strange
|
||||
const value = this._payload[booleanProperty];
|
||||
if (!value) continue;
|
||||
node[booleanProperty] = value;
|
||||
}
|
||||
const numericalProperties = ['level'];
|
||||
for (const numericalProperty of numericalProperties) {
|
||||
if (!(numericalProperty in this._payload)) continue;
|
||||
node[numericalProperty] = this._payload[numericalProperty];
|
||||
}
|
||||
const tokenProperties = ['autocomplete', 'haspopup', 'orientation'];
|
||||
for (const tokenProperty of tokenProperties) {
|
||||
const value = this._payload[tokenProperty];
|
||||
if (!value || value === 'false') continue;
|
||||
node[tokenProperty] = value;
|
||||
}
|
||||
const axNode = node;
|
||||
axNode.valueString = this._payload.value;
|
||||
if ('checked' in this._payload) axNode.checked = this._payload.checked === true ? 'checked' : this._payload.checked === 'mixed' ? 'mixed' : 'unchecked';
|
||||
if ('pressed' in this._payload) axNode.pressed = this._payload.pressed === true ? 'pressed' : 'released';
|
||||
if ('invalid' in this._payload) axNode.invalid = this._payload.invalid === true ? 'true' : 'false';
|
||||
return axNode;
|
||||
}
|
||||
}
|
||||
460
node_modules/playwright-core/lib/server/firefox/ffBrowser.js
generated
vendored
Normal file
460
node_modules/playwright-core/lib/server/firefox/ffBrowser.js
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FFBrowserContext = exports.FFBrowser = void 0;
|
||||
var _errors = require("../errors");
|
||||
var _utils = require("../../utils");
|
||||
var _browser = require("../browser");
|
||||
var _browserContext = require("../browserContext");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
var _page = require("../page");
|
||||
var _ffConnection = require("./ffConnection");
|
||||
var _ffPage = require("./ffPage");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FFBrowser extends _browser.Browser {
|
||||
static async connect(parent, transport, options) {
|
||||
var _options$originalLaun;
|
||||
const connection = new _ffConnection.FFConnection(transport, options.protocolLogger, options.browserLogsCollector);
|
||||
const browser = new FFBrowser(parent, connection, options);
|
||||
if (options.__testHookOnConnectToBrowser) await options.__testHookOnConnectToBrowser();
|
||||
let firefoxUserPrefs = (_options$originalLaun = options.originalLaunchOptions.firefoxUserPrefs) !== null && _options$originalLaun !== void 0 ? _options$originalLaun : {};
|
||||
if (Object.keys(kBandaidFirefoxUserPrefs).length) firefoxUserPrefs = {
|
||||
...kBandaidFirefoxUserPrefs,
|
||||
...firefoxUserPrefs
|
||||
};
|
||||
const promises = [browser.session.send('Browser.enable', {
|
||||
attachToDefaultContext: !!options.persistent,
|
||||
userPrefs: Object.entries(firefoxUserPrefs).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}))
|
||||
}), browser._initVersion()];
|
||||
if (options.persistent) {
|
||||
browser._defaultContext = new FFBrowserContext(browser, undefined, options.persistent);
|
||||
promises.push(browser._defaultContext._initialize());
|
||||
}
|
||||
const proxy = options.originalLaunchOptions.proxyOverride || options.proxy;
|
||||
if (proxy) promises.push(browser.session.send('Browser.setBrowserProxy', toJugglerProxyOptions(proxy)));
|
||||
await Promise.all(promises);
|
||||
return browser;
|
||||
}
|
||||
constructor(parent, connection, options) {
|
||||
super(parent, options);
|
||||
this._connection = void 0;
|
||||
this.session = void 0;
|
||||
this._ffPages = void 0;
|
||||
this._contexts = void 0;
|
||||
this._version = '';
|
||||
this._userAgent = '';
|
||||
this._connection = connection;
|
||||
this.session = connection.rootSession;
|
||||
this._ffPages = new Map();
|
||||
this._contexts = new Map();
|
||||
this._connection.on(_ffConnection.ConnectionEvents.Disconnected, () => this._onDisconnect());
|
||||
this.session.on('Browser.attachedToTarget', this._onAttachedToTarget.bind(this));
|
||||
this.session.on('Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this));
|
||||
this.session.on('Browser.downloadCreated', this._onDownloadCreated.bind(this));
|
||||
this.session.on('Browser.downloadFinished', this._onDownloadFinished.bind(this));
|
||||
this.session.on('Browser.videoRecordingFinished', this._onVideoRecordingFinished.bind(this));
|
||||
}
|
||||
async _initVersion() {
|
||||
const result = await this.session.send('Browser.getInfo');
|
||||
this._version = result.version.substring(result.version.indexOf('/') + 1);
|
||||
this._userAgent = result.userAgent;
|
||||
}
|
||||
isConnected() {
|
||||
return !this._connection._closed;
|
||||
}
|
||||
async doCreateNewContext(options) {
|
||||
if (options.isMobile) throw new Error('options.isMobile is not supported in Firefox');
|
||||
const {
|
||||
browserContextId
|
||||
} = await this.session.send('Browser.createBrowserContext', {
|
||||
removeOnDetach: true
|
||||
});
|
||||
const context = new FFBrowserContext(this, browserContextId, options);
|
||||
await context._initialize();
|
||||
this._contexts.set(browserContextId, context);
|
||||
return context;
|
||||
}
|
||||
contexts() {
|
||||
return Array.from(this._contexts.values());
|
||||
}
|
||||
version() {
|
||||
return this._version;
|
||||
}
|
||||
userAgent() {
|
||||
return this._userAgent;
|
||||
}
|
||||
_onDetachedFromTarget(payload) {
|
||||
const ffPage = this._ffPages.get(payload.targetId);
|
||||
this._ffPages.delete(payload.targetId);
|
||||
ffPage.didClose();
|
||||
}
|
||||
_onAttachedToTarget(payload) {
|
||||
const {
|
||||
targetId,
|
||||
browserContextId,
|
||||
openerId,
|
||||
type
|
||||
} = payload.targetInfo;
|
||||
(0, _utils.assert)(type === 'page');
|
||||
const context = browserContextId ? this._contexts.get(browserContextId) : this._defaultContext;
|
||||
(0, _utils.assert)(context, `Unknown context id:${browserContextId}, _defaultContext: ${this._defaultContext}`);
|
||||
const session = this._connection.createSession(payload.sessionId);
|
||||
const opener = openerId ? this._ffPages.get(openerId) : null;
|
||||
const ffPage = new _ffPage.FFPage(session, context, opener);
|
||||
this._ffPages.set(targetId, ffPage);
|
||||
}
|
||||
_onDownloadCreated(payload) {
|
||||
const ffPage = this._ffPages.get(payload.pageTargetId);
|
||||
if (!ffPage) return;
|
||||
|
||||
// Abort the navigation that turned into download.
|
||||
ffPage._page._frameManager.frameAbortedNavigation(payload.frameId, 'Download is starting');
|
||||
let originPage = ffPage._initializedPage;
|
||||
// If it's a new window download, report it on the opener page.
|
||||
if (!originPage) {
|
||||
// Resume the page creation with an error. The page will automatically close right
|
||||
// after the download begins.
|
||||
ffPage._markAsError(new Error('Starting new page download'));
|
||||
if (ffPage._opener) originPage = ffPage._opener._initializedPage;
|
||||
}
|
||||
if (!originPage) return;
|
||||
this._downloadCreated(originPage, payload.uuid, payload.url, payload.suggestedFileName);
|
||||
}
|
||||
_onDownloadFinished(payload) {
|
||||
const error = payload.canceled ? 'canceled' : payload.error;
|
||||
this._downloadFinished(payload.uuid, error);
|
||||
}
|
||||
_onVideoRecordingFinished(payload) {
|
||||
var _this$_takeVideo;
|
||||
(_this$_takeVideo = this._takeVideo(payload.screencastId)) === null || _this$_takeVideo === void 0 || _this$_takeVideo.reportFinished();
|
||||
}
|
||||
_onDisconnect() {
|
||||
for (const video of this._idToVideo.values()) video.artifact.reportFinished(new _errors.TargetClosedError());
|
||||
this._idToVideo.clear();
|
||||
for (const ffPage of this._ffPages.values()) ffPage.didClose();
|
||||
this._ffPages.clear();
|
||||
this._didClose();
|
||||
}
|
||||
}
|
||||
exports.FFBrowser = FFBrowser;
|
||||
class FFBrowserContext extends _browserContext.BrowserContext {
|
||||
constructor(browser, browserContextId, options) {
|
||||
super(browser, options, browserContextId);
|
||||
}
|
||||
async _initialize() {
|
||||
(0, _utils.assert)(!this._ffPages().length);
|
||||
const browserContextId = this._browserContextId;
|
||||
const promises = [super._initialize(), this._browser.session.send('Browser.addBinding', {
|
||||
browserContextId: this._browserContextId,
|
||||
name: _page.PageBinding.kPlaywrightBinding,
|
||||
script: ''
|
||||
})];
|
||||
if (this._options.acceptDownloads !== 'internal-browser-default') {
|
||||
promises.push(this._browser.session.send('Browser.setDownloadOptions', {
|
||||
browserContextId,
|
||||
downloadOptions: {
|
||||
behavior: this._options.acceptDownloads === 'accept' ? 'saveToDisk' : 'cancel',
|
||||
downloadsDir: this._browser.options.downloadsPath
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (this._options.viewport) {
|
||||
const viewport = {
|
||||
viewportSize: {
|
||||
width: this._options.viewport.width,
|
||||
height: this._options.viewport.height
|
||||
},
|
||||
deviceScaleFactor: this._options.deviceScaleFactor || 1
|
||||
};
|
||||
promises.push(this._browser.session.send('Browser.setDefaultViewport', {
|
||||
browserContextId,
|
||||
viewport
|
||||
}));
|
||||
}
|
||||
if (this._options.hasTouch) promises.push(this._browser.session.send('Browser.setTouchOverride', {
|
||||
browserContextId,
|
||||
hasTouch: true
|
||||
}));
|
||||
if (this._options.userAgent) promises.push(this._browser.session.send('Browser.setUserAgentOverride', {
|
||||
browserContextId,
|
||||
userAgent: this._options.userAgent
|
||||
}));
|
||||
if (this._options.bypassCSP) promises.push(this._browser.session.send('Browser.setBypassCSP', {
|
||||
browserContextId,
|
||||
bypassCSP: true
|
||||
}));
|
||||
if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors) promises.push(this._browser.session.send('Browser.setIgnoreHTTPSErrors', {
|
||||
browserContextId,
|
||||
ignoreHTTPSErrors: true
|
||||
}));
|
||||
if (this._options.javaScriptEnabled === false) promises.push(this._browser.session.send('Browser.setJavaScriptDisabled', {
|
||||
browserContextId,
|
||||
javaScriptDisabled: true
|
||||
}));
|
||||
if (this._options.locale) promises.push(this._browser.session.send('Browser.setLocaleOverride', {
|
||||
browserContextId,
|
||||
locale: this._options.locale
|
||||
}));
|
||||
if (this._options.timezoneId) promises.push(this._browser.session.send('Browser.setTimezoneOverride', {
|
||||
browserContextId,
|
||||
timezoneId: this._options.timezoneId
|
||||
}));
|
||||
if (this._options.extraHTTPHeaders || this._options.locale) promises.push(this.setExtraHTTPHeaders(this._options.extraHTTPHeaders || []));
|
||||
if (this._options.httpCredentials) promises.push(this.setHTTPCredentials(this._options.httpCredentials));
|
||||
if (this._options.geolocation) promises.push(this.setGeolocation(this._options.geolocation));
|
||||
if (this._options.offline) promises.push(this.setOffline(this._options.offline));
|
||||
if (this._options.colorScheme !== 'no-override') {
|
||||
promises.push(this._browser.session.send('Browser.setColorScheme', {
|
||||
browserContextId,
|
||||
colorScheme: this._options.colorScheme !== undefined ? this._options.colorScheme : 'light'
|
||||
}));
|
||||
}
|
||||
if (this._options.reducedMotion !== 'no-override') {
|
||||
promises.push(this._browser.session.send('Browser.setReducedMotion', {
|
||||
browserContextId,
|
||||
reducedMotion: this._options.reducedMotion !== undefined ? this._options.reducedMotion : 'no-preference'
|
||||
}));
|
||||
}
|
||||
if (this._options.forcedColors !== 'no-override') {
|
||||
promises.push(this._browser.session.send('Browser.setForcedColors', {
|
||||
browserContextId,
|
||||
forcedColors: this._options.forcedColors !== undefined ? this._options.forcedColors : 'none'
|
||||
}));
|
||||
}
|
||||
if (this._options.recordVideo) {
|
||||
promises.push(this._ensureVideosPath().then(() => {
|
||||
return this._browser.session.send('Browser.setVideoRecordingOptions', {
|
||||
// validateBrowserContextOptions ensures correct video size.
|
||||
options: {
|
||||
...this._options.recordVideo.size,
|
||||
dir: this._options.recordVideo.dir
|
||||
},
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}));
|
||||
}
|
||||
const proxy = this._options.proxyOverride || this._options.proxy;
|
||||
if (proxy) {
|
||||
promises.push(this._browser.session.send('Browser.setContextProxy', {
|
||||
browserContextId: this._browserContextId,
|
||||
...toJugglerProxyOptions(proxy)
|
||||
}));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
}
|
||||
_ffPages() {
|
||||
return Array.from(this._browser._ffPages.values()).filter(ffPage => ffPage._browserContext === this);
|
||||
}
|
||||
pages() {
|
||||
return this._ffPages().map(ffPage => ffPage._initializedPage).filter(pageOrNull => !!pageOrNull);
|
||||
}
|
||||
async newPageDelegate() {
|
||||
(0, _browserContext.assertBrowserContextIsNotOwned)(this);
|
||||
const {
|
||||
targetId
|
||||
} = await this._browser.session.send('Browser.newPage', {
|
||||
browserContextId: this._browserContextId
|
||||
}).catch(e => {
|
||||
if (e.message.includes('Failed to override timezone')) throw new Error(`Invalid timezone ID: ${this._options.timezoneId}`);
|
||||
throw e;
|
||||
});
|
||||
return this._browser._ffPages.get(targetId);
|
||||
}
|
||||
async doGetCookies(urls) {
|
||||
const {
|
||||
cookies
|
||||
} = await this._browser.session.send('Browser.getCookies', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
return network.filterCookies(cookies.map(c => {
|
||||
const copy = {
|
||||
...c
|
||||
};
|
||||
delete copy.size;
|
||||
delete copy.session;
|
||||
return copy;
|
||||
}), urls);
|
||||
}
|
||||
async addCookies(cookies) {
|
||||
const cc = network.rewriteCookies(cookies).map(c => ({
|
||||
...c,
|
||||
expires: c.expires === -1 ? undefined : c.expires
|
||||
}));
|
||||
await this._browser.session.send('Browser.setCookies', {
|
||||
browserContextId: this._browserContextId,
|
||||
cookies: cc
|
||||
});
|
||||
}
|
||||
async doClearCookies() {
|
||||
await this._browser.session.send('Browser.clearCookies', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
async doGrantPermissions(origin, permissions) {
|
||||
const webPermissionToProtocol = new Map([['geolocation', 'geo'], ['persistent-storage', 'persistent-storage'], ['push', 'push'], ['notifications', 'desktop-notification']]);
|
||||
const filtered = permissions.map(permission => {
|
||||
const protocolPermission = webPermissionToProtocol.get(permission);
|
||||
if (!protocolPermission) throw new Error('Unknown permission: ' + permission);
|
||||
return protocolPermission;
|
||||
});
|
||||
await this._browser.session.send('Browser.grantPermissions', {
|
||||
origin: origin,
|
||||
browserContextId: this._browserContextId,
|
||||
permissions: filtered
|
||||
});
|
||||
}
|
||||
async doClearPermissions() {
|
||||
await this._browser.session.send('Browser.resetPermissions', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
async setGeolocation(geolocation) {
|
||||
(0, _browserContext.verifyGeolocation)(geolocation);
|
||||
this._options.geolocation = geolocation;
|
||||
await this._browser.session.send('Browser.setGeolocationOverride', {
|
||||
browserContextId: this._browserContextId,
|
||||
geolocation: geolocation || null
|
||||
});
|
||||
}
|
||||
async setExtraHTTPHeaders(headers) {
|
||||
this._options.extraHTTPHeaders = headers;
|
||||
let allHeaders = this._options.extraHTTPHeaders;
|
||||
if (this._options.locale) allHeaders = network.mergeHeaders([allHeaders, network.singleHeader('Accept-Language', this._options.locale)]);
|
||||
await this._browser.session.send('Browser.setExtraHTTPHeaders', {
|
||||
browserContextId: this._browserContextId,
|
||||
headers: allHeaders
|
||||
});
|
||||
}
|
||||
async setUserAgent(userAgent) {
|
||||
await this._browser.session.send('Browser.setUserAgentOverride', {
|
||||
browserContextId: this._browserContextId,
|
||||
userAgent: userAgent || null
|
||||
});
|
||||
}
|
||||
async setOffline(offline) {
|
||||
this._options.offline = offline;
|
||||
await this._browser.session.send('Browser.setOnlineOverride', {
|
||||
browserContextId: this._browserContextId,
|
||||
override: offline ? 'offline' : 'online'
|
||||
});
|
||||
}
|
||||
async doSetHTTPCredentials(httpCredentials) {
|
||||
this._options.httpCredentials = httpCredentials;
|
||||
let credentials = null;
|
||||
if (httpCredentials) {
|
||||
const {
|
||||
username,
|
||||
password,
|
||||
origin
|
||||
} = httpCredentials;
|
||||
credentials = {
|
||||
username,
|
||||
password,
|
||||
origin
|
||||
};
|
||||
}
|
||||
await this._browser.session.send('Browser.setHTTPCredentials', {
|
||||
browserContextId: this._browserContextId,
|
||||
credentials
|
||||
});
|
||||
}
|
||||
async doAddInitScript(initScript) {
|
||||
await this._updateInitScripts();
|
||||
}
|
||||
async doRemoveNonInternalInitScripts() {
|
||||
await this._updateInitScripts();
|
||||
}
|
||||
async _updateInitScripts() {
|
||||
const bindingScripts = [...this._pageBindings.values()].map(binding => binding.initScript.source);
|
||||
const initScripts = this.initScripts.map(script => script.source);
|
||||
await this._browser.session.send('Browser.setInitScripts', {
|
||||
browserContextId: this._browserContextId,
|
||||
scripts: [...bindingScripts, ...initScripts].map(script => ({
|
||||
script
|
||||
}))
|
||||
});
|
||||
}
|
||||
async doUpdateRequestInterception() {
|
||||
await Promise.all([this._browser.session.send('Browser.setRequestInterception', {
|
||||
browserContextId: this._browserContextId,
|
||||
enabled: !!this._requestInterceptor
|
||||
}), this._browser.session.send('Browser.setCacheDisabled', {
|
||||
browserContextId: this._browserContextId,
|
||||
cacheDisabled: !!this._requestInterceptor
|
||||
})]);
|
||||
}
|
||||
onClosePersistent() {}
|
||||
async clearCache() {
|
||||
// Clearing only the context cache does not work: https://bugzilla.mozilla.org/show_bug.cgi?id=1819147
|
||||
await this._browser.session.send('Browser.clearCache');
|
||||
}
|
||||
async doClose(reason) {
|
||||
if (!this._browserContextId) {
|
||||
if (this._options.recordVideo) {
|
||||
await this._browser.session.send('Browser.setVideoRecordingOptions', {
|
||||
options: undefined,
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
}
|
||||
// Closing persistent context should close the browser.
|
||||
await this._browser.close({
|
||||
reason
|
||||
});
|
||||
} else {
|
||||
await this._browser.session.send('Browser.removeBrowserContext', {
|
||||
browserContextId: this._browserContextId
|
||||
});
|
||||
this._browser._contexts.delete(this._browserContextId);
|
||||
}
|
||||
}
|
||||
async cancelDownload(uuid) {
|
||||
await this._browser.session.send('Browser.cancelDownload', {
|
||||
uuid
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.FFBrowserContext = FFBrowserContext;
|
||||
function toJugglerProxyOptions(proxy) {
|
||||
const proxyServer = new URL(proxy.server);
|
||||
let port = parseInt(proxyServer.port, 10);
|
||||
let type = 'http';
|
||||
if (proxyServer.protocol === 'socks5:') type = 'socks';else if (proxyServer.protocol === 'socks4:') type = 'socks4';else if (proxyServer.protocol === 'https:') type = 'https';
|
||||
if (proxyServer.port === '') {
|
||||
if (proxyServer.protocol === 'http:') port = 80;else if (proxyServer.protocol === 'https:') port = 443;
|
||||
}
|
||||
return {
|
||||
type,
|
||||
bypass: proxy.bypass ? proxy.bypass.split(',').map(domain => domain.trim()) : [],
|
||||
host: proxyServer.hostname,
|
||||
port,
|
||||
username: proxy.username,
|
||||
password: proxy.password
|
||||
};
|
||||
}
|
||||
|
||||
// Prefs for quick fixes that didn't make it to the build.
|
||||
// Should all be moved to `playwright.cfg`.
|
||||
const kBandaidFirefoxUserPrefs = {};
|
||||
168
node_modules/playwright-core/lib/server/firefox/ffConnection.js
generated
vendored
Normal file
168
node_modules/playwright-core/lib/server/firefox/ffConnection.js
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.kBrowserCloseMessageId = exports.FFSession = exports.FFConnection = exports.ConnectionEvents = void 0;
|
||||
var _events = require("events");
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _helper = require("../helper");
|
||||
var _protocolError = require("../protocolError");
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const ConnectionEvents = exports.ConnectionEvents = {
|
||||
Disconnected: Symbol('Disconnected')
|
||||
};
|
||||
|
||||
// FFPlaywright uses this special id to issue Browser.close command which we
|
||||
// should ignore.
|
||||
const kBrowserCloseMessageId = exports.kBrowserCloseMessageId = -9999;
|
||||
class FFConnection extends _events.EventEmitter {
|
||||
constructor(transport, protocolLogger, browserLogsCollector) {
|
||||
super();
|
||||
this._lastId = void 0;
|
||||
this._transport = void 0;
|
||||
this._protocolLogger = void 0;
|
||||
this._browserLogsCollector = void 0;
|
||||
this._browserDisconnectedLogs = void 0;
|
||||
this.rootSession = void 0;
|
||||
this._sessions = void 0;
|
||||
this._closed = void 0;
|
||||
this.setMaxListeners(0);
|
||||
this._transport = transport;
|
||||
this._protocolLogger = protocolLogger;
|
||||
this._browserLogsCollector = browserLogsCollector;
|
||||
this._lastId = 0;
|
||||
this._sessions = new Map();
|
||||
this._closed = false;
|
||||
this.rootSession = new FFSession(this, '', message => this._rawSend(message));
|
||||
this._sessions.set('', this.rootSession);
|
||||
this._transport.onmessage = this._onMessage.bind(this);
|
||||
// onclose should be set last, since it can be immediately called.
|
||||
this._transport.onclose = this._onClose.bind(this);
|
||||
}
|
||||
nextMessageId() {
|
||||
return ++this._lastId;
|
||||
}
|
||||
_rawSend(message) {
|
||||
this._protocolLogger('send', message);
|
||||
this._transport.send(message);
|
||||
}
|
||||
async _onMessage(message) {
|
||||
this._protocolLogger('receive', message);
|
||||
if (message.id === kBrowserCloseMessageId) return;
|
||||
const session = this._sessions.get(message.sessionId || '');
|
||||
if (session) session.dispatchMessage(message);
|
||||
}
|
||||
_onClose(reason) {
|
||||
this._closed = true;
|
||||
this._transport.onmessage = undefined;
|
||||
this._transport.onclose = undefined;
|
||||
this._browserDisconnectedLogs = _helper.helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
|
||||
this.rootSession.dispose();
|
||||
Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
|
||||
}
|
||||
close() {
|
||||
if (!this._closed) this._transport.close();
|
||||
}
|
||||
createSession(sessionId) {
|
||||
const session = new FFSession(this, sessionId, message => this._rawSend({
|
||||
...message,
|
||||
sessionId
|
||||
}));
|
||||
this._sessions.set(sessionId, session);
|
||||
return session;
|
||||
}
|
||||
}
|
||||
exports.FFConnection = FFConnection;
|
||||
class FFSession extends _events.EventEmitter {
|
||||
constructor(connection, sessionId, rawSend) {
|
||||
super();
|
||||
this._connection = void 0;
|
||||
this._disposed = false;
|
||||
this._callbacks = void 0;
|
||||
this._sessionId = void 0;
|
||||
this._rawSend = void 0;
|
||||
this._crashed = false;
|
||||
this.on = void 0;
|
||||
this.addListener = void 0;
|
||||
this.off = void 0;
|
||||
this.removeListener = void 0;
|
||||
this.once = void 0;
|
||||
this.setMaxListeners(0);
|
||||
this._callbacks = new Map();
|
||||
this._connection = connection;
|
||||
this._sessionId = sessionId;
|
||||
this._rawSend = rawSend;
|
||||
this.on = super.on;
|
||||
this.addListener = super.addListener;
|
||||
this.off = super.removeListener;
|
||||
this.removeListener = super.removeListener;
|
||||
this.once = super.once;
|
||||
}
|
||||
markAsCrashed() {
|
||||
this._crashed = true;
|
||||
}
|
||||
async send(method, params) {
|
||||
if (this._crashed || this._disposed || this._connection._closed || this._connection._browserDisconnectedLogs) throw new _protocolError.ProtocolError(this._crashed ? 'crashed' : 'closed', undefined, this._connection._browserDisconnectedLogs);
|
||||
const id = this._connection.nextMessageId();
|
||||
this._rawSend({
|
||||
method,
|
||||
params,
|
||||
id
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
this._callbacks.set(id, {
|
||||
resolve,
|
||||
reject,
|
||||
error: new _protocolError.ProtocolError('error', method)
|
||||
});
|
||||
});
|
||||
}
|
||||
sendMayFail(method, params) {
|
||||
return this.send(method, params).catch(error => _debugLogger.debugLogger.log('error', error));
|
||||
}
|
||||
dispatchMessage(object) {
|
||||
if (object.id) {
|
||||
const callback = this._callbacks.get(object.id);
|
||||
// Callbacks could be all rejected if someone has called `.dispose()`.
|
||||
if (callback) {
|
||||
this._callbacks.delete(object.id);
|
||||
if (object.error) {
|
||||
callback.error.setMessage(object.error.message);
|
||||
callback.reject(callback.error);
|
||||
} else {
|
||||
callback.resolve(object.result);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Promise.resolve().then(() => this.emit(object.method, object.params));
|
||||
}
|
||||
}
|
||||
dispose() {
|
||||
this._disposed = true;
|
||||
this._connection._sessions.delete(this._sessionId);
|
||||
for (const callback of this._callbacks.values()) {
|
||||
callback.error.type = this._crashed ? 'crashed' : 'closed';
|
||||
callback.error.logs = this._connection._browserDisconnectedLogs;
|
||||
callback.reject(callback.error);
|
||||
}
|
||||
this._callbacks.clear();
|
||||
}
|
||||
}
|
||||
exports.FFSession = FFSession;
|
||||
123
node_modules/playwright-core/lib/server/firefox/ffExecutionContext.js
generated
vendored
Normal file
123
node_modules/playwright-core/lib/server/firefox/ffExecutionContext.js
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FFExecutionContext = void 0;
|
||||
var js = _interopRequireWildcard(require("../javascript"));
|
||||
var _stackTrace = require("../../utils/stackTrace");
|
||||
var _utilityScriptSerializers = require("../isomorphic/utilityScriptSerializers");
|
||||
var _protocolError = require("../protocolError");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FFExecutionContext {
|
||||
constructor(session, executionContextId) {
|
||||
this._session = void 0;
|
||||
this._executionContextId = void 0;
|
||||
this._session = session;
|
||||
this._executionContextId = executionContextId;
|
||||
}
|
||||
async rawEvaluateJSON(expression) {
|
||||
const payload = await this._session.send('Runtime.evaluate', {
|
||||
expression,
|
||||
returnByValue: true,
|
||||
executionContextId: this._executionContextId
|
||||
}).catch(rewriteError);
|
||||
checkException(payload.exceptionDetails);
|
||||
return payload.result.value;
|
||||
}
|
||||
async rawEvaluateHandle(expression) {
|
||||
const payload = await this._session.send('Runtime.evaluate', {
|
||||
expression,
|
||||
returnByValue: false,
|
||||
executionContextId: this._executionContextId
|
||||
}).catch(rewriteError);
|
||||
checkException(payload.exceptionDetails);
|
||||
return payload.result.objectId;
|
||||
}
|
||||
async evaluateWithArguments(expression, returnByValue, utilityScript, values, objectIds) {
|
||||
const payload = await this._session.send('Runtime.callFunction', {
|
||||
functionDeclaration: expression,
|
||||
args: [{
|
||||
objectId: utilityScript._objectId,
|
||||
value: undefined
|
||||
}, ...values.map(value => ({
|
||||
value
|
||||
})), ...objectIds.map(objectId => ({
|
||||
objectId,
|
||||
value: undefined
|
||||
}))],
|
||||
returnByValue,
|
||||
executionContextId: this._executionContextId
|
||||
}).catch(rewriteError);
|
||||
checkException(payload.exceptionDetails);
|
||||
if (returnByValue) return (0, _utilityScriptSerializers.parseEvaluationResultValue)(payload.result.value);
|
||||
return utilityScript._context.createHandle(payload.result);
|
||||
}
|
||||
async getProperties(context, objectId) {
|
||||
const response = await this._session.send('Runtime.getObjectProperties', {
|
||||
executionContextId: this._executionContextId,
|
||||
objectId
|
||||
});
|
||||
const result = new Map();
|
||||
for (const property of response.properties) result.set(property.name, context.createHandle(property.value));
|
||||
return result;
|
||||
}
|
||||
createHandle(context, remoteObject) {
|
||||
return new js.JSHandle(context, remoteObject.subtype || remoteObject.type || '', renderPreview(remoteObject), remoteObject.objectId, potentiallyUnserializableValue(remoteObject));
|
||||
}
|
||||
async releaseHandle(objectId) {
|
||||
await this._session.send('Runtime.disposeObject', {
|
||||
executionContextId: this._executionContextId,
|
||||
objectId
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.FFExecutionContext = FFExecutionContext;
|
||||
function checkException(exceptionDetails) {
|
||||
if (!exceptionDetails) return;
|
||||
if (exceptionDetails.value) throw new js.JavaScriptErrorInEvaluate(JSON.stringify(exceptionDetails.value));else throw new js.JavaScriptErrorInEvaluate(exceptionDetails.text + (exceptionDetails.stack ? '\n' + exceptionDetails.stack : ''));
|
||||
}
|
||||
function rewriteError(error) {
|
||||
if (error.message.includes('cyclic object value') || error.message.includes('Object is not serializable')) return {
|
||||
result: {
|
||||
type: 'undefined',
|
||||
value: undefined
|
||||
}
|
||||
};
|
||||
if (error instanceof TypeError && error.message.startsWith('Converting circular structure to JSON')) (0, _stackTrace.rewriteErrorMessage)(error, error.message + ' Are you passing a nested JSHandle?');
|
||||
if (!js.isJavaScriptErrorInEvaluate(error) && !(0, _protocolError.isSessionClosedError)(error)) throw new Error('Execution context was destroyed, most likely because of a navigation.');
|
||||
throw error;
|
||||
}
|
||||
function potentiallyUnserializableValue(remoteObject) {
|
||||
const value = remoteObject.value;
|
||||
const unserializableValue = remoteObject.unserializableValue;
|
||||
return unserializableValue ? js.parseUnserializableValue(unserializableValue) : value;
|
||||
}
|
||||
function renderPreview(object) {
|
||||
if (object.type === 'undefined') return 'undefined';
|
||||
if (object.unserializableValue) return String(object.unserializableValue);
|
||||
if (object.type === 'symbol') return 'Symbol()';
|
||||
if (object.subtype === 'regexp') return 'RegExp';
|
||||
if (object.subtype === 'weakmap') return 'WeakMap';
|
||||
if (object.subtype === 'weakset') return 'WeakSet';
|
||||
if (object.subtype) return object.subtype[0].toUpperCase() + object.subtype.slice(1);
|
||||
if ('value' in object) return String(object.value);
|
||||
}
|
||||
150
node_modules/playwright-core/lib/server/firefox/ffInput.js
generated
vendored
Normal file
150
node_modules/playwright-core/lib/server/firefox/ffInput.js
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.RawTouchscreenImpl = exports.RawMouseImpl = exports.RawKeyboardImpl = void 0;
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function toModifiersMask(modifiers) {
|
||||
let mask = 0;
|
||||
if (modifiers.has('Alt')) mask |= 1;
|
||||
if (modifiers.has('Control')) mask |= 2;
|
||||
if (modifiers.has('Shift')) mask |= 4;
|
||||
if (modifiers.has('Meta')) mask |= 8;
|
||||
return mask;
|
||||
}
|
||||
function toButtonNumber(button) {
|
||||
if (button === 'left') return 0;
|
||||
if (button === 'middle') return 1;
|
||||
if (button === 'right') return 2;
|
||||
return 0;
|
||||
}
|
||||
function toButtonsMask(buttons) {
|
||||
let mask = 0;
|
||||
if (buttons.has('left')) mask |= 1;
|
||||
if (buttons.has('right')) mask |= 2;
|
||||
if (buttons.has('middle')) mask |= 4;
|
||||
return mask;
|
||||
}
|
||||
class RawKeyboardImpl {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._client = client;
|
||||
}
|
||||
async keydown(modifiers, code, keyCode, keyCodeWithoutLocation, key, location, autoRepeat, text) {
|
||||
// Firefox will figure out Enter by itself
|
||||
if (text === '\r') text = '';
|
||||
await this._client.send('Page.dispatchKeyEvent', {
|
||||
type: 'keydown',
|
||||
keyCode: keyCodeWithoutLocation,
|
||||
code,
|
||||
key,
|
||||
repeat: autoRepeat,
|
||||
location,
|
||||
text
|
||||
});
|
||||
}
|
||||
async keyup(modifiers, code, keyCode, keyCodeWithoutLocation, key, location) {
|
||||
await this._client.send('Page.dispatchKeyEvent', {
|
||||
type: 'keyup',
|
||||
key,
|
||||
keyCode: keyCodeWithoutLocation,
|
||||
code,
|
||||
location,
|
||||
repeat: false
|
||||
});
|
||||
}
|
||||
async sendText(text) {
|
||||
await this._client.send('Page.insertText', {
|
||||
text
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RawKeyboardImpl = RawKeyboardImpl;
|
||||
class RawMouseImpl {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._page = void 0;
|
||||
this._client = client;
|
||||
}
|
||||
async move(x, y, button, buttons, modifiers, forClick) {
|
||||
await this._client.send('Page.dispatchMouseEvent', {
|
||||
type: 'mousemove',
|
||||
button: 0,
|
||||
buttons: toButtonsMask(buttons),
|
||||
x: Math.floor(x),
|
||||
y: Math.floor(y),
|
||||
modifiers: toModifiersMask(modifiers)
|
||||
});
|
||||
}
|
||||
async down(x, y, button, buttons, modifiers, clickCount) {
|
||||
await this._client.send('Page.dispatchMouseEvent', {
|
||||
type: 'mousedown',
|
||||
button: toButtonNumber(button),
|
||||
buttons: toButtonsMask(buttons),
|
||||
x: Math.floor(x),
|
||||
y: Math.floor(y),
|
||||
modifiers: toModifiersMask(modifiers),
|
||||
clickCount
|
||||
});
|
||||
}
|
||||
async up(x, y, button, buttons, modifiers, clickCount) {
|
||||
await this._client.send('Page.dispatchMouseEvent', {
|
||||
type: 'mouseup',
|
||||
button: toButtonNumber(button),
|
||||
buttons: toButtonsMask(buttons),
|
||||
x: Math.floor(x),
|
||||
y: Math.floor(y),
|
||||
modifiers: toModifiersMask(modifiers),
|
||||
clickCount
|
||||
});
|
||||
}
|
||||
async wheel(x, y, buttons, modifiers, deltaX, deltaY) {
|
||||
// Wheel events hit the compositor first, so wait one frame for it to be synced.
|
||||
await this._page.mainFrame().evaluateExpression(`new Promise(requestAnimationFrame)`, {
|
||||
world: 'utility'
|
||||
});
|
||||
await this._client.send('Page.dispatchWheelEvent', {
|
||||
deltaX,
|
||||
deltaY,
|
||||
x: Math.floor(x),
|
||||
y: Math.floor(y),
|
||||
deltaZ: 0,
|
||||
modifiers: toModifiersMask(modifiers)
|
||||
});
|
||||
}
|
||||
setPage(page) {
|
||||
this._page = page;
|
||||
}
|
||||
}
|
||||
exports.RawMouseImpl = RawMouseImpl;
|
||||
class RawTouchscreenImpl {
|
||||
constructor(client) {
|
||||
this._client = void 0;
|
||||
this._client = client;
|
||||
}
|
||||
async tap(x, y, modifiers) {
|
||||
await this._client.send('Page.dispatchTapEvent', {
|
||||
x,
|
||||
y,
|
||||
modifiers: toModifiersMask(modifiers)
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.RawTouchscreenImpl = RawTouchscreenImpl;
|
||||
233
node_modules/playwright-core/lib/server/firefox/ffNetworkManager.js
generated
vendored
Normal file
233
node_modules/playwright-core/lib/server/firefox/ffNetworkManager.js
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FFNetworkManager = void 0;
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var network = _interopRequireWildcard(require("../network"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FFNetworkManager {
|
||||
constructor(session, page) {
|
||||
this._session = void 0;
|
||||
this._requests = void 0;
|
||||
this._page = void 0;
|
||||
this._eventListeners = void 0;
|
||||
this._session = session;
|
||||
this._requests = new Map();
|
||||
this._page = page;
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.requestFinished', this._onRequestFinished.bind(this)), _eventsHelper.eventsHelper.addEventListener(session, 'Network.requestFailed', this._onRequestFailed.bind(this))];
|
||||
}
|
||||
dispose() {
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
}
|
||||
async setRequestInterception(enabled) {
|
||||
await Promise.all([this._session.send('Network.setRequestInterception', {
|
||||
enabled
|
||||
}), this._session.send('Page.setCacheDisabled', {
|
||||
cacheDisabled: enabled
|
||||
})]);
|
||||
}
|
||||
_onRequestWillBeSent(event) {
|
||||
const redirectedFrom = event.redirectedFrom ? this._requests.get(event.redirectedFrom) || null : null;
|
||||
const frame = redirectedFrom ? redirectedFrom.request.frame() : event.frameId ? this._page._frameManager.frame(event.frameId) : null;
|
||||
if (!frame) return;
|
||||
if (redirectedFrom) this._requests.delete(redirectedFrom._id);
|
||||
const request = new InterceptableRequest(frame, redirectedFrom, event);
|
||||
let route;
|
||||
if (event.isIntercepted) route = new FFRouteImpl(this._session, request);
|
||||
this._requests.set(request._id, request);
|
||||
this._page._frameManager.requestStarted(request.request, route);
|
||||
}
|
||||
_onResponseReceived(event) {
|
||||
var _event$securityDetail, _event$securityDetail2, _event$securityDetail3, _event$securityDetail4, _event$securityDetail5;
|
||||
const request = this._requests.get(event.requestId);
|
||||
if (!request) return;
|
||||
const getResponseBody = async () => {
|
||||
const response = await this._session.send('Network.getResponseBody', {
|
||||
requestId: request._id
|
||||
});
|
||||
if (response.evicted) throw new Error(`Response body for ${request.request.method()} ${request.request.url()} was evicted!`);
|
||||
return Buffer.from(response.base64body, 'base64');
|
||||
};
|
||||
const startTime = event.timing.startTime;
|
||||
function relativeToStart(time) {
|
||||
if (!time) return -1;
|
||||
return (time - startTime) / 1000;
|
||||
}
|
||||
const timing = {
|
||||
startTime: startTime / 1000,
|
||||
domainLookupStart: relativeToStart(event.timing.domainLookupStart),
|
||||
domainLookupEnd: relativeToStart(event.timing.domainLookupEnd),
|
||||
connectStart: relativeToStart(event.timing.connectStart),
|
||||
secureConnectionStart: relativeToStart(event.timing.secureConnectionStart),
|
||||
connectEnd: relativeToStart(event.timing.connectEnd),
|
||||
requestStart: relativeToStart(event.timing.requestStart),
|
||||
responseStart: relativeToStart(event.timing.responseStart)
|
||||
};
|
||||
const response = new network.Response(request.request, event.status, event.statusText, parseMultivalueHeaders(event.headers), timing, getResponseBody, event.fromServiceWorker);
|
||||
if (event !== null && event !== void 0 && event.remoteIPAddress && typeof (event === null || event === void 0 ? void 0 : event.remotePort) === 'number') {
|
||||
response._serverAddrFinished({
|
||||
ipAddress: event.remoteIPAddress,
|
||||
port: event.remotePort
|
||||
});
|
||||
} else {
|
||||
response._serverAddrFinished();
|
||||
}
|
||||
response._securityDetailsFinished({
|
||||
protocol: event === null || event === void 0 || (_event$securityDetail = event.securityDetails) === null || _event$securityDetail === void 0 ? void 0 : _event$securityDetail.protocol,
|
||||
subjectName: event === null || event === void 0 || (_event$securityDetail2 = event.securityDetails) === null || _event$securityDetail2 === void 0 ? void 0 : _event$securityDetail2.subjectName,
|
||||
issuer: event === null || event === void 0 || (_event$securityDetail3 = event.securityDetails) === null || _event$securityDetail3 === void 0 ? void 0 : _event$securityDetail3.issuer,
|
||||
validFrom: event === null || event === void 0 || (_event$securityDetail4 = event.securityDetails) === null || _event$securityDetail4 === void 0 ? void 0 : _event$securityDetail4.validFrom,
|
||||
validTo: event === null || event === void 0 || (_event$securityDetail5 = event.securityDetails) === null || _event$securityDetail5 === void 0 ? void 0 : _event$securityDetail5.validTo
|
||||
});
|
||||
// "raw" headers are the same as "provisional" headers in Firefox.
|
||||
response.setRawResponseHeaders(null);
|
||||
// Headers size are not available in Firefox.
|
||||
response.setResponseHeadersSize(null);
|
||||
this._page._frameManager.requestReceivedResponse(response);
|
||||
}
|
||||
_onRequestFinished(event) {
|
||||
const request = this._requests.get(event.requestId);
|
||||
if (!request) return;
|
||||
const response = request.request._existingResponse();
|
||||
response.setTransferSize(event.transferSize);
|
||||
response.setEncodedBodySize(event.encodedBodySize);
|
||||
|
||||
// Keep redirected requests in the map for future reference as redirectedFrom.
|
||||
const isRedirected = response.status() >= 300 && response.status() <= 399;
|
||||
const responseEndTime = event.responseEndTime ? event.responseEndTime / 1000 - response.timing().startTime : -1;
|
||||
if (isRedirected) {
|
||||
response._requestFinished(responseEndTime);
|
||||
} else {
|
||||
this._requests.delete(request._id);
|
||||
response._requestFinished(responseEndTime);
|
||||
}
|
||||
if (event.protocolVersion) response._setHttpVersion(event.protocolVersion);
|
||||
this._page._frameManager.reportRequestFinished(request.request, response);
|
||||
}
|
||||
_onRequestFailed(event) {
|
||||
const request = this._requests.get(event.requestId);
|
||||
if (!request) return;
|
||||
this._requests.delete(request._id);
|
||||
const response = request.request._existingResponse();
|
||||
if (response) {
|
||||
response.setTransferSize(null);
|
||||
response.setEncodedBodySize(null);
|
||||
response._requestFinished(-1);
|
||||
}
|
||||
request.request._setFailureText(event.errorCode);
|
||||
this._page._frameManager.requestFailed(request.request, event.errorCode === 'NS_BINDING_ABORTED');
|
||||
}
|
||||
}
|
||||
exports.FFNetworkManager = FFNetworkManager;
|
||||
const causeToResourceType = {
|
||||
TYPE_INVALID: 'other',
|
||||
TYPE_OTHER: 'other',
|
||||
TYPE_SCRIPT: 'script',
|
||||
TYPE_IMAGE: 'image',
|
||||
TYPE_STYLESHEET: 'stylesheet',
|
||||
TYPE_OBJECT: 'other',
|
||||
TYPE_DOCUMENT: 'document',
|
||||
TYPE_SUBDOCUMENT: 'document',
|
||||
TYPE_REFRESH: 'document',
|
||||
TYPE_XBL: 'other',
|
||||
TYPE_PING: 'other',
|
||||
TYPE_XMLHTTPREQUEST: 'xhr',
|
||||
TYPE_OBJECT_SUBREQUEST: 'other',
|
||||
TYPE_DTD: 'other',
|
||||
TYPE_FONT: 'font',
|
||||
TYPE_MEDIA: 'media',
|
||||
TYPE_WEBSOCKET: 'websocket',
|
||||
TYPE_CSP_REPORT: 'other',
|
||||
TYPE_XSLT: 'other',
|
||||
TYPE_BEACON: 'other',
|
||||
TYPE_FETCH: 'fetch',
|
||||
TYPE_IMAGESET: 'image',
|
||||
TYPE_WEB_MANIFEST: 'manifest'
|
||||
};
|
||||
const internalCauseToResourceType = {
|
||||
TYPE_INTERNAL_EVENTSOURCE: 'eventsource'
|
||||
};
|
||||
class InterceptableRequest {
|
||||
constructor(frame, redirectedFrom, payload) {
|
||||
this.request = void 0;
|
||||
this._id = void 0;
|
||||
this._redirectedTo = void 0;
|
||||
this._id = payload.requestId;
|
||||
if (redirectedFrom) redirectedFrom._redirectedTo = this;
|
||||
let postDataBuffer = null;
|
||||
if (payload.postData) postDataBuffer = Buffer.from(payload.postData, 'base64');
|
||||
this.request = new network.Request(frame._page._browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigationId, payload.url, internalCauseToResourceType[payload.internalCause] || causeToResourceType[payload.cause] || 'other', payload.method, postDataBuffer, payload.headers);
|
||||
// "raw" headers are the same as "provisional" headers in Firefox.
|
||||
this.request.setRawRequestHeaders(null);
|
||||
}
|
||||
_finalRequest() {
|
||||
let request = this;
|
||||
while (request._redirectedTo) request = request._redirectedTo;
|
||||
return request;
|
||||
}
|
||||
}
|
||||
class FFRouteImpl {
|
||||
constructor(session, request) {
|
||||
this._request = void 0;
|
||||
this._session = void 0;
|
||||
this._session = session;
|
||||
this._request = request;
|
||||
}
|
||||
async continue(overrides) {
|
||||
await this._session.sendMayFail('Network.resumeInterceptedRequest', {
|
||||
requestId: this._request._id,
|
||||
url: overrides.url,
|
||||
method: overrides.method,
|
||||
headers: overrides.headers,
|
||||
postData: overrides.postData ? Buffer.from(overrides.postData).toString('base64') : undefined
|
||||
});
|
||||
}
|
||||
async fulfill(response) {
|
||||
const base64body = response.isBase64 ? response.body : Buffer.from(response.body).toString('base64');
|
||||
await this._session.sendMayFail('Network.fulfillInterceptedRequest', {
|
||||
requestId: this._request._id,
|
||||
status: response.status,
|
||||
statusText: network.statusText(response.status),
|
||||
headers: response.headers,
|
||||
base64body
|
||||
});
|
||||
}
|
||||
async abort(errorCode) {
|
||||
await this._session.sendMayFail('Network.abortInterceptedRequest', {
|
||||
requestId: this._request._id,
|
||||
errorCode
|
||||
});
|
||||
}
|
||||
}
|
||||
function parseMultivalueHeaders(headers) {
|
||||
const result = [];
|
||||
for (const header of headers) {
|
||||
const separator = header.name.toLowerCase() === 'set-cookie' ? '\n' : ',';
|
||||
const tokens = header.value.split(separator).map(s => s.trim());
|
||||
for (const token of tokens) result.push({
|
||||
name: header.name,
|
||||
value: token
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
559
node_modules/playwright-core/lib/server/firefox/ffPage.js
generated
vendored
Normal file
559
node_modules/playwright-core/lib/server/firefox/ffPage.js
generated
vendored
Normal file
@@ -0,0 +1,559 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.UTILITY_WORLD_NAME = exports.FFPage = void 0;
|
||||
var dialog = _interopRequireWildcard(require("../dialog"));
|
||||
var dom = _interopRequireWildcard(require("../dom"));
|
||||
var _eventsHelper = require("../../utils/eventsHelper");
|
||||
var _page = require("../page");
|
||||
var _ffAccessibility = require("./ffAccessibility");
|
||||
var _ffConnection = require("./ffConnection");
|
||||
var _ffExecutionContext = require("./ffExecutionContext");
|
||||
var _ffInput = require("./ffInput");
|
||||
var _ffNetworkManager = require("./ffNetworkManager");
|
||||
var _stackTrace = require("../../utils/stackTrace");
|
||||
var _debugLogger = require("../../utils/debugLogger");
|
||||
var _manualPromise = require("../../utils/manualPromise");
|
||||
var _browserContext = require("../browserContext");
|
||||
var _errors = require("../errors");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2019 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const UTILITY_WORLD_NAME = exports.UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||
class FFPage {
|
||||
constructor(session, browserContext, opener) {
|
||||
this.cspErrorsAsynchronousForInlineScripts = true;
|
||||
this.rawMouse = void 0;
|
||||
this.rawKeyboard = void 0;
|
||||
this.rawTouchscreen = void 0;
|
||||
this._session = void 0;
|
||||
this._page = void 0;
|
||||
this._networkManager = void 0;
|
||||
this._browserContext = void 0;
|
||||
this._pagePromise = new _manualPromise.ManualPromise();
|
||||
this._initializedPage = null;
|
||||
this._initializationFailed = false;
|
||||
this._opener = void 0;
|
||||
this._contextIdToContext = void 0;
|
||||
this._eventListeners = void 0;
|
||||
this._workers = new Map();
|
||||
this._screencastId = void 0;
|
||||
this._initScripts = [];
|
||||
this._session = session;
|
||||
this._opener = opener;
|
||||
this.rawKeyboard = new _ffInput.RawKeyboardImpl(session);
|
||||
this.rawMouse = new _ffInput.RawMouseImpl(session);
|
||||
this.rawTouchscreen = new _ffInput.RawTouchscreenImpl(session);
|
||||
this._contextIdToContext = new Map();
|
||||
this._browserContext = browserContext;
|
||||
this._page = new _page.Page(this, browserContext);
|
||||
this.rawMouse.setPage(this._page);
|
||||
this._networkManager = new _ffNetworkManager.FFNetworkManager(session, this._page);
|
||||
this._page.on(_page.Page.Events.FrameDetached, frame => this._removeContextsForFrame(frame));
|
||||
// TODO: remove Page.willOpenNewWindowAsynchronously from the protocol.
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._session, 'Page.eventFired', this._onEventFired.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.frameAttached', this._onFrameAttached.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.frameDetached', this._onFrameDetached.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.navigationAborted', this._onNavigationAborted.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.navigationCommitted', this._onNavigationCommitted.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.navigationStarted', this._onNavigationStarted.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.sameDocumentNavigation', this._onSameDocumentNavigation.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Runtime.executionContextCreated', this._onExecutionContextCreated.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Runtime.executionContextDestroyed', this._onExecutionContextDestroyed.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.linkClicked', event => this._onLinkClicked(event.phase)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.uncaughtError', this._onUncaughtError.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Runtime.console', this._onConsole.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.dialogOpened', this._onDialogOpened.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.bindingCalled', this._onBindingCalled.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.fileChooserOpened', this._onFileChooserOpened.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.workerCreated', this._onWorkerCreated.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.workerDestroyed', this._onWorkerDestroyed.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.dispatchMessageFromWorker', this._onDispatchMessageFromWorker.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.crashed', this._onCrashed.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.videoRecordingStarted', this._onVideoRecordingStarted.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.webSocketCreated', this._onWebSocketCreated.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.webSocketClosed', this._onWebSocketClosed.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.webSocketFrameReceived', this._onWebSocketFrameReceived.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.webSocketFrameSent', this._onWebSocketFrameSent.bind(this)), _eventsHelper.eventsHelper.addEventListener(this._session, 'Page.screencastFrame', this._onScreencastFrame.bind(this))];
|
||||
this._session.once('Page.ready', async () => {
|
||||
await this._page.initOpener(this._opener);
|
||||
if (this._initializationFailed) return;
|
||||
// Note: it is important to call |reportAsNew| before resolving pageOrError promise,
|
||||
// so that anyone who awaits pageOrError got a ready and reported page.
|
||||
this._initializedPage = this._page;
|
||||
this._page.reportAsNew();
|
||||
this._pagePromise.resolve(this._page);
|
||||
});
|
||||
// Ideally, we somehow ensure that utility world is created before Page.ready arrives, but currently it is racy.
|
||||
// Therefore, we can end up with an initialized page without utility world, although very unlikely.
|
||||
this.addInitScript(new _page.InitScript('', true), UTILITY_WORLD_NAME).catch(e => this._markAsError(e));
|
||||
}
|
||||
potentiallyUninitializedPage() {
|
||||
return this._page;
|
||||
}
|
||||
async _markAsError(error) {
|
||||
// Same error may be report twice: channer disconnected and session.send fails.
|
||||
if (this._initializationFailed) return;
|
||||
this._initializationFailed = true;
|
||||
if (!this._initializedPage) {
|
||||
await this._page.initOpener(this._opener);
|
||||
this._page.reportAsNew(error);
|
||||
this._pagePromise.resolve(error);
|
||||
}
|
||||
}
|
||||
async pageOrError() {
|
||||
return this._pagePromise;
|
||||
}
|
||||
_onWebSocketCreated(event) {
|
||||
this._page._frameManager.onWebSocketCreated(webSocketId(event.frameId, event.wsid), event.requestURL);
|
||||
this._page._frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid));
|
||||
}
|
||||
_onWebSocketClosed(event) {
|
||||
if (event.error) this._page._frameManager.webSocketError(webSocketId(event.frameId, event.wsid), event.error);
|
||||
this._page._frameManager.webSocketClosed(webSocketId(event.frameId, event.wsid));
|
||||
}
|
||||
_onWebSocketFrameReceived(event) {
|
||||
this._page._frameManager.webSocketFrameReceived(webSocketId(event.frameId, event.wsid), event.opcode, event.data);
|
||||
}
|
||||
_onWebSocketFrameSent(event) {
|
||||
this._page._frameManager.onWebSocketFrameSent(webSocketId(event.frameId, event.wsid), event.opcode, event.data);
|
||||
}
|
||||
_onExecutionContextCreated(payload) {
|
||||
const {
|
||||
executionContextId,
|
||||
auxData
|
||||
} = payload;
|
||||
const frame = this._page._frameManager.frame(auxData.frameId);
|
||||
if (!frame) return;
|
||||
const delegate = new _ffExecutionContext.FFExecutionContext(this._session, executionContextId);
|
||||
let worldName = null;
|
||||
if (auxData.name === UTILITY_WORLD_NAME) worldName = 'utility';else if (!auxData.name) worldName = 'main';
|
||||
const context = new dom.FrameExecutionContext(delegate, frame, worldName);
|
||||
context[contextDelegateSymbol] = delegate;
|
||||
if (worldName) frame._contextCreated(worldName, context);
|
||||
this._contextIdToContext.set(executionContextId, context);
|
||||
}
|
||||
_onExecutionContextDestroyed(payload) {
|
||||
const {
|
||||
executionContextId
|
||||
} = payload;
|
||||
const context = this._contextIdToContext.get(executionContextId);
|
||||
if (!context) return;
|
||||
this._contextIdToContext.delete(executionContextId);
|
||||
context.frame._contextDestroyed(context);
|
||||
}
|
||||
_onExecutionContextsCleared() {
|
||||
for (const executionContextId of Array.from(this._contextIdToContext.keys())) this._onExecutionContextDestroyed({
|
||||
executionContextId
|
||||
});
|
||||
}
|
||||
_removeContextsForFrame(frame) {
|
||||
for (const [contextId, context] of this._contextIdToContext) {
|
||||
if (context.frame === frame) this._contextIdToContext.delete(contextId);
|
||||
}
|
||||
}
|
||||
_onLinkClicked(phase) {
|
||||
if (phase === 'before') this._page._frameManager.frameWillPotentiallyRequestNavigation();else this._page._frameManager.frameDidPotentiallyRequestNavigation();
|
||||
}
|
||||
_onNavigationStarted(params) {
|
||||
this._page._frameManager.frameRequestedNavigation(params.frameId, params.navigationId);
|
||||
}
|
||||
_onNavigationAborted(params) {
|
||||
this._page._frameManager.frameAbortedNavigation(params.frameId, params.errorText, params.navigationId);
|
||||
}
|
||||
_onNavigationCommitted(params) {
|
||||
for (const [workerId, worker] of this._workers) {
|
||||
if (worker.frameId === params.frameId) this._onWorkerDestroyed({
|
||||
workerId
|
||||
});
|
||||
}
|
||||
this._page._frameManager.frameCommittedNewDocumentNavigation(params.frameId, params.url, params.name || '', params.navigationId || '', false);
|
||||
}
|
||||
_onSameDocumentNavigation(params) {
|
||||
this._page._frameManager.frameCommittedSameDocumentNavigation(params.frameId, params.url);
|
||||
}
|
||||
_onFrameAttached(params) {
|
||||
this._page._frameManager.frameAttached(params.frameId, params.parentFrameId);
|
||||
}
|
||||
_onFrameDetached(params) {
|
||||
this._page._frameManager.frameDetached(params.frameId);
|
||||
}
|
||||
_onEventFired(payload) {
|
||||
const {
|
||||
frameId,
|
||||
name
|
||||
} = payload;
|
||||
if (name === 'load') this._page._frameManager.frameLifecycleEvent(frameId, 'load');
|
||||
if (name === 'DOMContentLoaded') this._page._frameManager.frameLifecycleEvent(frameId, 'domcontentloaded');
|
||||
}
|
||||
_onUncaughtError(params) {
|
||||
const {
|
||||
name,
|
||||
message
|
||||
} = (0, _stackTrace.splitErrorMessage)(params.message);
|
||||
const error = new Error(message);
|
||||
error.stack = params.message + '\n' + params.stack.split('\n').filter(Boolean).map(a => a.replace(/([^@]*)@(.*)/, ' at $1 ($2)')).join('\n');
|
||||
error.name = name;
|
||||
this._page.emitOnContextOnceInitialized(_browserContext.BrowserContext.Events.PageError, error, this._page);
|
||||
}
|
||||
_onConsole(payload) {
|
||||
const {
|
||||
type,
|
||||
args,
|
||||
executionContextId,
|
||||
location
|
||||
} = payload;
|
||||
const context = this._contextIdToContext.get(executionContextId);
|
||||
if (!context) return;
|
||||
// Juggler reports 'warn' for some internal messages generated by the browser.
|
||||
this._page._addConsoleMessage(type === 'warn' ? 'warning' : type, args.map(arg => context.createHandle(arg)), location);
|
||||
}
|
||||
_onDialogOpened(params) {
|
||||
this._page.emitOnContext(_browserContext.BrowserContext.Events.Dialog, new dialog.Dialog(this._page, params.type, params.message, async (accept, promptText) => {
|
||||
await this._session.sendMayFail('Page.handleDialog', {
|
||||
dialogId: params.dialogId,
|
||||
accept,
|
||||
promptText
|
||||
});
|
||||
}, params.defaultValue));
|
||||
}
|
||||
async _onBindingCalled(event) {
|
||||
const pageOrError = await this.pageOrError();
|
||||
if (!(pageOrError instanceof Error)) {
|
||||
const context = this._contextIdToContext.get(event.executionContextId);
|
||||
if (context) await this._page._onBindingCalled(event.payload, context);
|
||||
}
|
||||
}
|
||||
async _onFileChooserOpened(payload) {
|
||||
const {
|
||||
executionContextId,
|
||||
element
|
||||
} = payload;
|
||||
const context = this._contextIdToContext.get(executionContextId);
|
||||
if (!context) return;
|
||||
const handle = context.createHandle(element).asElement();
|
||||
await this._page._onFileChooserOpened(handle);
|
||||
}
|
||||
async _onWorkerCreated(event) {
|
||||
const workerId = event.workerId;
|
||||
const worker = new _page.Worker(this._page, event.url);
|
||||
const workerSession = new _ffConnection.FFSession(this._session._connection, workerId, message => {
|
||||
this._session.send('Page.sendMessageToWorker', {
|
||||
frameId: event.frameId,
|
||||
workerId: workerId,
|
||||
message: JSON.stringify(message)
|
||||
}).catch(e => {
|
||||
workerSession.dispatchMessage({
|
||||
id: message.id,
|
||||
method: '',
|
||||
params: {},
|
||||
error: {
|
||||
message: e.message,
|
||||
data: undefined
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
this._workers.set(workerId, {
|
||||
session: workerSession,
|
||||
frameId: event.frameId
|
||||
});
|
||||
this._page._addWorker(workerId, worker);
|
||||
workerSession.once('Runtime.executionContextCreated', event => {
|
||||
worker._createExecutionContext(new _ffExecutionContext.FFExecutionContext(workerSession, event.executionContextId));
|
||||
});
|
||||
workerSession.on('Runtime.console', event => {
|
||||
const {
|
||||
type,
|
||||
args,
|
||||
location
|
||||
} = event;
|
||||
const context = worker._existingExecutionContext;
|
||||
this._page._addConsoleMessage(type, args.map(arg => context.createHandle(arg)), location);
|
||||
});
|
||||
// Note: we receive worker exceptions directly from the page.
|
||||
}
|
||||
_onWorkerDestroyed(event) {
|
||||
const workerId = event.workerId;
|
||||
const worker = this._workers.get(workerId);
|
||||
if (!worker) return;
|
||||
worker.session.dispose();
|
||||
this._workers.delete(workerId);
|
||||
this._page._removeWorker(workerId);
|
||||
}
|
||||
async _onDispatchMessageFromWorker(event) {
|
||||
const worker = this._workers.get(event.workerId);
|
||||
if (!worker) return;
|
||||
worker.session.dispatchMessage(JSON.parse(event.message));
|
||||
}
|
||||
async _onCrashed(event) {
|
||||
this._session.markAsCrashed();
|
||||
this._page._didCrash();
|
||||
}
|
||||
_onVideoRecordingStarted(event) {
|
||||
this._browserContext._browser._videoStarted(this._browserContext, event.screencastId, event.file, this.pageOrError());
|
||||
}
|
||||
didClose() {
|
||||
this._markAsError(new _errors.TargetClosedError());
|
||||
this._session.dispose();
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
this._networkManager.dispose();
|
||||
this._page._didClose();
|
||||
}
|
||||
async navigateFrame(frame, url, referer) {
|
||||
const response = await this._session.send('Page.navigate', {
|
||||
url,
|
||||
referer,
|
||||
frameId: frame._id
|
||||
});
|
||||
return {
|
||||
newDocumentId: response.navigationId || undefined
|
||||
};
|
||||
}
|
||||
async updateExtraHTTPHeaders() {
|
||||
await this._session.send('Network.setExtraHTTPHeaders', {
|
||||
headers: this._page.extraHTTPHeaders() || []
|
||||
});
|
||||
}
|
||||
async updateEmulatedViewportSize() {
|
||||
const viewportSize = this._page.viewportSize();
|
||||
await this._session.send('Page.setViewportSize', {
|
||||
viewportSize
|
||||
});
|
||||
}
|
||||
async bringToFront() {
|
||||
await this._session.send('Page.bringToFront', {});
|
||||
}
|
||||
async updateEmulateMedia() {
|
||||
const emulatedMedia = this._page.emulatedMedia();
|
||||
const colorScheme = emulatedMedia.colorScheme === 'no-override' ? undefined : emulatedMedia.colorScheme;
|
||||
const reducedMotion = emulatedMedia.reducedMotion === 'no-override' ? undefined : emulatedMedia.reducedMotion;
|
||||
const forcedColors = emulatedMedia.forcedColors === 'no-override' ? undefined : emulatedMedia.forcedColors;
|
||||
await this._session.send('Page.setEmulatedMedia', {
|
||||
// Empty string means reset.
|
||||
type: emulatedMedia.media === 'no-override' ? '' : emulatedMedia.media,
|
||||
colorScheme,
|
||||
reducedMotion,
|
||||
forcedColors
|
||||
});
|
||||
}
|
||||
async updateRequestInterception() {
|
||||
await this._networkManager.setRequestInterception(this._page.needsRequestInterception());
|
||||
}
|
||||
async updateFileChooserInterception() {
|
||||
const enabled = this._page.fileChooserIntercepted();
|
||||
await this._session.send('Page.setInterceptFileChooserDialog', {
|
||||
enabled
|
||||
}).catch(() => {}); // target can be closed.
|
||||
}
|
||||
async reload() {
|
||||
await this._session.send('Page.reload');
|
||||
}
|
||||
async goBack() {
|
||||
const {
|
||||
success
|
||||
} = await this._session.send('Page.goBack', {
|
||||
frameId: this._page.mainFrame()._id
|
||||
});
|
||||
return success;
|
||||
}
|
||||
async goForward() {
|
||||
const {
|
||||
success
|
||||
} = await this._session.send('Page.goForward', {
|
||||
frameId: this._page.mainFrame()._id
|
||||
});
|
||||
return success;
|
||||
}
|
||||
async requestGC() {
|
||||
await this._session.send('Heap.collectGarbage');
|
||||
}
|
||||
async addInitScript(initScript, worldName) {
|
||||
this._initScripts.push({
|
||||
initScript,
|
||||
worldName
|
||||
});
|
||||
await this._session.send('Page.setInitScripts', {
|
||||
scripts: this._initScripts.map(s => ({
|
||||
script: s.initScript.source,
|
||||
worldName: s.worldName
|
||||
}))
|
||||
});
|
||||
}
|
||||
async removeNonInternalInitScripts() {
|
||||
this._initScripts = this._initScripts.filter(s => s.initScript.internal);
|
||||
await this._session.send('Page.setInitScripts', {
|
||||
scripts: this._initScripts.map(s => ({
|
||||
script: s.initScript.source,
|
||||
worldName: s.worldName
|
||||
}))
|
||||
});
|
||||
}
|
||||
async closePage(runBeforeUnload) {
|
||||
await this._session.send('Page.close', {
|
||||
runBeforeUnload
|
||||
});
|
||||
}
|
||||
async setBackgroundColor(color) {
|
||||
if (color) throw new Error('Not implemented');
|
||||
}
|
||||
async takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, scale) {
|
||||
if (!documentRect) {
|
||||
const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => ({
|
||||
x: window.scrollX,
|
||||
y: window.scrollY
|
||||
}));
|
||||
documentRect = {
|
||||
x: viewportRect.x + scrollOffset.x,
|
||||
y: viewportRect.y + scrollOffset.y,
|
||||
width: viewportRect.width,
|
||||
height: viewportRect.height
|
||||
};
|
||||
}
|
||||
progress.throwIfAborted();
|
||||
const {
|
||||
data
|
||||
} = await this._session.send('Page.screenshot', {
|
||||
mimeType: 'image/' + format,
|
||||
clip: documentRect,
|
||||
quality,
|
||||
omitDeviceScaleFactor: scale === 'css'
|
||||
});
|
||||
return Buffer.from(data, 'base64');
|
||||
}
|
||||
async getContentFrame(handle) {
|
||||
const {
|
||||
contentFrameId
|
||||
} = await this._session.send('Page.describeNode', {
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: handle._objectId
|
||||
});
|
||||
if (!contentFrameId) return null;
|
||||
return this._page._frameManager.frame(contentFrameId);
|
||||
}
|
||||
async getOwnerFrame(handle) {
|
||||
const {
|
||||
ownerFrameId
|
||||
} = await this._session.send('Page.describeNode', {
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: handle._objectId
|
||||
});
|
||||
return ownerFrameId || null;
|
||||
}
|
||||
isElementHandle(remoteObject) {
|
||||
return remoteObject.subtype === 'node';
|
||||
}
|
||||
async getBoundingBox(handle) {
|
||||
const quads = await this.getContentQuads(handle);
|
||||
if (!quads || !quads.length) return null;
|
||||
let minX = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let minY = Infinity;
|
||||
let maxY = -Infinity;
|
||||
for (const quad of quads) {
|
||||
for (const point of quad) {
|
||||
minX = Math.min(minX, point.x);
|
||||
maxX = Math.max(maxX, point.x);
|
||||
minY = Math.min(minY, point.y);
|
||||
maxY = Math.max(maxY, point.y);
|
||||
}
|
||||
}
|
||||
return {
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY
|
||||
};
|
||||
}
|
||||
async scrollRectIntoViewIfNeeded(handle, rect) {
|
||||
return await this._session.send('Page.scrollIntoViewIfNeeded', {
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: handle._objectId,
|
||||
rect
|
||||
}).then(() => 'done').catch(e => {
|
||||
if (e instanceof Error && e.message.includes('Node is detached from document')) return 'error:notconnected';
|
||||
if (e instanceof Error && e.message.includes('Node does not have a layout object')) return 'error:notvisible';
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
async setScreencastOptions(options) {
|
||||
if (options) {
|
||||
const {
|
||||
screencastId
|
||||
} = await this._session.send('Page.startScreencast', options);
|
||||
this._screencastId = screencastId;
|
||||
} else {
|
||||
await this._session.send('Page.stopScreencast');
|
||||
}
|
||||
}
|
||||
_onScreencastFrame(event) {
|
||||
if (!this._screencastId) return;
|
||||
const screencastId = this._screencastId;
|
||||
this._page.throttleScreencastFrameAck(() => {
|
||||
this._session.send('Page.screencastFrameAck', {
|
||||
screencastId
|
||||
}).catch(e => _debugLogger.debugLogger.log('error', e));
|
||||
});
|
||||
const buffer = Buffer.from(event.data, 'base64');
|
||||
this._page.emit(_page.Page.Events.ScreencastFrame, {
|
||||
buffer,
|
||||
width: event.deviceWidth,
|
||||
height: event.deviceHeight
|
||||
});
|
||||
}
|
||||
rafCountForStablePosition() {
|
||||
return 1;
|
||||
}
|
||||
async getContentQuads(handle) {
|
||||
const result = await this._session.sendMayFail('Page.getContentQuads', {
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: handle._objectId
|
||||
});
|
||||
if (!result) return null;
|
||||
return result.quads.map(quad => [quad.p1, quad.p2, quad.p3, quad.p4]);
|
||||
}
|
||||
async setInputFiles(handle, files) {
|
||||
await handle.evaluateInUtility(([injected, node, files]) => injected.setInputFiles(node, files), files);
|
||||
}
|
||||
async setInputFilePaths(handle, files) {
|
||||
await this._session.send('Page.setFileInputFiles', {
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: handle._objectId,
|
||||
files
|
||||
});
|
||||
}
|
||||
async adoptElementHandle(handle, to) {
|
||||
const result = await this._session.send('Page.adoptNode', {
|
||||
frameId: handle._context.frame._id,
|
||||
objectId: handle._objectId,
|
||||
executionContextId: to[contextDelegateSymbol]._executionContextId
|
||||
});
|
||||
if (!result.remoteObject) throw new Error(dom.kUnableToAdoptErrorMessage);
|
||||
return to.createHandle(result.remoteObject);
|
||||
}
|
||||
async getAccessibilityTree(needle) {
|
||||
return (0, _ffAccessibility.getAccessibilityTree)(this._session, needle);
|
||||
}
|
||||
async inputActionEpilogue() {}
|
||||
async resetForReuse() {
|
||||
// Firefox sometimes keeps the last mouse position in the page,
|
||||
// which affects things like hovered state.
|
||||
// See https://github.com/microsoft/playwright/issues/22432.
|
||||
// Move mouse to (-1, -1) to avoid anything being hovered.
|
||||
await this.rawMouse.move(-1, -1, 'none', new Set(), new Set(), false);
|
||||
}
|
||||
async getFrameElement(frame) {
|
||||
const parent = frame.parentFrame();
|
||||
if (!parent) throw new Error('Frame has been detached.');
|
||||
const context = await parent._mainContext();
|
||||
const result = await this._session.send('Page.adoptNode', {
|
||||
frameId: frame._id,
|
||||
executionContextId: context[contextDelegateSymbol]._executionContextId
|
||||
});
|
||||
if (!result.remoteObject) throw new Error('Frame has been detached.');
|
||||
return context.createHandle(result.remoteObject);
|
||||
}
|
||||
shouldToggleStyleSheetToSyncAnimations() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.FFPage = FFPage;
|
||||
function webSocketId(frameId, wsid) {
|
||||
return `${frameId}---${wsid}`;
|
||||
}
|
||||
const contextDelegateSymbol = Symbol('delegate');
|
||||
99
node_modules/playwright-core/lib/server/firefox/firefox.js
generated
vendored
Normal file
99
node_modules/playwright-core/lib/server/firefox/firefox.js
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Firefox = void 0;
|
||||
var os = _interopRequireWildcard(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _ffBrowser = require("./ffBrowser");
|
||||
var _ffConnection = require("./ffConnection");
|
||||
var _browserType = require("../browserType");
|
||||
var _utils = require("../../utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Firefox extends _browserType.BrowserType {
|
||||
constructor(parent) {
|
||||
super(parent, 'firefox');
|
||||
}
|
||||
connectToTransport(transport, options) {
|
||||
return _ffBrowser.FFBrowser.connect(this.attribution.playwright, transport, options);
|
||||
}
|
||||
doRewriteStartupLog(error) {
|
||||
if (!error.logs) return error;
|
||||
// https://github.com/microsoft/playwright/issues/6500
|
||||
if (error.logs.includes(`as root in a regular user's session is not supported.`)) error.logs = '\n' + (0, _utils.wrapInASCIIBox)(`Firefox is unable to launch if the $HOME folder isn't owned by the current user.\nWorkaround: Set the HOME=/root environment variable${process.env.GITHUB_ACTION ? ' in your GitHub Actions workflow file' : ''} when running Playwright.`, 1);
|
||||
if (error.logs.includes('no DISPLAY environment variable specified')) error.logs = '\n' + (0, _utils.wrapInASCIIBox)(_browserType.kNoXServerRunningError, 1);
|
||||
return error;
|
||||
}
|
||||
amendEnvironment(env, userDataDir, executable, browserArguments) {
|
||||
if (!_path.default.isAbsolute(os.homedir())) throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${os.platform() === 'win32' ? 'USERPROFILE' : 'HOME'} to a relative path?`);
|
||||
if (os.platform() === 'linux') {
|
||||
// Always remove SNAP_NAME and SNAP_INSTANCE_NAME env variables since they
|
||||
// confuse Firefox: in our case, builds never come from SNAP.
|
||||
// See https://github.com/microsoft/playwright/issues/20555
|
||||
return {
|
||||
...env,
|
||||
SNAP_NAME: undefined,
|
||||
SNAP_INSTANCE_NAME: undefined
|
||||
};
|
||||
}
|
||||
return env;
|
||||
}
|
||||
attemptToGracefullyCloseBrowser(transport) {
|
||||
const message = {
|
||||
method: 'Browser.close',
|
||||
params: {},
|
||||
id: _ffConnection.kBrowserCloseMessageId
|
||||
};
|
||||
transport.send(message);
|
||||
}
|
||||
defaultArgs(options, isPersistent, userDataDir) {
|
||||
const {
|
||||
args = [],
|
||||
headless
|
||||
} = options;
|
||||
const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
|
||||
if (userDataDirArg) throw this._createUserDataDirArgMisuseError('--profile');
|
||||
if (args.find(arg => arg.startsWith('-juggler'))) throw new Error('Use the port parameter instead of -juggler argument');
|
||||
const firefoxArguments = ['-no-remote'];
|
||||
if (headless) {
|
||||
firefoxArguments.push('-headless');
|
||||
} else {
|
||||
firefoxArguments.push('-wait-for-browser');
|
||||
firefoxArguments.push('-foreground');
|
||||
}
|
||||
firefoxArguments.push(`-profile`, userDataDir);
|
||||
firefoxArguments.push('-juggler-pipe');
|
||||
firefoxArguments.push(...args);
|
||||
if (isPersistent) firefoxArguments.push('about:blank');else firefoxArguments.push('-silent');
|
||||
return firefoxArguments;
|
||||
}
|
||||
readyState(options) {
|
||||
return new JugglerReadyState();
|
||||
}
|
||||
}
|
||||
exports.Firefox = Firefox;
|
||||
class JugglerReadyState extends _browserType.BrowserReadyState {
|
||||
onBrowserOutput(message) {
|
||||
if (message.includes('Juggler listening to the pipe')) this._wsEndpoint.resolve(undefined);
|
||||
}
|
||||
}
|
||||
75
node_modules/playwright-core/lib/server/formData.js
generated
vendored
Normal file
75
node_modules/playwright-core/lib/server/formData.js
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.MultipartFormData = void 0;
|
||||
var _utilsBundle = require("../utilsBundle");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class MultipartFormData {
|
||||
constructor() {
|
||||
this._boundary = void 0;
|
||||
this._chunks = [];
|
||||
this._boundary = generateUniqueBoundaryString();
|
||||
}
|
||||
contentTypeHeader() {
|
||||
return `multipart/form-data; boundary=${this._boundary}`;
|
||||
}
|
||||
addField(name, value) {
|
||||
this._beginMultiPartHeader(name);
|
||||
this._finishMultiPartHeader();
|
||||
this._chunks.push(Buffer.from(value));
|
||||
this._finishMultiPartField();
|
||||
}
|
||||
addFileField(name, value) {
|
||||
this._beginMultiPartHeader(name);
|
||||
this._chunks.push(Buffer.from(`; filename="${value.name}"`));
|
||||
this._chunks.push(Buffer.from(`\r\ncontent-type: ${value.mimeType || _utilsBundle.mime.getType(value.name) || 'application/octet-stream'}`));
|
||||
this._finishMultiPartHeader();
|
||||
this._chunks.push(value.buffer);
|
||||
this._finishMultiPartField();
|
||||
}
|
||||
finish() {
|
||||
this._addBoundary(true);
|
||||
return Buffer.concat(this._chunks);
|
||||
}
|
||||
_beginMultiPartHeader(name) {
|
||||
this._addBoundary();
|
||||
this._chunks.push(Buffer.from(`content-disposition: form-data; name="${name}"`));
|
||||
}
|
||||
_finishMultiPartHeader() {
|
||||
this._chunks.push(Buffer.from(`\r\n\r\n`));
|
||||
}
|
||||
_finishMultiPartField() {
|
||||
this._chunks.push(Buffer.from(`\r\n`));
|
||||
}
|
||||
_addBoundary(isLastBoundary) {
|
||||
this._chunks.push(Buffer.from('--' + this._boundary));
|
||||
if (isLastBoundary) this._chunks.push(Buffer.from('--'));
|
||||
this._chunks.push(Buffer.from('\r\n'));
|
||||
}
|
||||
}
|
||||
exports.MultipartFormData = MultipartFormData;
|
||||
const alphaNumericEncodingMap = [0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42];
|
||||
|
||||
// See generateUniqueBoundaryString() in WebKit
|
||||
function generateUniqueBoundaryString() {
|
||||
const charCodes = [];
|
||||
for (let i = 0; i < 16; i++) charCodes.push(alphaNumericEncodingMap[Math.floor(Math.random() * alphaNumericEncodingMap.length)]);
|
||||
return '----WebKitFormBoundary' + String.fromCharCode(...charCodes);
|
||||
}
|
||||
171
node_modules/playwright-core/lib/server/frameSelectors.js
generated
vendored
Normal file
171
node_modules/playwright-core/lib/server/frameSelectors.js
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FrameSelectors = void 0;
|
||||
var _selectorParser = require("../utils/isomorphic/selectorParser");
|
||||
var _utils = require("../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class FrameSelectors {
|
||||
constructor(frame) {
|
||||
this.frame = void 0;
|
||||
this.frame = frame;
|
||||
}
|
||||
_parseSelector(selector, options) {
|
||||
const strict = typeof (options === null || options === void 0 ? void 0 : options.strict) === 'boolean' ? options.strict : !!this.frame._page.context()._options.strictSelectors;
|
||||
return this.frame._page.context().selectors().parseSelector(selector, strict);
|
||||
}
|
||||
async query(selector, options, scope) {
|
||||
const resolved = await this.resolveInjectedForSelector(selector, options, scope);
|
||||
// Be careful, |this.frame| can be different from |resolved.frame|.
|
||||
if (!resolved) return null;
|
||||
const handle = await resolved.injected.evaluateHandle((injected, {
|
||||
info,
|
||||
scope
|
||||
}) => {
|
||||
return injected.querySelector(info.parsed, scope || document, info.strict);
|
||||
}, {
|
||||
info: resolved.info,
|
||||
scope: resolved.scope
|
||||
});
|
||||
const elementHandle = handle.asElement();
|
||||
if (!elementHandle) {
|
||||
handle.dispose();
|
||||
return null;
|
||||
}
|
||||
return adoptIfNeeded(elementHandle, await resolved.frame._mainContext());
|
||||
}
|
||||
async queryArrayInMainWorld(selector, scope) {
|
||||
const resolved = await this.resolveInjectedForSelector(selector, {
|
||||
mainWorld: true
|
||||
}, scope);
|
||||
// Be careful, |this.frame| can be different from |resolved.frame|.
|
||||
if (!resolved) throw new Error(`Failed to find frame for selector "${selector}"`);
|
||||
return await resolved.injected.evaluateHandle((injected, {
|
||||
info,
|
||||
scope
|
||||
}) => {
|
||||
return injected.querySelectorAll(info.parsed, scope || document);
|
||||
}, {
|
||||
info: resolved.info,
|
||||
scope: resolved.scope
|
||||
});
|
||||
}
|
||||
async queryCount(selector) {
|
||||
const resolved = await this.resolveInjectedForSelector(selector);
|
||||
// Be careful, |this.frame| can be different from |resolved.frame|.
|
||||
if (!resolved) throw new Error(`Failed to find frame for selector "${selector}"`);
|
||||
return await resolved.injected.evaluate((injected, {
|
||||
info
|
||||
}) => {
|
||||
return injected.querySelectorAll(info.parsed, document).length;
|
||||
}, {
|
||||
info: resolved.info
|
||||
});
|
||||
}
|
||||
async queryAll(selector, scope) {
|
||||
const resolved = await this.resolveInjectedForSelector(selector, {}, scope);
|
||||
// Be careful, |this.frame| can be different from |resolved.frame|.
|
||||
if (!resolved) return [];
|
||||
const arrayHandle = await resolved.injected.evaluateHandle((injected, {
|
||||
info,
|
||||
scope
|
||||
}) => {
|
||||
return injected.querySelectorAll(info.parsed, scope || document);
|
||||
}, {
|
||||
info: resolved.info,
|
||||
scope: resolved.scope
|
||||
});
|
||||
const properties = await arrayHandle.getProperties();
|
||||
arrayHandle.dispose();
|
||||
|
||||
// Note: adopting elements one by one may be slow. If we encounter the issue here,
|
||||
// we might introduce 'useMainContext' option or similar to speed things up.
|
||||
const targetContext = await resolved.frame._mainContext();
|
||||
const result = [];
|
||||
for (const property of properties.values()) {
|
||||
const elementHandle = property.asElement();
|
||||
if (elementHandle) result.push(adoptIfNeeded(elementHandle, targetContext));else property.dispose();
|
||||
}
|
||||
return Promise.all(result);
|
||||
}
|
||||
async resolveFrameForSelector(selector, options = {}, scope) {
|
||||
let frame = this.frame;
|
||||
const frameChunks = (0, _selectorParser.splitSelectorByFrame)(selector);
|
||||
for (const chunk of frameChunks) {
|
||||
(0, _selectorParser.visitAllSelectorParts)(chunk, (part, nested) => {
|
||||
if (nested && part.name === 'internal:control' && part.body === 'enter-frame') {
|
||||
const locator = (0, _utils.asLocator)(this.frame._page.attribution.playwright.options.sdkLanguage, selector);
|
||||
throw new _selectorParser.InvalidSelectorError(`Frame locators are not allowed inside composite locators, while querying "${locator}"`);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < frameChunks.length - 1; ++i) {
|
||||
const info = this._parseSelector(frameChunks[i], options);
|
||||
const context = await frame._context(info.world);
|
||||
const injectedScript = await context.injectedScript();
|
||||
const handle = await injectedScript.evaluateHandle((injected, {
|
||||
info,
|
||||
scope,
|
||||
selectorString
|
||||
}) => {
|
||||
const element = injected.querySelector(info.parsed, scope || document, info.strict);
|
||||
if (element && element.nodeName !== 'IFRAME' && element.nodeName !== 'FRAME') throw injected.createStacklessError(`Selector "${selectorString}" resolved to ${injected.previewNode(element)}, <iframe> was expected`);
|
||||
return element;
|
||||
}, {
|
||||
info,
|
||||
scope: i === 0 ? scope : undefined,
|
||||
selectorString: (0, _selectorParser.stringifySelector)(info.parsed)
|
||||
});
|
||||
const element = handle.asElement();
|
||||
if (!element) return null;
|
||||
const maybeFrame = await frame._page._delegate.getContentFrame(element);
|
||||
element.dispose();
|
||||
if (!maybeFrame) return null;
|
||||
frame = maybeFrame;
|
||||
}
|
||||
// If we end up in the different frame, we should start from the frame root, so throw away the scope.
|
||||
if (frame !== this.frame) scope = undefined;
|
||||
return {
|
||||
frame,
|
||||
info: frame.selectors._parseSelector(frameChunks[frameChunks.length - 1], options),
|
||||
scope
|
||||
};
|
||||
}
|
||||
async resolveInjectedForSelector(selector, options, scope) {
|
||||
const resolved = await this.resolveFrameForSelector(selector, options, scope);
|
||||
// Be careful, |this.frame| can be different from |resolved.frame|.
|
||||
if (!resolved) return;
|
||||
const context = await resolved.frame._context(options !== null && options !== void 0 && options.mainWorld ? 'main' : resolved.info.world);
|
||||
const injected = await context.injectedScript();
|
||||
return {
|
||||
injected,
|
||||
info: resolved.info,
|
||||
frame: resolved.frame,
|
||||
scope: resolved.scope
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.FrameSelectors = FrameSelectors;
|
||||
async function adoptIfNeeded(handle, context) {
|
||||
if (handle._context === context) return handle;
|
||||
const adopted = await handle._page._delegate.adoptElementHandle(handle, context);
|
||||
handle.dispose();
|
||||
return adopted;
|
||||
}
|
||||
1670
node_modules/playwright-core/lib/server/frames.js
generated
vendored
Normal file
1670
node_modules/playwright-core/lib/server/frames.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
139
node_modules/playwright-core/lib/server/har/harRecorder.js
generated
vendored
Normal file
139
node_modules/playwright-core/lib/server/har/harRecorder.js
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.HarRecorder = void 0;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _artifact = require("../artifact");
|
||||
var _harTracer = require("./harTracer");
|
||||
var _zipBundle = require("../../zipBundle");
|
||||
var _manualPromise = require("../../utils/manualPromise");
|
||||
var _utils = require("../../utils");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class HarRecorder {
|
||||
constructor(context, page, options) {
|
||||
this._artifact = void 0;
|
||||
this._isFlushed = false;
|
||||
this._tracer = void 0;
|
||||
this._entries = [];
|
||||
this._zipFile = null;
|
||||
this._writtenZipEntries = new Set();
|
||||
this._artifact = new _artifact.Artifact(context, _path.default.join(context._browser.options.artifactsDir, `${(0, _utils.createGuid)()}.har`));
|
||||
const urlFilterRe = options.urlRegexSource !== undefined && options.urlRegexFlags !== undefined ? new RegExp(options.urlRegexSource, options.urlRegexFlags) : undefined;
|
||||
const expectsZip = options.path.endsWith('.zip');
|
||||
const content = options.content || (expectsZip ? 'attach' : 'embed');
|
||||
this._tracer = new _harTracer.HarTracer(context, page, this, {
|
||||
content,
|
||||
slimMode: options.mode === 'minimal',
|
||||
includeTraceInfo: false,
|
||||
recordRequestOverrides: true,
|
||||
waitForContentOnStop: true,
|
||||
urlFilter: urlFilterRe !== null && urlFilterRe !== void 0 ? urlFilterRe : options.urlGlob
|
||||
});
|
||||
this._zipFile = content === 'attach' || expectsZip ? new _zipBundle.yazl.ZipFile() : null;
|
||||
this._tracer.start({
|
||||
omitScripts: false
|
||||
});
|
||||
}
|
||||
onEntryStarted(entry) {
|
||||
this._entries.push(entry);
|
||||
}
|
||||
onEntryFinished(entry) {}
|
||||
onContentBlob(sha1, buffer) {
|
||||
if (!this._zipFile || this._writtenZipEntries.has(sha1)) return;
|
||||
this._writtenZipEntries.add(sha1);
|
||||
this._zipFile.addBuffer(buffer, sha1);
|
||||
}
|
||||
async flush() {
|
||||
if (this._isFlushed) return;
|
||||
this._isFlushed = true;
|
||||
await this._tracer.flush();
|
||||
const log = this._tracer.stop();
|
||||
log.entries = this._entries;
|
||||
const harFileContent = jsonStringify({
|
||||
log
|
||||
});
|
||||
if (this._zipFile) {
|
||||
const result = new _manualPromise.ManualPromise();
|
||||
this._zipFile.on('error', error => result.reject(error));
|
||||
this._zipFile.addBuffer(Buffer.from(harFileContent, 'utf-8'), 'har.har');
|
||||
this._zipFile.end();
|
||||
this._zipFile.outputStream.pipe(_fs.default.createWriteStream(this._artifact.localPath())).on('close', () => {
|
||||
result.resolve();
|
||||
});
|
||||
await result;
|
||||
} else {
|
||||
await _fs.default.promises.writeFile(this._artifact.localPath(), harFileContent);
|
||||
}
|
||||
}
|
||||
async export() {
|
||||
await this.flush();
|
||||
this._artifact.reportFinished();
|
||||
return this._artifact;
|
||||
}
|
||||
}
|
||||
exports.HarRecorder = HarRecorder;
|
||||
function jsonStringify(object) {
|
||||
const tokens = [];
|
||||
innerJsonStringify(object, tokens, '', false, undefined);
|
||||
return tokens.join('');
|
||||
}
|
||||
function innerJsonStringify(object, tokens, indent, flat, parentKey) {
|
||||
if (typeof object !== 'object' || object === null) {
|
||||
tokens.push(JSON.stringify(object));
|
||||
return;
|
||||
}
|
||||
const isArray = Array.isArray(object);
|
||||
if (!isArray && object.constructor.name !== 'Object') {
|
||||
tokens.push(JSON.stringify(object));
|
||||
return;
|
||||
}
|
||||
const entries = isArray ? object : Object.entries(object).filter(e => e[1] !== undefined);
|
||||
if (!entries.length) {
|
||||
tokens.push(isArray ? `[]` : `{}`);
|
||||
return;
|
||||
}
|
||||
const childIndent = `${indent} `;
|
||||
let brackets;
|
||||
if (isArray) brackets = flat ? {
|
||||
open: '[',
|
||||
close: ']'
|
||||
} : {
|
||||
open: `[\n${childIndent}`,
|
||||
close: `\n${indent}]`
|
||||
};else brackets = flat ? {
|
||||
open: '{ ',
|
||||
close: ' }'
|
||||
} : {
|
||||
open: `{\n${childIndent}`,
|
||||
close: `\n${indent}}`
|
||||
};
|
||||
tokens.push(brackets.open);
|
||||
for (let i = 0; i < entries.length; ++i) {
|
||||
const entry = entries[i];
|
||||
if (i) tokens.push(flat ? `, ` : `,\n${childIndent}`);
|
||||
if (!isArray) tokens.push(`${JSON.stringify(entry[0])}: `);
|
||||
const key = isArray ? undefined : entry[0];
|
||||
const flatten = flat || key === 'timings' || parentKey === 'headers';
|
||||
innerJsonStringify(isArray ? entry : entry[1], tokens, childIndent, flatten, key);
|
||||
}
|
||||
tokens.push(brackets.close);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user