/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const flags = require(
"resource://devtools/shared/flags.js");
/**
* A transport for the debugging protocol that uses nsIMessageManagers to
* exchange packets with servers running in child processes.
*
* In the parent process, |mm| should be the nsIMessageSender for the
* child process. In a child process, |mm| should be the child process
* message manager, which sends packets to the parent.
*
* |prefix| is a string included in the message names, to distinguish
* multiple servers running in the same child process.
*
* This transport exchanges messages named 'debug:<prefix>:packet', where
* <prefix> is |prefix|, whose data is the protocol packet.
*/
function ChildDebuggerTransport(mm, prefix) {
this._mm = mm;
this._messageName =
"debug:" + prefix +
":packet";
}
/*
* To avoid confusion, we use 'message' to mean something that
* nsIMessageSender conveys, and 'packet' to mean a remote debugging
* protocol packet.
*/
ChildDebuggerTransport.prototype = {
constructor: ChildDebuggerTransport,
hooks:
null,
_addListener() {
this._mm.addMessageListener(
this._messageName,
this);
},
_removeListener() {
try {
this._mm.removeMessageListener(
this._messageName,
this);
}
catch (e) {
if (e.result != Cr.NS_ERROR_NULL_POINTER) {
throw e;
}
// In some cases, especially when using messageManagers in non-e10s mode, we reach
// this point with a dead messageManager which only throws errors but does not
// seem to indicate in any other way that it is dead.
}
},
ready() {
this._addListener();
},
close(options) {
this._removeListener();
if (
this.hooks.onTransportClosed) {
this.hooks.onTransportClosed(
null, options);
}
},
receiveMessage({ data }) {
this.hooks.onPacket(data);
},
/**
* Helper method to ensure a given `object` can be sent across message manager
* without being serialized to JSON.
* See https://searchfox.org/mozilla-central/rev/6bfadf95b4a6aaa8bb3b2a166d6c3545983e179a/dom/base/nsFrameMessageManager.cpp#458-469
*/
_canBeSerialized(object) {
try {
const holder =
new StructuredCloneHolder(
"ChildDebuggerTransport._canBeSerialized",
null,
object
);
holder.deserialize(
this);
}
catch (e) {
return false;
}
return true;
},
pathToUnserializable(object) {
for (
const key in object) {
const value = object[key];
if (!
this._canBeSerialized(value)) {
if (
typeof value ==
"object") {
return [key].concat(
this.pathToUnserializable(value));
}
return [key];
}
}
return [];
},
send(packet) {
if (flags.testing && !
this._canBeSerialized(packet)) {
const attributes =
this.pathToUnserializable(packet);
let msg =
"Following packet can't be serialized: " + JSON.stringify(packet);
msg +=
"\nBecause of attributes: " + attributes.join(
", ") +
"\n";
msg +=
"Did you pass a function or an XPCOM object in it?";
throw new Error(msg);
}
try {
this._mm.sendAsyncMessage(
this._messageName, packet);
}
catch (e) {
if (e.result != Cr.NS_ERROR_NULL_POINTER) {
throw e;
}
// In some cases, especially when using messageManagers in non-e10s mode, we reach
// this point with a dead messageManager which only throws errors but does not
// seem to indicate in any other way that it is dead.
}
},
startBulkSend() {
throw new Error(
"Can't send bulk data to child processes.");
},
};
exports.ChildDebuggerTransport = ChildDebuggerTransport;