Bug 1278357 - Clean up code style in transport.js. r=tromey

MozReview-Commit-ID: 3uFu6AhOfbg
This commit is contained in:
J. Ryan Stinnett 2016-06-06 15:30:43 -05:00
Родитель 9d0ac66004
Коммит e1d147bbfc
2 изменённых файлов: 337 добавлений и 335 удалений

Просмотреть файл

@ -133,6 +133,7 @@ devtools/shared/tests/**
!devtools/shared/tests/unit/test_csslexer.js !devtools/shared/tests/unit/test_csslexer.js
devtools/shared/touch/** devtools/shared/touch/**
devtools/shared/transport/** devtools/shared/transport/**
!devtools/shared/transport/transport.js
devtools/shared/webconsole/test/** devtools/shared/webconsole/test/**
devtools/shared/worker/** devtools/shared/worker/**

Просмотреть файл

@ -1,29 +1,28 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* global Pipe, ScriptableInputStream, uneval */
// TODO: Get rid of this code once the marionette server loads transport.js as // TODO: Get rid of this code once the marionette server loads transport.js as
// an SDK module (see bug 1000814) // an SDK module (see bug 1000814)
(function (factory) { // Module boilerplate (function (factory) {
if (this.module && module.id.indexOf("transport") >= 0) { // require if (this.module && module.id.indexOf("transport") >= 0) {
// require
factory.call(this, require, exports); factory.call(this, require, exports);
} else { // loadSubScript } else if (this.require) {
if (this.require) { // loadSubScript
factory.call(this, require, this); factory.call(this, require, this);
} else { } else {
const Cu = Components.utils; // Cu.import
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); const Cu = Components.utils;
factory.call(this, require, this); const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
} factory.call(this, require, this);
} }
}).call(this, function (require, exports) { }).call(this, function (require, exports) {
const { Cc, Ci, Cr, CC } = require("chrome");
"use strict";
const { Cc, Ci, Cr, Cu, CC } = require("chrome");
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils"); const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { dumpn, dumpv } = DevToolsUtils; const { dumpn, dumpv } = DevToolsUtils;
const StreamUtils = require("devtools/shared/transport/stream-utils"); const StreamUtils = require("devtools/shared/transport/stream-utils");
@ -43,63 +42,63 @@
const PACKET_HEADER_MAX = 200; const PACKET_HEADER_MAX = 200;
/** /**
* An adapter that handles data transfers between the debugger client and * An adapter that handles data transfers between the debugger client and
* server. It can work with both nsIPipe and nsIServerSocket transports so * server. It can work with both nsIPipe and nsIServerSocket transports so
* long as the properly created input and output streams are specified. * long as the properly created input and output streams are specified.
* (However, for intra-process connections, LocalDebuggerTransport, below, * (However, for intra-process connections, LocalDebuggerTransport, below,
* is more efficient than using an nsIPipe pair with DebuggerTransport.) * is more efficient than using an nsIPipe pair with DebuggerTransport.)
* *
* @param input nsIAsyncInputStream * @param input nsIAsyncInputStream
* The input stream. * The input stream.
* @param output nsIAsyncOutputStream * @param output nsIAsyncOutputStream
* The output stream. * The output stream.
* *
* Given a DebuggerTransport instance dt: * Given a DebuggerTransport instance dt:
* 1) Set dt.hooks to a packet handler object (described below). * 1) Set dt.hooks to a packet handler object (described below).
* 2) Call dt.ready() to begin watching for input packets. * 2) Call dt.ready() to begin watching for input packets.
* 3) Call dt.send() / dt.startBulkSend() to send packets. * 3) Call dt.send() / dt.startBulkSend() to send packets.
* 4) Call dt.close() to close the connection, and disengage from the event * 4) Call dt.close() to close the connection, and disengage from the event
* loop. * loop.
* *
* A packet handler is an object with the following methods: * A packet handler is an object with the following methods:
* *
* - onPacket(packet) - called when we have received a complete packet. * - onPacket(packet) - called when we have received a complete packet.
* |packet| is the parsed form of the packet --- a JavaScript value, not * |packet| is the parsed form of the packet --- a JavaScript value, not
* a JSON-syntax string. * a JSON-syntax string.
* *
* - onBulkPacket(packet) - called when we have switched to bulk packet * - onBulkPacket(packet) - called when we have switched to bulk packet
* receiving mode. |packet| is an object containing: * receiving mode. |packet| is an object containing:
* * actor: Name of actor that will receive the packet * * actor: Name of actor that will receive the packet
* * type: Name of actor's method that should be called on receipt * * type: Name of actor's method that should be called on receipt
* * length: Size of the data to be read * * length: Size of the data to be read
* * stream: This input stream should only be used directly if you can ensure * * stream: This input stream should only be used directly if you can ensure
* that you will read exactly |length| bytes and will not close the * that you will read exactly |length| bytes and will not close the
* stream when reading is complete * stream when reading is complete
* * done: If you use the stream directly (instead of |copyTo| below), you * * done: If you use the stream directly (instead of |copyTo| below), you
* must signal completion by resolving / rejecting this deferred. * must signal completion by resolving / rejecting this deferred.
* If it's rejected, the transport will be closed. If an Error is * If it's rejected, the transport will be closed. If an Error is
* supplied as a rejection value, it will be logged via |dumpn|. * supplied as a rejection value, it will be logged via |dumpn|.
* If you do use |copyTo|, resolving is taken care of for you when * If you do use |copyTo|, resolving is taken care of for you when
* copying completes. * copying completes.
* * copyTo: A helper function for getting your data out of the stream that * * copyTo: A helper function for getting your data out of the stream that
* meets the stream handling requirements above, and has the * meets the stream handling requirements above, and has the
* following signature: * following signature:
* @param output nsIAsyncOutputStream * @param output nsIAsyncOutputStream
* The stream to copy to. * The stream to copy to.
* @return Promise * @return Promise
* The promise is resolved when copying completes or rejected if any * The promise is resolved when copying completes or rejected if any
* (unexpected) errors occur. * (unexpected) errors occur.
* This object also emits "progress" events for each chunk that is * This object also emits "progress" events for each chunk that is
* copied. See stream-utils.js. * copied. See stream-utils.js.
* *
* - onClosed(reason) - called when the connection is closed. |reason| is * - onClosed(reason) - called when the connection is closed. |reason| is
* an optional nsresult or object, typically passed when the transport is * an optional nsresult or object, typically passed when the transport is
* closed due to some error in a underlying stream. * closed due to some error in a underlying stream.
* *
* See ./packets.js and the Remote Debugging Protocol specification for more * See ./packets.js and the Remote Debugging Protocol specification for more
* details on the format of these packets. * details on the format of these packets.
*/ */
function DebuggerTransport(input, output) { function DebuggerTransport(input, output) {
EventEmitter.decorate(this); EventEmitter.decorate(this);
@ -107,12 +106,12 @@
this._scriptableInput = new ScriptableInputStream(input); this._scriptableInput = new ScriptableInputStream(input);
this._output = output; this._output = output;
// The current incoming (possibly partial) header, which will determine which // The current incoming (possibly partial) header, which will determine which
// type of Packet |_incoming| below will become. // type of Packet |_incoming| below will become.
this._incomingHeader = ""; this._incomingHeader = "";
// The current incoming Packet object // The current incoming Packet object
this._incoming = null; this._incoming = null;
// A queue of outgoing Packet objects // A queue of outgoing Packet objects
this._outgoing = []; this._outgoing = [];
this.hooks = null; this.hooks = null;
@ -125,14 +124,14 @@
} }
DebuggerTransport.prototype = { DebuggerTransport.prototype = {
/** /**
* Transmit an object as a JSON packet. * Transmit an object as a JSON packet.
* *
* This method returns immediately, without waiting for the entire * This method returns immediately, without waiting for the entire
* packet to be transmitted, registering event handlers as needed to * packet to be transmitted, registering event handlers as needed to
* transmit the entire packet. Packets are transmitted in the order * transmit the entire packet. Packets are transmitted in the order
* they are passed to this method. * they are passed to this method.
*/ */
send: function (object) { send: function (object) {
this.emit("send", object); this.emit("send", object);
@ -142,47 +141,47 @@
this._flushOutgoing(); this._flushOutgoing();
}, },
/** /**
* Transmit streaming data via a bulk packet. * Transmit streaming data via a bulk packet.
* *
* This method initiates the bulk send process by queuing up the header data. * This method initiates the bulk send process by queuing up the header data.
* The caller receives eventual access to a stream for writing. * The caller receives eventual access to a stream for writing.
* *
* N.B.: Do *not* attempt to close the stream handed to you, as it will * N.B.: Do *not* attempt to close the stream handed to you, as it will
* continue to be used by this transport afterwards. Most users should * continue to be used by this transport afterwards. Most users should
* instead use the provided |copyFrom| function instead. * instead use the provided |copyFrom| function instead.
* *
* @param header Object * @param header Object
* This is modeled after the format of JSON packets above, but does not * This is modeled after the format of JSON packets above, but does not
* actually contain the data, but is instead just a routing header: * actually contain the data, but is instead just a routing header:
* * actor: Name of actor that will receive the packet * * actor: Name of actor that will receive the packet
* * type: Name of actor's method that should be called on receipt * * type: Name of actor's method that should be called on receipt
* * length: Size of the data to be sent * * length: Size of the data to be sent
* @return Promise * @return Promise
* The promise will be resolved when you are allowed to write to the * The promise will be resolved when you are allowed to write to the
* stream with an object containing: * stream with an object containing:
* * stream: This output stream should only be used directly if * * stream: This output stream should only be used directly if
* you can ensure that you will write exactly |length| * you can ensure that you will write exactly |length|
* bytes and will not close the stream when writing is * bytes and will not close the stream when writing is
* complete * complete
* * done: If you use the stream directly (instead of |copyFrom| * * done: If you use the stream directly (instead of |copyFrom|
* below), you must signal completion by resolving / * below), you must signal completion by resolving /
* rejecting this deferred. If it's rejected, the * rejecting this deferred. If it's rejected, the
* transport will be closed. If an Error is supplied as * transport will be closed. If an Error is supplied as
* a rejection value, it will be logged via |dumpn|. If * a rejection value, it will be logged via |dumpn|. If
* you do use |copyFrom|, resolving is taken care of for * you do use |copyFrom|, resolving is taken care of for
* you when copying completes. * you when copying completes.
* * copyFrom: A helper function for getting your data onto the * * copyFrom: A helper function for getting your data onto the
* stream that meets the stream handling requirements * stream that meets the stream handling requirements
* above, and has the following signature: * above, and has the following signature:
* @param input nsIAsyncInputStream * @param input nsIAsyncInputStream
* The stream to copy from. * The stream to copy from.
* @return Promise * @return Promise
* The promise is resolved when copying completes or * The promise is resolved when copying completes or
* rejected if any (unexpected) errors occur. * rejected if any (unexpected) errors occur.
* This object also emits "progress" events for each chunk * This object also emits "progress" events for each chunk
* that is copied. See stream-utils.js. * that is copied. See stream-utils.js.
*/ */
startBulkSend: function (header) { startBulkSend: function (header) {
this.emit("startBulkSend", header); this.emit("startBulkSend", header);
@ -193,12 +192,12 @@
return packet.streamReadyForWriting; return packet.streamReadyForWriting;
}, },
/** /**
* Close the transport. * Close the transport.
* @param reason nsresult / object (optional) * @param reason nsresult / object (optional)
* The status code or error message that corresponds to the reason for * The status code or error message that corresponds to the reason for
* closing the transport (likely because a stream closed or failed). * closing the transport (likely because a stream closed or failed).
*/ */
close: function (reason) { close: function (reason) {
this.emit("onClosed", reason); this.emit("onClosed", reason);
@ -219,54 +218,56 @@
} }
}, },
/** /**
* The currently outgoing packet (at the top of the queue). * The currently outgoing packet (at the top of the queue).
*/ */
get _currentOutgoing() { return this._outgoing[0]; }, get _currentOutgoing() {
return this._outgoing[0];
},
/** /**
* Flush data to the outgoing stream. Waits until the output stream notifies * Flush data to the outgoing stream. Waits until the output stream notifies
* us that it is ready to be written to (via onOutputStreamReady). * us that it is ready to be written to (via onOutputStreamReady).
*/ */
_flushOutgoing: function () { _flushOutgoing: function () {
if (!this._outgoingEnabled || this._outgoing.length === 0) { if (!this._outgoingEnabled || this._outgoing.length === 0) {
return; return;
} }
// If the top of the packet queue has nothing more to send, remove it. // If the top of the packet queue has nothing more to send, remove it.
if (this._currentOutgoing.done) { if (this._currentOutgoing.done) {
this._finishCurrentOutgoing(); this._finishCurrentOutgoing();
} }
if (this._outgoing.length > 0) { if (this._outgoing.length > 0) {
var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(); let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
this._output.asyncWait(this, 0, 0, threadManager.currentThread); this._output.asyncWait(this, 0, 0, threadManager.currentThread);
} }
}, },
/** /**
* Pause this transport's attempts to write to the output stream. This is * Pause this transport's attempts to write to the output stream. This is
* used when we've temporarily handed off our output stream for writing bulk * used when we've temporarily handed off our output stream for writing bulk
* data. * data.
*/ */
pauseOutgoing: function () { pauseOutgoing: function () {
this._outgoingEnabled = false; this._outgoingEnabled = false;
}, },
/** /**
* Resume this transport's attempts to write to the output stream. * Resume this transport's attempts to write to the output stream.
*/ */
resumeOutgoing: function () { resumeOutgoing: function () {
this._outgoingEnabled = true; this._outgoingEnabled = true;
this._flushOutgoing(); this._flushOutgoing();
}, },
// nsIOutputStreamCallback // nsIOutputStreamCallback
/** /**
* This is called when the output stream is ready for more data to be written. * This is called when the output stream is ready for more data to be written.
* The current outgoing packet will attempt to write some amount of data, but * The current outgoing packet will attempt to write some amount of data, but
* may not complete. * may not complete.
*/ */
onOutputStreamReady: DevToolsUtils.makeInfallible(function (stream) { onOutputStreamReady: DevToolsUtils.makeInfallible(function (stream) {
if (!this._outgoingEnabled || this._outgoing.length === 0) { if (!this._outgoingEnabled || this._outgoing.length === 0) {
return; return;
@ -278,17 +279,16 @@
if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) { if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) {
this.close(e.result); this.close(e.result);
return; return;
} else {
throw e;
} }
throw e;
} }
this._flushOutgoing(); this._flushOutgoing();
}, "DebuggerTransport.prototype.onOutputStreamReady"), }, "DebuggerTransport.prototype.onOutputStreamReady"),
/** /**
* Remove the current outgoing packet from the queue upon completion. * Remove the current outgoing packet from the queue upon completion.
*/ */
_finishCurrentOutgoing: function () { _finishCurrentOutgoing: function () {
if (this._currentOutgoing) { if (this._currentOutgoing) {
this._currentOutgoing.destroy(); this._currentOutgoing.destroy();
@ -296,9 +296,9 @@
} }
}, },
/** /**
* Clear the entire outgoing queue. * Clear the entire outgoing queue.
*/ */
_destroyAllOutgoing: function () { _destroyAllOutgoing: function () {
for (let packet of this._outgoing) { for (let packet of this._outgoing) {
packet.destroy(); packet.destroy();
@ -306,20 +306,20 @@
this._outgoing = []; this._outgoing = [];
}, },
/** /**
* Initialize the input stream for reading. Once this method has been called, * Initialize the input stream for reading. Once this method has been called,
* we watch for packets on the input stream, and pass them to the appropriate * we watch for packets on the input stream, and pass them to the appropriate
* handlers via this.hooks. * handlers via this.hooks.
*/ */
ready: function () { ready: function () {
this.active = true; this.active = true;
this._waitForIncoming(); this._waitForIncoming();
}, },
/** /**
* Asks the input stream to notify us (via onInputStreamReady) when it is * Asks the input stream to notify us (via onInputStreamReady) when it is
* ready for reading. * ready for reading.
*/ */
_waitForIncoming: function () { _waitForIncoming: function () {
if (this._incomingEnabled) { if (this._incomingEnabled) {
let threadManager = Cc["@mozilla.org/thread-manager;1"].getService(); let threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
@ -327,53 +327,54 @@
} }
}, },
/** /**
* Pause this transport's attempts to read from the input stream. This is * Pause this transport's attempts to read from the input stream. This is
* used when we've temporarily handed off our input stream for reading bulk * used when we've temporarily handed off our input stream for reading bulk
* data. * data.
*/ */
pauseIncoming: function () { pauseIncoming: function () {
this._incomingEnabled = false; this._incomingEnabled = false;
}, },
/** /**
* Resume this transport's attempts to read from the input stream. * Resume this transport's attempts to read from the input stream.
*/ */
resumeIncoming: function () { resumeIncoming: function () {
this._incomingEnabled = true; this._incomingEnabled = true;
this._flushIncoming(); this._flushIncoming();
this._waitForIncoming(); this._waitForIncoming();
}, },
// nsIInputStreamCallback // nsIInputStreamCallback
/** /**
* Called when the stream is either readable or closed. * Called when the stream is either readable or closed.
*/ */
onInputStreamReady: onInputStreamReady: DevToolsUtils.makeInfallible(function (stream) {
DevToolsUtils.makeInfallible(function (stream) { try {
try { while (stream.available() && this._incomingEnabled &&
while (stream.available() && this._incomingEnabled && this._processIncoming(stream, stream.available())) {
this._processIncoming(stream, stream.available())) {} // Loop until there is nothing more to process
this._waitForIncoming(); }
} catch (e) { this._waitForIncoming();
if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) { } catch (e) {
this.close(e.result); if (e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK) {
} else { this.close(e.result);
throw e; } else {
throw e;
}
} }
} }, "DebuggerTransport.prototype.onInputStreamReady"),
}, "DebuggerTransport.prototype.onInputStreamReady"),
/** /**
* Process the incoming data. Will create a new currently incoming Packet if * Process the incoming data. Will create a new currently incoming Packet if
* needed. Tells the incoming Packet to read as much data as it can, but * needed. Tells the incoming Packet to read as much data as it can, but
* reading may not complete. The Packet signals that its data is ready for * reading may not complete. The Packet signals that its data is ready for
* delivery by calling one of this transport's _on*Ready methods (see * delivery by calling one of this transport's _on*Ready methods (see
* ./packets.js and the _on*Ready methods below). * ./packets.js and the _on*Ready methods below).
* @return boolean * @return boolean
* Whether incoming stream processing should continue for any * Whether incoming stream processing should continue for any
* remaining data. * remaining data.
*/ */
_processIncoming: function (stream, count) { _processIncoming: function (stream, count) {
dumpv("Data available: " + count); dumpv("Data available: " + count);
@ -387,11 +388,12 @@
dumpv("Creating a new packet from incoming"); dumpv("Creating a new packet from incoming");
if (!this._readHeader(stream)) { if (!this._readHeader(stream)) {
return false; // Not enough data to read packet type // Not enough data to read packet type
return false;
} }
// Attempt to create a new Packet by trying to parse each possible // Attempt to create a new Packet by trying to parse each possible
// header pattern. // header pattern.
this._incoming = Packet.fromHeader(this._incomingHeader, this); this._incoming = Packet.fromHeader(this._incomingHeader, this);
if (!this._incoming) { if (!this._incoming) {
throw new Error("No packet types for header: " + throw new Error("No packet types for header: " +
@ -400,7 +402,7 @@
} }
if (!this._incoming.done) { if (!this._incoming.done) {
// We have an incomplete packet, keep reading it. // We have an incomplete packet, keep reading it.
dumpv("Existing packet incomplete, keep reading"); dumpv("Existing packet incomplete, keep reading");
this._incoming.read(stream, this._scriptableInput); this._incoming.read(stream, this._scriptableInput);
} }
@ -408,29 +410,29 @@
let msg = "Error reading incoming packet: (" + e + " - " + e.stack + ")"; let msg = "Error reading incoming packet: (" + e + " - " + e.stack + ")";
dumpn(msg); dumpn(msg);
// Now in an invalid state, shut down the transport. // Now in an invalid state, shut down the transport.
this.close(); this.close();
return false; return false;
} }
if (!this._incoming.done) { if (!this._incoming.done) {
// Still not complete, we'll wait for more data. // Still not complete, we'll wait for more data.
dumpv("Packet not done, wait for more"); dumpv("Packet not done, wait for more");
return true; return true;
} }
// Ready for next packet // Ready for next packet
this._flushIncoming(); this._flushIncoming();
return true; return true;
}, },
/** /**
* Read as far as we can into the incoming data, attempting to build up a * Read as far as we can into the incoming data, attempting to build up a
* complete packet header (which terminates with ":"). We'll only read up to * complete packet header (which terminates with ":"). We'll only read up to
* PACKET_HEADER_MAX characters. * PACKET_HEADER_MAX characters.
* @return boolean * @return boolean
* True if we now have a complete header. * True if we now have a complete header.
*/ */
_readHeader: function () { _readHeader: function () {
let amountToRead = PACKET_HEADER_MAX - this._incomingHeader.length; let amountToRead = PACKET_HEADER_MAX - this._incomingHeader.length;
this._incomingHeader += this._incomingHeader +=
@ -450,13 +452,13 @@
throw new Error("Failed to parse packet header!"); throw new Error("Failed to parse packet header!");
} }
// Not enough data yet. // Not enough data yet.
return false; return false;
}, },
/** /**
* If the incoming packet is done, log it as needed and clear the buffer. * If the incoming packet is done, log it as needed and clear the buffer.
*/ */
_flushIncoming: function () { _flushIncoming: function () {
if (!this._incoming.done) { if (!this._incoming.done) {
return; return;
@ -467,10 +469,10 @@
this._destroyIncoming(); this._destroyIncoming();
}, },
/** /**
* Handler triggered by an incoming JSONPacket completing it's |read| method. * Handler triggered by an incoming JSONPacket completing it's |read| method.
* Delivers the packet to this.hooks.onPacket. * Delivers the packet to this.hooks.onPacket.
*/ */
_onJSONObjectReady: function (object) { _onJSONObjectReady: function (object) {
DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => { DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
// Ensure the transport is still alive by the time this runs. // Ensure the transport is still alive by the time this runs.
@ -481,12 +483,12 @@
}, "DebuggerTransport instance's this.hooks.onPacket")); }, "DebuggerTransport instance's this.hooks.onPacket"));
}, },
/** /**
* Handler triggered by an incoming BulkPacket entering the |read| phase for * Handler triggered by an incoming BulkPacket entering the |read| phase for
* the stream portion of the packet. Delivers info about the incoming * the stream portion of the packet. Delivers info about the incoming
* streaming data to this.hooks.onBulkPacket. See the main comment on the * streaming data to this.hooks.onBulkPacket. See the main comment on the
* transport at the top of this file for more details. * transport at the top of this file for more details.
*/ */
_onBulkReadReady: function (...args) { _onBulkReadReady: function (...args) {
DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => { DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
// Ensure the transport is still alive by the time this runs. // Ensure the transport is still alive by the time this runs.
@ -497,10 +499,10 @@
}, "DebuggerTransport instance's this.hooks.onBulkPacket")); }, "DebuggerTransport instance's this.hooks.onBulkPacket"));
}, },
/** /**
* Remove all handlers and references related to the current incoming packet, * Remove all handlers and references related to the current incoming packet,
* either because it is now complete or because the transport is closing. * either because it is now complete or because the transport is closing.
*/ */
_destroyIncoming: function () { _destroyIncoming: function () {
if (this._incoming) { if (this._incoming) {
this._incoming.destroy(); this._incoming.destroy();
@ -513,43 +515,40 @@
exports.DebuggerTransport = DebuggerTransport; exports.DebuggerTransport = DebuggerTransport;
/** /**
* An adapter that handles data transfers between the debugger client and * An adapter that handles data transfers between the debugger client and
* server when they both run in the same process. It presents the same API as * server when they both run in the same process. It presents the same API as
* DebuggerTransport, but instead of transmitting serialized messages across a * DebuggerTransport, but instead of transmitting serialized messages across a
* connection it merely calls the packet dispatcher of the other side. * connection it merely calls the packet dispatcher of the other side.
* *
* @param other LocalDebuggerTransport * @param other LocalDebuggerTransport
* The other endpoint for this debugger connection. * The other endpoint for this debugger connection.
* *
* @see DebuggerTransport * @see DebuggerTransport
*/ */
function LocalDebuggerTransport(other) { function LocalDebuggerTransport(other) {
EventEmitter.decorate(this); EventEmitter.decorate(this);
this.other = other; this.other = other;
this.hooks = null; this.hooks = null;
/* // A packet number, shared between this and this.other. This isn't used by the
* A packet number, shared between this and this.other. This isn't used // protocol at all, but it makes the packet traces a lot easier to follow.
* by the protocol at all, but it makes the packet traces a lot easier to
* follow.
*/
this._serial = this.other ? this.other._serial : { count: 0 }; this._serial = this.other ? this.other._serial : { count: 0 };
this.close = this.close.bind(this); this.close = this.close.bind(this);
} }
LocalDebuggerTransport.prototype = { LocalDebuggerTransport.prototype = {
/** /**
* Transmit a message by directly calling the onPacket handler of the other * Transmit a message by directly calling the onPacket handler of the other
* endpoint. * endpoint.
*/ */
send: function (packet) { send: function (packet) {
this.emit("send", packet); this.emit("send", packet);
let serial = this._serial.count++; let serial = this._serial.count++;
if (dumpn.wantLogging) { if (dumpn.wantLogging) {
/* Check 'from' first, as 'echo' packets have both. */ // Check 'from' first, as 'echo' packets have both.
if (packet.from) { if (packet.from) {
dumpn("Packet " + serial + " sent from " + uneval(packet.from)); dumpn("Packet " + serial + " sent from " + uneval(packet.from));
} else if (packet.to) { } else if (packet.to) {
@ -560,7 +559,7 @@
let other = this.other; let other = this.other;
if (other) { if (other) {
DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => { DevToolsUtils.executeSoon(DevToolsUtils.makeInfallible(() => {
// Avoid the cost of JSON.stringify() when logging is disabled. // Avoid the cost of JSON.stringify() when logging is disabled.
if (dumpn.wantLogging) { if (dumpn.wantLogging) {
dumpn("Received packet " + serial + ": " + JSON.stringify(packet, null, 2)); dumpn("Received packet " + serial + ": " + JSON.stringify(packet, null, 2));
} }
@ -572,15 +571,15 @@
} }
}, },
/** /**
* Send a streaming bulk packet directly to the onBulkPacket handler of the * Send a streaming bulk packet directly to the onBulkPacket handler of the
* other endpoint. * other endpoint.
* *
* This case is much simpler than the full DebuggerTransport, since there is * This case is much simpler than the full DebuggerTransport, since there is
* no primary stream we have to worry about managing while we hand it off to * no primary stream we have to worry about managing while we hand it off to
* others temporarily. Instead, we can just make a single use pipe and be * others temporarily. Instead, we can just make a single use pipe and be
* done with it. * done with it.
*/ */
startBulkSend: function ({actor, type, length}) { startBulkSend: function ({actor, type, length}) {
this.emit("startBulkSend", {actor, type, length}); this.emit("startBulkSend", {actor, type, length});
@ -588,7 +587,8 @@
dumpn("Sent bulk packet " + serial + " for actor " + actor); dumpn("Sent bulk packet " + serial + " for actor " + actor);
if (!this.other) { if (!this.other) {
return; let error = new Error("startBulkSend: other side of transport missing");
return promise.reject(error);
} }
let pipe = new Pipe(true, true, 0, 0, null); let pipe = new Pipe(true, true, 0, 0, null);
@ -599,7 +599,7 @@
return; return;
} }
// Receiver // Receiver
let deferred = promise.defer(); let deferred = promise.defer();
let packet = { let packet = {
actor: actor, actor: actor,
@ -618,15 +618,15 @@
this.other.emit("onBulkPacket", packet); this.other.emit("onBulkPacket", packet);
this.other.hooks.onBulkPacket(packet); this.other.hooks.onBulkPacket(packet);
// Await the result of reading from the stream // Await the result of reading from the stream
deferred.promise.then(() => pipe.inputStream.close(), this.close); deferred.promise.then(() => pipe.inputStream.close(), this.close);
}, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket")); }, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket"));
// Sender // Sender
let sendDeferred = promise.defer(); let sendDeferred = promise.defer();
// The remote transport is not capable of resolving immediately here, so we // The remote transport is not capable of resolving immediately here, so we
// shouldn't be able to either. // shouldn't be able to either.
DevToolsUtils.executeSoon(() => { DevToolsUtils.executeSoon(() => {
let copyDeferred = promise.defer(); let copyDeferred = promise.defer();
@ -641,22 +641,22 @@
done: copyDeferred done: copyDeferred
}); });
// Await the result of writing to the stream // Await the result of writing to the stream
copyDeferred.promise.then(() => pipe.outputStream.close(), this.close); copyDeferred.promise.then(() => pipe.outputStream.close(), this.close);
}); });
return sendDeferred.promise; return sendDeferred.promise;
}, },
/** /**
* Close the transport. * Close the transport.
*/ */
close: function () { close: function () {
this.emit("close"); this.emit("close");
if (this.other) { if (this.other) {
// Remove the reference to the other endpoint before calling close(), to // Remove the reference to the other endpoint before calling close(), to
// avoid infinite recursion. // avoid infinite recursion.
let other = this.other; let other = this.other;
this.other = null; this.other = null;
other.close(); other.close();
@ -671,24 +671,24 @@
} }
}, },
/** /**
* An empty method for emulating the DebuggerTransport API. * An empty method for emulating the DebuggerTransport API.
*/ */
ready: function () {}, ready: function () {},
/** /**
* Helper function that makes an object fully immutable. * Helper function that makes an object fully immutable.
*/ */
_deepFreeze: function (object) { _deepFreeze: function (object) {
Object.freeze(object); Object.freeze(object);
for (let prop in object) { for (let prop in object) {
// Freeze the properties that are objects, not on the prototype, and not // Freeze the properties that are objects, not on the prototype, and not
// already frozen. Note that this might leave an unfrozen reference // already frozen. Note that this might leave an unfrozen reference
// somewhere in the object if there is an already frozen object containing // somewhere in the object if there is an already frozen object containing
// an unfrozen object. // an unfrozen object.
if (object.hasOwnProperty(prop) && typeof object === "object" && if (object.hasOwnProperty(prop) && typeof object === "object" &&
!Object.isFrozen(object)) { !Object.isFrozen(object)) {
this._deepFreeze(o[prop]); this._deepFreeze(object[prop]);
} }
} }
} }
@ -696,20 +696,20 @@
exports.LocalDebuggerTransport = LocalDebuggerTransport; exports.LocalDebuggerTransport = LocalDebuggerTransport;
/** /**
* A transport for the debugging protocol that uses nsIMessageSenders to * A transport for the debugging protocol that uses nsIMessageSenders to
* exchange packets with servers running in child processes. * exchange packets with servers running in child processes.
* *
* In the parent process, |sender| should be the nsIMessageSender for the * In the parent process, |sender| should be the nsIMessageSender for the
* child process. In a child process, |sender| should be the child process * child process. In a child process, |sender| should be the child process
* message manager, which sends packets to the parent. * message manager, which sends packets to the parent.
* *
* |prefix| is a string included in the message names, to distinguish * |prefix| is a string included in the message names, to distinguish
* multiple servers running in the same child process. * multiple servers running in the same child process.
* *
* This transport exchanges messages named 'debug:<prefix>:packet', where * This transport exchanges messages named 'debug:<prefix>:packet', where
* <prefix> is |prefix|, whose data is the protocol packet. * <prefix> is |prefix|, whose data is the protocol packet.
*/ */
function ChildDebuggerTransport(sender, prefix) { function ChildDebuggerTransport(sender, prefix) {
EventEmitter.decorate(this); EventEmitter.decorate(this);
@ -717,11 +717,11 @@
this._messageName = "debug:" + prefix + ":packet"; this._messageName = "debug:" + prefix + ":packet";
} }
/* /*
* To avoid confusion, we use 'message' to mean something that * To avoid confusion, we use 'message' to mean something that
* nsIMessageSender conveys, and 'packet' to mean a remote debugging * nsIMessageSender conveys, and 'packet' to mean a remote debugging
* protocol packet. * protocol packet.
*/ */
ChildDebuggerTransport.prototype = { ChildDebuggerTransport.prototype = {
constructor: ChildDebuggerTransport, constructor: ChildDebuggerTransport,
@ -754,22 +754,23 @@
exports.ChildDebuggerTransport = ChildDebuggerTransport; exports.ChildDebuggerTransport = ChildDebuggerTransport;
// WorkerDebuggerTransport is defined differently depending on whether we are // WorkerDebuggerTransport is defined differently depending on whether we are
// on the main thread or a worker thread. In the former case, we are required // on the main thread or a worker thread. In the former case, we are required
// by the devtools loader, and isWorker will be false. Otherwise, we are // by the devtools loader, and isWorker will be false. Otherwise, we are
// required by the worker loader, and isWorker will be true. // required by the worker loader, and isWorker will be true.
// //
// Each worker debugger supports only a single connection to the main thread. // Each worker debugger supports only a single connection to the main thread.
// However, its theoretically possible for multiple servers to connect to the // However, its theoretically possible for multiple servers to connect to the
// same worker. Consequently, each transport has a connection id, to allow // same worker. Consequently, each transport has a connection id, to allow
// messages from multiple connections to be multiplexed on a single channel. // messages from multiple connections to be multiplexed on a single channel.
if (!this.isWorker) { if (!this.isWorker) {
(function () { // Main thread // Main thread
/** (function () {
* A transport that uses a WorkerDebugger to send packets from the main /**
* thread to a worker thread. * A transport that uses a WorkerDebugger to send packets from the main
*/ * thread to a worker thread.
*/
function WorkerDebuggerTransport(dbg, id) { function WorkerDebuggerTransport(dbg, id) {
this._dbg = dbg; this._dbg = dbg;
this._id = id; this._id = id;
@ -817,11 +818,12 @@
exports.WorkerDebuggerTransport = WorkerDebuggerTransport; exports.WorkerDebuggerTransport = WorkerDebuggerTransport;
}).call(this); }).call(this);
} else { } else {
(function () { // Worker thread // Worker thread
/* (function () {
* A transport that uses a WorkerDebuggerGlobalScope to send packets from a /**
* worker thread to the main thread. * A transport that uses a WorkerDebuggerGlobalScope to send packets from a
*/ * worker thread to the main thread.
*/
function WorkerDebuggerTransport(scope, id) { function WorkerDebuggerTransport(scope, id) {
this._scope = scope; this._scope = scope;
this._id = id; this._id = id;
@ -869,5 +871,4 @@
exports.WorkerDebuggerTransport = WorkerDebuggerTransport; exports.WorkerDebuggerTransport = WorkerDebuggerTransport;
}).call(this); }).call(this);
} }
}); });