child-transport.js (3782B)
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/. */ 4 5 "use strict"; 6 7 const flags = require("resource://devtools/shared/flags.js"); 8 9 /** 10 * A transport for the debugging protocol that uses nsIMessageManagers to 11 * exchange packets with servers running in child processes. 12 * 13 * In the parent process, |mm| should be the nsIMessageSender for the 14 * child process. In a child process, |mm| should be the child process 15 * message manager, which sends packets to the parent. 16 * 17 * |prefix| is a string included in the message names, to distinguish 18 * multiple servers running in the same child process. 19 * 20 * This transport exchanges messages named 'debug:<prefix>:packet', where 21 * <prefix> is |prefix|, whose data is the protocol packet. 22 * 23 * To avoid confusion, we use 'message' to mean something that 24 * nsIMessageSender conveys, and 'packet' to mean a remote debugging 25 * protocol packet. 26 */ 27 class ChildDebuggerTransport { 28 constructor(mm, prefix) { 29 this._mm = mm; 30 this._messageName = "debug:" + prefix + ":packet"; 31 } 32 33 hooks = null; 34 35 _addListener() { 36 this._mm.addMessageListener(this._messageName, this); 37 } 38 39 _removeListener() { 40 try { 41 this._mm.removeMessageListener(this._messageName, this); 42 } catch (e) { 43 if (e.result != Cr.NS_ERROR_NULL_POINTER) { 44 throw e; 45 } 46 // In some cases, especially when using messageManagers in non-e10s mode, we reach 47 // this point with a dead messageManager which only throws errors but does not 48 // seem to indicate in any other way that it is dead. 49 } 50 } 51 52 ready() { 53 this._addListener(); 54 } 55 56 close(options) { 57 this._removeListener(); 58 if (this.hooks.onTransportClosed) { 59 this.hooks.onTransportClosed(null, options); 60 } 61 } 62 63 receiveMessage({ data }) { 64 this.hooks.onPacket(data); 65 } 66 67 /** 68 * Helper method to ensure a given `object` can be sent across message manager 69 * without being serialized to JSON. 70 * See https://searchfox.org/mozilla-central/rev/6bfadf95b4a6aaa8bb3b2a166d6c3545983e179a/dom/base/nsFrameMessageManager.cpp#458-469 71 */ 72 _canBeSerialized(object) { 73 try { 74 const holder = new StructuredCloneHolder( 75 "ChildDebuggerTransport._canBeSerialized", 76 null, 77 object 78 ); 79 holder.deserialize(this); 80 } catch (e) { 81 return false; 82 } 83 return true; 84 } 85 86 pathToUnserializable(object) { 87 for (const key in object) { 88 const value = object[key]; 89 if (!this._canBeSerialized(value)) { 90 if (typeof value == "object") { 91 return [key].concat(this.pathToUnserializable(value)); 92 } 93 return [key]; 94 } 95 } 96 return []; 97 } 98 99 send(packet) { 100 if (flags.testing && !this._canBeSerialized(packet)) { 101 const attributes = this.pathToUnserializable(packet); 102 let msg = 103 "Following packet can't be serialized: " + JSON.stringify(packet); 104 msg += "\nBecause of attributes: " + attributes.join(", ") + "\n"; 105 msg += "Did you pass a function or an XPCOM object in it?"; 106 throw new Error(msg); 107 } 108 try { 109 this._mm.sendAsyncMessage(this._messageName, packet); 110 } catch (e) { 111 if (e.result != Cr.NS_ERROR_NULL_POINTER) { 112 throw e; 113 } 114 // In some cases, especially when using messageManagers in non-e10s mode, we reach 115 // this point with a dead messageManager which only throws errors but does not 116 // seem to indicate in any other way that it is dead. 117 } 118 } 119 120 startBulkSend() { 121 throw new Error("Can't send bulk data to child processes."); 122 } 123 } 124 125 exports.ChildDebuggerTransport = ChildDebuggerTransport;