"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("../processLauncher");

var _browserContext = require("../browserContext");

var _progress = require("../progress");

var _helper = require("../helper");

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)({
        executablePath: 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: {
          sdkLanguage: options.sdkLanguage,
          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 = [_helper.helper.addEventListener(rl, 'line', onLine), _helper.helper.addEventListener(rl, 'close', reject.bind(null, failError)), _helper.helper.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.
    _helper.helper.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() {
      _helper.helper.removeEventListeners(listeners);
    }
  });
}
//# sourceMappingURL=electron.js.map