DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (07314de37ce5)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

var EXPORTED_SYMBOLS = ["Connection"];

const {Log} = ChromeUtils.import("chrome://remote/content/Log.jsm");
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyGetter(this, "log", Log.get);
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGen", "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");

class Connection {
  /**
   * @param WebSocketDebuggerTransport transport
   * @param httpd.js's Connection httpdConnection
   */
  constructor(transport, httpdConnection) {
    this.id = UUIDGen.generateUUID().toString();
    this.transport = transport;
    this.httpdConnection = httpdConnection;

    this.transport.hooks = this;
    this.transport.ready();

    this.defaultSession = null;
    this.sessions = new Map();
  }

  /**
   * Register a new Session to forward the messages to.
   * Session without any `id` attribute will be considered to be the
   * default one, to which messages without `sessionId` attribute are
   * forwarded to. Only one such session can be registered.
   *
   * @param Session session
   */
  registerSession(session) {
    if (!session.id) {
      if (this.defaultSession) {
        throw new Error("Default session is already set on Connection," +
                        "can't register another one.");
      }
      this.defaultSession = session;
    }
    this.sessions.set(session.id, session);
  }

  send(message) {
    log.trace(`<-(connection ${this.id}) ${JSON.stringify(message)}`);
    this.transport.send(message);
  }

  error(id, e) {
    const error = {
      message: e.message,
      data: e.stack,
    };
    this.send({id, error});
  }

  deserialize(data) {
    const id = data.id;
    const method = data.method;
    const params = data.params || {};
    return {id, method, params};
  }

  // transport hooks

  onPacket(packet) {
    log.trace(`(connection ${this.id})-> ${JSON.stringify(packet)}`);

    let message = {id: null};
    try {
      message = this.deserialize(packet);
      const { sessionId } = packet;
      if (!sessionId) {
        if (!this.defaultSession) {
          throw new Error(`Connection is missing a default Session.`);
        }
        this.defaultSession.onMessage(message);
      } else {
        const session = this.sessions.get(sessionId);
        if (!session) {
          throw new Error(`Session '${sessionId}' doesn't exists.`);
        }
        session.onMessage(message);
      }
    } catch (e) {
      log.warn(e);
      this.error(message.id, e);
    }
  }

  close() {
    this.transport.close();
    this.sessions.clear();

    // In addition to the WebSocket transport, we also have to close the Connection
    // used internaly within httpd.js. Otherwise the server doesn't shut down correctly
    // and keep these Connection instances alive.
    this.httpdConnection.close();
  }

  onClosed(status) {}

  toString() {
    return `[object Connection ${this.id}]`;
  }
}