1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const DevToolsUtils
= require("resource://devtools/shared/DevToolsUtils.js");
8 const { dumpn
} = DevToolsUtils
;
9 const flags
= require("resource://devtools/shared/flags.js");
10 const StreamUtils
= require("resource://devtools/shared/transport/stream-utils.js");
12 loader
.lazyGetter(this, "Pipe", () => {
13 return Components
.Constructor("@mozilla.org/pipe;1", "nsIPipe", "init");
17 * An adapter that handles data transfers between the devtools client and
18 * server when they both run in the same process. It presents the same API as
19 * DebuggerTransport, but instead of transmitting serialized messages across a
20 * connection it merely calls the packet dispatcher of the other side.
22 * @param other LocalDebuggerTransport
23 * The other endpoint for this debugger connection.
25 * @see DebuggerTransport
27 function LocalDebuggerTransport(other
) {
31 // A packet number, shared between this and this.other. This isn't used by the
32 // protocol at all, but it makes the packet traces a lot easier to follow.
33 this._serial
= this.other
? this.other
._serial
: { count
: 0 };
34 this.close
= this.close
.bind(this);
37 LocalDebuggerTransport
.prototype = {
39 * Boolean to help identify DevToolsClient instances connected to a LocalDevToolsTransport pipe
40 * and so connected to the same runtime as the frontend.
42 isLocalTransport
: true,
45 * Transmit a message by directly calling the onPacket handler of the other
49 const serial
= this._serial
.count
++;
50 if (flags
.wantLogging
) {
51 // Check 'from' first, as 'echo' packets have both.
53 dumpn("Packet " + serial
+ " sent from " + JSON
.stringify(packet
.from));
54 } else if (packet
.to
) {
55 dumpn("Packet " + serial
+ " sent to " + JSON
.stringify(packet
.to
));
58 this._deepFreeze(packet
);
59 const other
= this.other
;
61 DevToolsUtils
.executeSoon(
62 DevToolsUtils
.makeInfallible(() => {
63 // Avoid the cost of JSON.stringify() when logging is disabled.
64 if (flags
.wantLogging
) {
69 JSON
.stringify(packet
, null, 2)
73 other
.hooks
.onPacket(packet
);
75 }, "LocalDebuggerTransport instance's this.other.hooks.onPacket")
81 * Send a streaming bulk packet directly to the onBulkPacket handler of the
84 * This case is much simpler than the full DebuggerTransport, since there is
85 * no primary stream we have to worry about managing while we hand it off to
86 * others temporarily. Instead, we can just make a single use pipe and be
89 startBulkSend({ actor
, type
, length
}) {
90 const serial
= this._serial
.count
++;
92 dumpn("Sent bulk packet " + serial
+ " for actor " + actor
);
94 const error
= new Error("startBulkSend: other side of transport missing");
95 return Promise
.reject(error
);
98 const pipe
= new Pipe(true, true, 0, 0, null);
100 DevToolsUtils
.executeSoon(
101 DevToolsUtils
.makeInfallible(() => {
102 dumpn("Received bulk packet " + serial
);
103 if (!this.other
.hooks
) {
108 new Promise(receiverResolve
=> {
114 const copying
= StreamUtils
.copyStream(
119 receiverResolve(copying
);
122 stream
: pipe
.inputStream
,
123 done
: receiverResolve
,
126 this.other
.hooks
.onBulkPacket(packet
);
128 // Await the result of reading from the stream
129 .then(() => pipe
.inputStream
.close(), this.close
);
130 }, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket")
134 return new Promise(senderResolve
=> {
135 // The remote transport is not capable of resolving immediately here, so we
136 // shouldn't be able to either.
137 DevToolsUtils
.executeSoon(() => {
139 new Promise(copyResolve
=> {
142 const copying
= StreamUtils
.copyStream(
147 copyResolve(copying
);
150 stream
: pipe
.outputStream
,
154 // Await the result of writing to the stream
155 .then(() => pipe
.outputStream
.close(), this.close
)
162 * Close the transport.
166 // Remove the reference to the other endpoint before calling close(), to
167 // avoid infinite recursion.
168 const other
= this.other
;
174 if (this.hooks
.onTransportClosed
) {
175 this.hooks
.onTransportClosed();
185 * An empty method for emulating the DebuggerTransport API.
190 * Helper function that makes an object fully immutable.
192 _deepFreeze(object
) {
193 Object
.freeze(object
);
194 for (const prop
in object
) {
195 // Freeze the properties that are objects, not on the prototype, and not
196 // already frozen. Note that this might leave an unfrozen reference
197 // somewhere in the object if there is an already frozen object containing
198 // an unfrozen object.
200 object
.hasOwnProperty(prop
) &&
201 typeof object
=== "object" &&
202 !Object
.isFrozen(object
)
204 this._deepFreeze(object
[prop
]);
210 exports
.LocalDebuggerTransport
= LocalDebuggerTransport
;