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 * Transmit a message by directly calling the onPacket handler of the other
43 const serial = this._serial.count++;
44 if (flags.wantLogging) {
45 // Check 'from' first, as 'echo' packets have both.
47 dumpn("Packet " + serial + " sent from " + JSON.stringify(packet.from));
48 } else if (packet.to) {
49 dumpn("Packet " + serial + " sent to " + JSON.stringify(packet.to));
52 this._deepFreeze(packet);
53 const other = this.other;
55 DevToolsUtils.executeSoon(
56 DevToolsUtils.makeInfallible(() => {
57 // Avoid the cost of JSON.stringify() when logging is disabled.
58 if (flags.wantLogging) {
63 JSON.stringify(packet, null, 2)
67 other.hooks.onPacket(packet);
69 }, "LocalDebuggerTransport instance's this.other.hooks.onPacket")
75 * Send a streaming bulk packet directly to the onBulkPacket handler of the
78 * This case is much simpler than the full DebuggerTransport, since there is
79 * no primary stream we have to worry about managing while we hand it off to
80 * others temporarily. Instead, we can just make a single use pipe and be
83 startBulkSend({ actor, type, length }) {
84 const serial = this._serial.count++;
86 dumpn("Sent bulk packet " + serial + " for actor " + actor);
88 const error = new Error("startBulkSend: other side of transport missing");
89 return Promise.reject(error);
92 const pipe = new Pipe(true, true, 0, 0, null);
94 DevToolsUtils.executeSoon(
95 DevToolsUtils.makeInfallible(() => {
96 dumpn("Received bulk packet " + serial);
97 if (!this.other.hooks) {
102 new Promise(receiverResolve => {
108 const copying = StreamUtils.copyStream(
113 receiverResolve(copying);
116 stream: pipe.inputStream,
117 done: receiverResolve,
120 this.other.hooks.onBulkPacket(packet);
122 // Await the result of reading from the stream
123 .then(() => pipe.inputStream.close(), this.close);
124 }, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket")
128 return new Promise(senderResolve => {
129 // The remote transport is not capable of resolving immediately here, so we
130 // shouldn't be able to either.
131 DevToolsUtils.executeSoon(() => {
133 new Promise(copyResolve => {
136 const copying = StreamUtils.copyStream(
141 copyResolve(copying);
144 stream: pipe.outputStream,
148 // Await the result of writing to the stream
149 .then(() => pipe.outputStream.close(), this.close)
156 * Close the transport.
160 // Remove the reference to the other endpoint before calling close(), to
161 // avoid infinite recursion.
162 const other = this.other;
168 if (this.hooks.onTransportClosed) {
169 this.hooks.onTransportClosed();
179 * An empty method for emulating the DebuggerTransport API.
184 * Helper function that makes an object fully immutable.
186 _deepFreeze(object) {
187 Object.freeze(object);
188 for (const prop in object) {
189 // Freeze the properties that are objects, not on the prototype, and not
190 // already frozen. Note that this might leave an unfrozen reference
191 // somewhere in the object if there is an already frozen object containing
192 // an unfrozen object.
194 object.hasOwnProperty(prop) &&
195 typeof object === "object" &&
196 !Object.isFrozen(object)
198 this._deepFreeze(object[prop]);
204 exports.LocalDebuggerTransport = LocalDebuggerTransport;