252 lines
10 KiB
JavaScript
252 lines
10 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.Electron = exports.ElectronApplication = 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("../../utils/timeoutSettings");
|
|
|
|
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");
|
|
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
|
|
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) {
|
|
super(parent, 'electron-app');
|
|
this._browserContext = void 0;
|
|
this._nodeConnection = void 0;
|
|
this._nodeSession = void 0;
|
|
this._nodeExecutionContext = void 0;
|
|
this._nodeElectronHandlePromise = void 0;
|
|
this._lastWindowId = 0;
|
|
this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
|
|
this._browserContext = browser._defaultContext;
|
|
|
|
this._browserContext.on(_browserContext.BrowserContext.Events.Close, () => {
|
|
// Emit application closed after context closed.
|
|
Promise.resolve().then(() => this.emit(ElectronApplication.Events.Close));
|
|
});
|
|
|
|
for (const page of this._browserContext.pages()) this._onPage(page);
|
|
|
|
this._browserContext.on(_browserContext.BrowserContext.Events.Page, event => this._onPage(event));
|
|
|
|
this._nodeConnection = nodeConnection;
|
|
this._nodeSession = nodeConnection.rootSession;
|
|
this._nodeElectronHandlePromise = new Promise(f => {
|
|
this._nodeSession.on('Runtime.executionContextCreated', async event => {
|
|
if (event.context.auxData && event.context.auxData.isDefault) {
|
|
this._nodeExecutionContext = new js.ExecutionContext(this, new _crExecutionContext.CRExecutionContext(this._nodeSession, event.context));
|
|
f(await js.evaluate(this._nodeExecutionContext, false
|
|
/* returnByValue */
|
|
, `process.mainModule.require('electron')`));
|
|
}
|
|
});
|
|
});
|
|
|
|
this._nodeSession.send('Runtime.enable', {}).catch(e => {});
|
|
}
|
|
|
|
_onPage(page) {
|
|
// Needs to be sync.
|
|
const windowId = ++this._lastWindowId;
|
|
page._browserWindowId = windowId;
|
|
}
|
|
|
|
context() {
|
|
return this._browserContext;
|
|
}
|
|
|
|
async close() {
|
|
const progressController = new _progress.ProgressController((0, _instrumentation.internalCallMetadata)(), this);
|
|
const closed = progressController.run(progress => _helper.helper.waitForEvent(progress, this, ElectronApplication.Events.Close).promise, this._timeoutSettings.timeout({}));
|
|
const electronHandle = await this._nodeElectronHandlePromise;
|
|
await electronHandle.evaluate(({
|
|
app
|
|
}) => app.quit());
|
|
|
|
this._nodeConnection.close();
|
|
|
|
await closed;
|
|
}
|
|
|
|
async browserWindow(page) {
|
|
const electronHandle = await this._nodeElectronHandlePromise;
|
|
return await electronHandle.evaluateHandle(({
|
|
BrowserWindow
|
|
}, windowId) => BrowserWindow.fromId(windowId), page._browserWindowId);
|
|
}
|
|
|
|
}
|
|
|
|
exports.ElectronApplication = ElectronApplication;
|
|
ElectronApplication.Events = {
|
|
Close: 'close'
|
|
};
|
|
|
|
class Electron extends _instrumentation.SdkObject {
|
|
constructor(playwrightOptions) {
|
|
super(playwrightOptions.rootSdkObject, 'electron');
|
|
this._playwrightOptions = void 0;
|
|
this._playwrightOptions = playwrightOptions;
|
|
}
|
|
|
|
async launch(options) {
|
|
const {
|
|
args = []
|
|
} = options;
|
|
const controller = new _progress.ProgressController((0, _instrumentation.internalCallMetadata)(), this);
|
|
controller.setLogName('browser');
|
|
return controller.run(async progress => {
|
|
let app = undefined;
|
|
const 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.push('--no-sandbox');
|
|
}
|
|
|
|
const artifactsDir = await _fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
|
|
const browserLogsCollector = new _debugLogger.RecentLogsCollector();
|
|
const {
|
|
launchedProcess,
|
|
gracefullyClose,
|
|
kill
|
|
} = await (0, _processLauncher.launchProcess)({
|
|
command: options.executablePath || require('electron/index.js'),
|
|
args: electronArguments,
|
|
env: options.env ? (0, _processLauncher.envArrayToObject)(options.env) : process.env,
|
|
log: message => {
|
|
progress.log(message);
|
|
browserLogsCollector.log(message);
|
|
},
|
|
stdio: 'pipe',
|
|
cwd: options.cwd,
|
|
tempDirectories: [artifactsDir],
|
|
attemptToGracefullyClose: () => app.close(),
|
|
handleSIGINT: true,
|
|
handleSIGTERM: true,
|
|
handleSIGHUP: true,
|
|
onExit: () => {}
|
|
});
|
|
const nodeMatch = await waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
|
|
const nodeTransport = await _transport.WebSocketTransport.connect(progress, nodeMatch[1]);
|
|
const nodeConnection = new _crConnection.CRConnection(nodeTransport, _helper.helper.debugProtocolLogger(), browserLogsCollector);
|
|
const chromeMatch = await waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
|
|
const chromeTransport = await _transport.WebSocketTransport.connect(progress, chromeMatch[1]);
|
|
const browserProcess = {
|
|
onclose: undefined,
|
|
process: launchedProcess,
|
|
close: gracefullyClose,
|
|
kill
|
|
};
|
|
const browserOptions = { ...this._playwrightOptions,
|
|
name: 'electron',
|
|
isChromium: true,
|
|
headful: true,
|
|
persistent: {
|
|
noDefaultViewport: true,
|
|
acceptDownloads: options.acceptDownloads,
|
|
bypassCSP: options.bypassCSP,
|
|
colorScheme: options.colorScheme,
|
|
extraHTTPHeaders: options.extraHTTPHeaders,
|
|
geolocation: options.geolocation,
|
|
httpCredentials: options.httpCredentials,
|
|
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
|
|
locale: options.locale,
|
|
offline: options.offline,
|
|
recordHar: options.recordHar,
|
|
recordVideo: options.recordVideo,
|
|
timezoneId: options.timezoneId
|
|
},
|
|
browserProcess,
|
|
protocolLogger: _helper.helper.debugProtocolLogger(),
|
|
browserLogsCollector,
|
|
artifactsDir,
|
|
downloadsPath: artifactsDir,
|
|
tracesDir: artifactsDir
|
|
};
|
|
const browser = await _crBrowser.CRBrowser.connect(chromeTransport, browserOptions);
|
|
app = new ElectronApplication(this, browser, nodeConnection);
|
|
return app;
|
|
}, _timeoutSettings.TimeoutSettings.timeout(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);
|
|
}
|
|
});
|
|
} |