Bug 1859954 - Use XP_DARWIN rather than XP_MACOS in PHC r=glandium
[gecko.git] / devtools / shared / transport / local-transport.js
blobbff83b766648740623799af37617909cfde07422
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.
21  *
22  * @param other LocalDebuggerTransport
23  *        The other endpoint for this debugger connection.
24  *
25  * @see DebuggerTransport
26  */
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    * Transmit a message by directly calling the onPacket handler of the other
40    * endpoint.
41    */
42   send(packet) {
43     const serial = this._serial.count++;
44     if (flags.wantLogging) {
45       // Check 'from' first, as 'echo' packets have both.
46       if (packet.from) {
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));
50       }
51     }
52     this._deepFreeze(packet);
53     const other = this.other;
54     if (other) {
55       DevToolsUtils.executeSoon(
56         DevToolsUtils.makeInfallible(() => {
57           // Avoid the cost of JSON.stringify() when logging is disabled.
58           if (flags.wantLogging) {
59             dumpn(
60               "Received packet " +
61                 serial +
62                 ": " +
63                 JSON.stringify(packet, null, 2)
64             );
65           }
66           if (other.hooks) {
67             other.hooks.onPacket(packet);
68           }
69         }, "LocalDebuggerTransport instance's this.other.hooks.onPacket")
70       );
71     }
72   },
74   /**
75    * Send a streaming bulk packet directly to the onBulkPacket handler of the
76    * other endpoint.
77    *
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
81    * done with it.
82    */
83   startBulkSend({ actor, type, length }) {
84     const serial = this._serial.count++;
86     dumpn("Sent bulk packet " + serial + " for actor " + actor);
87     if (!this.other) {
88       const error = new Error("startBulkSend: other side of transport missing");
89       return Promise.reject(error);
90     }
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) {
98           return;
99         }
101         // Receiver
102         new Promise(receiverResolve => {
103           const packet = {
104             actor,
105             type,
106             length,
107             copyTo: output => {
108               const copying = StreamUtils.copyStream(
109                 pipe.inputStream,
110                 output,
111                 length
112               );
113               receiverResolve(copying);
114               return copying;
115             },
116             stream: pipe.inputStream,
117             done: receiverResolve,
118           };
120           this.other.hooks.onBulkPacket(packet);
121         })
122           // Await the result of reading from the stream
123           .then(() => pipe.inputStream.close(), this.close);
124       }, "LocalDebuggerTransport instance's this.other.hooks.onBulkPacket")
125     );
127     // Sender
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(() => {
132         return (
133           new Promise(copyResolve => {
134             senderResolve({
135               copyFrom: input => {
136                 const copying = StreamUtils.copyStream(
137                   input,
138                   pipe.outputStream,
139                   length
140                 );
141                 copyResolve(copying);
142                 return copying;
143               },
144               stream: pipe.outputStream,
145               done: copyResolve,
146             });
147           })
148             // Await the result of writing to the stream
149             .then(() => pipe.outputStream.close(), this.close)
150         );
151       });
152     });
153   },
155   /**
156    * Close the transport.
157    */
158   close() {
159     if (this.other) {
160       // Remove the reference to the other endpoint before calling close(), to
161       // avoid infinite recursion.
162       const other = this.other;
163       this.other = null;
164       other.close();
165     }
166     if (this.hooks) {
167       try {
168         if (this.hooks.onTransportClosed) {
169           this.hooks.onTransportClosed();
170         }
171       } catch (ex) {
172         console.error(ex);
173       }
174       this.hooks = null;
175     }
176   },
178   /**
179    * An empty method for emulating the DebuggerTransport API.
180    */
181   ready() {},
183   /**
184    * Helper function that makes an object fully immutable.
185    */
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.
193       if (
194         object.hasOwnProperty(prop) &&
195         typeof object === "object" &&
196         !Object.isFrozen(object)
197       ) {
198         this._deepFreeze(object[prop]);
199       }
200     }
201   },
204 exports.LocalDebuggerTransport = LocalDebuggerTransport;