"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Connection = void 0;

var _browser = require("./browser");

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

var _browserType = require("./browserType");

var _channelOwner = require("./channelOwner");

var _elementHandle = require("./elementHandle");

var _frame = require("./frame");

var _jsHandle = require("./jsHandle");

var _network = require("./network");

var _page = require("./page");

var _worker = require("./worker");

var _consoleMessage = require("./consoleMessage");

var _dialog = require("./dialog");

var _serializers = require("../protocol/serializers");

var _cdpSession = require("./cdpSession");

var _playwright = require("./playwright");

var _electron = require("./electron");

var _stream = require("./stream");

var _debugLogger = require("../utils/debugLogger");

var _selectors = require("./selectors");

var _utils = require("../utils/utils");

var _android = require("./android");

var _socksSocket = require("./socksSocket");

var _stackTrace = require("../utils/stackTrace");

var _artifact = require("./artifact");

var _events = require("events");

/**
 * 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 Root extends _channelOwner.ChannelOwner {
  constructor(connection) {
    super(connection, '', '', {});
  }

}

class Connection extends _events.EventEmitter {
  constructor(onClose) {
    super();
    this._objects = new Map();
    this._waitingForObject = new Map();

    this.onmessage = message => {};

    this._lastId = 0;
    this._callbacks = new Map();
    this._rootObject = void 0;
    this._disconnectedErrorMessage = void 0;
    this._onClose = void 0;
    this._rootObject = new Root(this);
    this._onClose = onClose;
  }

  async waitForObjectWithKnownName(guid) {
    if (this._objects.has(guid)) return this._objects.get(guid);
    return new Promise(f => this._waitingForObject.set(guid, f));
  }

  pendingProtocolCalls() {
    return Array.from(this._callbacks.values()).map(callback => callback.metadata);
  }

  getObjectWithKnownName(guid) {
    return this._objects.get(guid);
  }

  async sendMessageToServer(object, method, params, apiName) {
    const guid = object._guid;
    const {
      stack,
      frames
    } = (0, _stackTrace.captureStackTrace)();
    const id = ++this._lastId;
    const converted = {
      id,
      guid,
      method,
      params
    }; // Do not include metadata in debug logs to avoid noise.

    _debugLogger.debugLogger.log('channel:command', converted);

    const metadata = {
      stack: frames,
      apiName
    };
    this.onmessage({ ...converted,
      metadata
    });

    try {
      if (this._disconnectedErrorMessage) throw new Error(this._disconnectedErrorMessage);
      return await new Promise((resolve, reject) => this._callbacks.set(id, {
        resolve,
        reject,
        metadata
      }));
    } catch (e) {
      const innerStack = (process.env.PWDEBUGIMPL || (0, _utils.isUnderTest)()) && e.stack ? e.stack.substring(e.stack.indexOf(e.message) + e.message.length) : '';
      e.stack = e.message + innerStack + '\n' + stack;
      throw e;
    }
  }

  _debugScopeState() {
    return this._rootObject._debugScopeState();
  }

  dispatch(message) {
    const {
      id,
      guid,
      method,
      params,
      result,
      error
    } = message;

    if (id) {
      _debugLogger.debugLogger.log('channel:response', message);

      const callback = this._callbacks.get(id);

      if (!callback) throw new Error(`Cannot find command to respond: ${id}`);

      this._callbacks.delete(id);

      if (error) callback.reject((0, _serializers.parseError)(error));else callback.resolve(this._replaceGuidsWithChannels(result));
      return;
    }

    _debugLogger.debugLogger.log('channel:event', message);

    if (method === '__create__') {
      this._createRemoteObject(guid, params.type, params.guid, params.initializer);

      return;
    }

    if (method === '__dispose__') {
      const object = this._objects.get(guid);

      if (!object) throw new Error(`Cannot find object to dispose: ${guid}`);

      object._dispose();

      return;
    }

    const object = this._objects.get(guid);

    if (!object) throw new Error(`Cannot find object to emit "${method}": ${guid}`);

    object._channel.emit(method, this._replaceGuidsWithChannels(params));
  }

  close() {
    if (this._onClose) this._onClose();
  }

  didDisconnect(errorMessage) {
    this._disconnectedErrorMessage = errorMessage;

    for (const callback of this._callbacks.values()) callback.reject(new Error(errorMessage));

    this._callbacks.clear();

    this.emit('disconnect');
  }

  isDisconnected() {
    return !!this._disconnectedErrorMessage;
  }

  _replaceGuidsWithChannels(payload) {
    if (!payload) return payload;
    if (Array.isArray(payload)) return payload.map(p => this._replaceGuidsWithChannels(p));
    if (payload.guid && this._objects.has(payload.guid)) return this._objects.get(payload.guid)._channel;

    if (typeof payload === 'object') {
      const result = {};

      for (const key of Object.keys(payload)) result[key] = this._replaceGuidsWithChannels(payload[key]);

      return result;
    }

    return payload;
  }

  _createRemoteObject(parentGuid, type, guid, initializer) {
    const parent = this._objects.get(parentGuid);

    if (!parent) throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`);
    let result;
    initializer = this._replaceGuidsWithChannels(initializer);

    switch (type) {
      case 'Android':
        result = new _android.Android(parent, type, guid, initializer);
        break;

      case 'AndroidSocket':
        result = new _android.AndroidSocket(parent, type, guid, initializer);
        break;

      case 'AndroidDevice':
        result = new _android.AndroidDevice(parent, type, guid, initializer);
        break;

      case 'Artifact':
        result = new _artifact.Artifact(parent, type, guid, initializer);
        break;

      case 'BindingCall':
        result = new _page.BindingCall(parent, type, guid, initializer);
        break;

      case 'Browser':
        result = new _browser.Browser(parent, type, guid, initializer);
        break;

      case 'BrowserContext':
        result = new _browserContext.BrowserContext(parent, type, guid, initializer);
        break;

      case 'BrowserType':
        result = new _browserType.BrowserType(parent, type, guid, initializer);
        break;

      case 'CDPSession':
        result = new _cdpSession.CDPSession(parent, type, guid, initializer);
        break;

      case 'ConsoleMessage':
        result = new _consoleMessage.ConsoleMessage(parent, type, guid, initializer);
        break;

      case 'Dialog':
        result = new _dialog.Dialog(parent, type, guid, initializer);
        break;

      case 'Electron':
        result = new _electron.Electron(parent, type, guid, initializer);
        break;

      case 'ElectronApplication':
        result = new _electron.ElectronApplication(parent, type, guid, initializer);
        break;

      case 'ElementHandle':
        result = new _elementHandle.ElementHandle(parent, type, guid, initializer);
        break;

      case 'Frame':
        result = new _frame.Frame(parent, type, guid, initializer);
        break;

      case 'JSHandle':
        result = new _jsHandle.JSHandle(parent, type, guid, initializer);
        break;

      case 'Page':
        result = new _page.Page(parent, type, guid, initializer);
        break;

      case 'Playwright':
        result = new _playwright.Playwright(parent, type, guid, initializer);
        break;

      case 'Request':
        result = new _network.Request(parent, type, guid, initializer);
        break;

      case 'Response':
        result = new _network.Response(parent, type, guid, initializer);
        break;

      case 'Route':
        result = new _network.Route(parent, type, guid, initializer);
        break;

      case 'Stream':
        result = new _stream.Stream(parent, type, guid, initializer);
        break;

      case 'Selectors':
        result = new _selectors.SelectorsOwner(parent, type, guid, initializer);
        break;

      case 'WebSocket':
        result = new _network.WebSocket(parent, type, guid, initializer);
        break;

      case 'Worker':
        result = new _worker.Worker(parent, type, guid, initializer);
        break;

      case 'SocksSocket':
        result = new _socksSocket.SocksSocket(parent, type, guid, initializer);
        break;

      default:
        throw new Error('Missing type ' + type);
    }

    const callback = this._waitingForObject.get(guid);

    if (callback) {
      callback(result);

      this._waitingForObject.delete(guid);
    }

    return result;
  }

}

exports.Connection = Connection;
//# sourceMappingURL=connection.js.map