Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / devtools / shared / transport / local-transport.js
blob99df74c90ebe8aa3d196599dfb6ad12a6d237169
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/. */
5 "use strict";
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");
14 });
16 /**
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) {
28 this.other = other;
29 this.hooks = null;
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 = {
38 /**
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,
44 /**
45 * Transmit a message by directly calling the onPacket handler of the other
46 * endpoint.
48 send(packet) {
49 const serial = this._serial.count++;
50 if (flags.wantLogging) {
51 // Check 'from' first, as 'echo' packets have both.
52 if (packet.from) {
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;
60 if (other) {
61 DevToolsUtils.executeSoon(
62 DevToolsUtils.makeInfallible(() => {
63 // Avoid the cost of JSON.stringify() when logging is disabled.
64 if (flags.wantLogging) {
65 dumpn(
66 "Received packet " +
67 serial +
68 ": " +
69 JSON.stringify(packet, null, 2)
72 if (other.hooks) {
73 other.hooks.onPacket(packet);
75 }, "LocalDebuggerTransport instance's this.other.hooks.onPacket")
80 /**
81 * Send a streaming bulk packet directly to the onBulkPacket handler of the
82 * other endpoint.
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
87 * done with it.
89 startBulkSend({ actor, type, length }) {
90 const serial = this._serial.count++;
92 dumpn("Sent bulk packet " + serial + " for actor " + actor);
93 if (!this.other) {
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) {
104 return;
107 // Receiver
108 new Promise(receiverResolve => {
109 const packet = {
110 actor,
111 type,
112 length,
113 copyTo: output => {
114 const copying = StreamUtils.copyStream(
115 pipe.inputStream,
116 output,
117 length
119 receiverResolve(copying);
120 return 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")
133 // Sender
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(() => {
138 return (
139 new Promise(copyResolve => {
140 senderResolve({
141 copyFrom: input => {
142 const copying = StreamUtils.copyStream(
143 input,
144 pipe.outputStream,
145 length
147 copyResolve(copying);
148 return copying;
150 stream: pipe.outputStream,
151 done: copyResolve,
154 // Await the result of writing to the stream
155 .then(() => pipe.outputStream.close(), this.close)
162 * Close the transport.
164 close() {
165 if (this.other) {
166 // Remove the reference to the other endpoint before calling close(), to
167 // avoid infinite recursion.
168 const other = this.other;
169 this.other = null;
170 other.close();
172 if (this.hooks) {
173 try {
174 if (this.hooks.onTransportClosed) {
175 this.hooks.onTransportClosed();
177 } catch (ex) {
178 console.error(ex);
180 this.hooks = null;
185 * An empty method for emulating the DebuggerTransport API.
187 ready() {},
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.
199 if (
200 object.hasOwnProperty(prop) &&
201 typeof object === "object" &&
202 !Object.isFrozen(object)
204 this._deepFreeze(object[prop]);
210 exports.LocalDebuggerTransport = LocalDebuggerTransport;