зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1060093 - Implement toolbox to debug chrome of the content process. r=jryans
This commit is contained in:
Родитель
9df3eb992d
Коммит
64aef65e17
|
@ -513,6 +513,8 @@
|
||||||
<menuitem id="menu_browserToolbox"
|
<menuitem id="menu_browserToolbox"
|
||||||
observes="devtoolsMenuBroadcaster_BrowserToolbox"
|
observes="devtoolsMenuBroadcaster_BrowserToolbox"
|
||||||
accesskey="&browserToolboxMenu.accesskey;"/>
|
accesskey="&browserToolboxMenu.accesskey;"/>
|
||||||
|
<menuitem id="menu_browserContentToolbox"
|
||||||
|
observes="devtoolsMenuBroadcaster_BrowserContentToolbox"/>
|
||||||
<menuitem id="menu_browserConsole"
|
<menuitem id="menu_browserConsole"
|
||||||
observes="devtoolsMenuBroadcaster_BrowserConsole"
|
observes="devtoolsMenuBroadcaster_BrowserConsole"
|
||||||
accesskey="&browserConsoleCmd.accesskey;"/>
|
accesskey="&browserConsoleCmd.accesskey;"/>
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
<command id="Tools:DevAppMgr" oncommand="gDevToolsBrowser.openAppManager(gBrowser);" disabled="true" hidden="true"/>
|
<command id="Tools:DevAppMgr" oncommand="gDevToolsBrowser.openAppManager(gBrowser);" disabled="true" hidden="true"/>
|
||||||
<command id="Tools:WebIDE" oncommand="gDevToolsBrowser.openWebIDE();" disabled="true" hidden="true"/>
|
<command id="Tools:WebIDE" oncommand="gDevToolsBrowser.openWebIDE();" disabled="true" hidden="true"/>
|
||||||
<command id="Tools:BrowserToolbox" oncommand="BrowserToolboxProcess.init();" disabled="true" hidden="true"/>
|
<command id="Tools:BrowserToolbox" oncommand="BrowserToolboxProcess.init();" disabled="true" hidden="true"/>
|
||||||
|
<command id="Tools:BrowserContentToolbox" oncommand="gDevToolsBrowser.openContentProcessToolbox();" disabled="true" hidden="true"/>
|
||||||
<command id="Tools:BrowserConsole" oncommand="HUDService.toggleBrowserConsole();"/>
|
<command id="Tools:BrowserConsole" oncommand="HUDService.toggleBrowserConsole();"/>
|
||||||
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
|
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
|
||||||
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
|
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
|
||||||
|
@ -207,6 +208,9 @@
|
||||||
<broadcaster id="devtoolsMenuBroadcaster_BrowserToolbox"
|
<broadcaster id="devtoolsMenuBroadcaster_BrowserToolbox"
|
||||||
label="&browserToolboxMenu.label;"
|
label="&browserToolboxMenu.label;"
|
||||||
command="Tools:BrowserToolbox"/>
|
command="Tools:BrowserToolbox"/>
|
||||||
|
<broadcaster id="devtoolsMenuBroadcaster_BrowserContentToolbox"
|
||||||
|
label="&browserContentToolboxMenu.label;"
|
||||||
|
command="Tools:BrowserContentToolbox"/>
|
||||||
<broadcaster id="devtoolsMenuBroadcaster_BrowserConsole"
|
<broadcaster id="devtoolsMenuBroadcaster_BrowserConsole"
|
||||||
label="&browserConsoleCmd.label;"
|
label="&browserConsoleCmd.label;"
|
||||||
key="key_browserConsole"
|
key="key_browserConsole"
|
||||||
|
|
|
@ -20,11 +20,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
|
||||||
"resource:///modules/CustomizableUI.jsm");
|
"resource:///modules/CustomizableUI.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
|
||||||
|
"resource://gre/modules/devtools/dbg-server.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
|
||||||
|
"resource://gre/modules/devtools/dbg-client.jsm");
|
||||||
|
|
||||||
const EventEmitter = devtools.require("devtools/toolkit/event-emitter");
|
const EventEmitter = devtools.require("devtools/toolkit/event-emitter");
|
||||||
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
|
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
|
||||||
const MAX_ORDINAL = 99;
|
const MAX_ORDINAL = 99;
|
||||||
|
|
||||||
|
const bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method name to use for ES6 iteration. If symbols are enabled in this
|
* The method name to use for ES6 iteration. If symbols are enabled in this
|
||||||
* build, use Symbol.iterator; otherwise "@@iterator".
|
* build, use Symbol.iterator; otherwise "@@iterator".
|
||||||
|
@ -593,6 +600,7 @@ let gDevToolsBrowser = {
|
||||||
let remoteEnabled = chromeEnabled && devtoolsRemoteEnabled &&
|
let remoteEnabled = chromeEnabled && devtoolsRemoteEnabled &&
|
||||||
Services.prefs.getBoolPref("devtools.debugger.chrome-enabled");
|
Services.prefs.getBoolPref("devtools.debugger.chrome-enabled");
|
||||||
toggleCmd("Tools:BrowserToolbox", remoteEnabled);
|
toggleCmd("Tools:BrowserToolbox", remoteEnabled);
|
||||||
|
toggleCmd("Tools:BrowserContentToolbox", remoteEnabled && win.gMultiProcessBrowser);
|
||||||
|
|
||||||
// Enable Error Console?
|
// Enable Error Console?
|
||||||
let consoleEnabled = Services.prefs.getBoolPref("devtools.errorconsole.enabled");
|
let consoleEnabled = Services.prefs.getBoolPref("devtools.errorconsole.enabled");
|
||||||
|
@ -688,6 +696,61 @@ let gDevToolsBrowser = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getContentProcessTarget: function () {
|
||||||
|
// Create a DebuggerServer in order to connect locally to it
|
||||||
|
if (!DebuggerServer.initialized) {
|
||||||
|
DebuggerServer.init();
|
||||||
|
DebuggerServer.addBrowserActors();
|
||||||
|
}
|
||||||
|
|
||||||
|
let transport = DebuggerServer.connectPipe();
|
||||||
|
let client = new DebuggerClient(transport);
|
||||||
|
|
||||||
|
let deferred = promise.defer();
|
||||||
|
client.connect(() => {
|
||||||
|
client.mainRoot.listProcesses(response => {
|
||||||
|
// Do nothing if there is only one process, the parent process.
|
||||||
|
let contentProcesses = response.processes.filter(p => (!p.parent));
|
||||||
|
if (contentProcesses.length < 1) {
|
||||||
|
let msg = bundle.GetStringFromName("toolbox.noContentProcess.message");
|
||||||
|
Services.prompt.alert(null, "", msg);
|
||||||
|
deferred.reject("No content processes available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Otherwise, arbitrary connect to the unique content process.
|
||||||
|
client.attachProcess(contentProcesses[0].id)
|
||||||
|
.then(response => {
|
||||||
|
let options = {
|
||||||
|
form: response.form,
|
||||||
|
client: client,
|
||||||
|
chrome: true
|
||||||
|
};
|
||||||
|
return devtools.TargetFactory.forRemoteTab(options);
|
||||||
|
})
|
||||||
|
.then(target => {
|
||||||
|
// Ensure closing the connection in order to cleanup
|
||||||
|
// the debugger client and also the server created in the
|
||||||
|
// content process
|
||||||
|
target.on("close", () => {
|
||||||
|
client.close();
|
||||||
|
});
|
||||||
|
deferred.resolve(target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
openContentProcessToolbox: function () {
|
||||||
|
this._getContentProcessTarget()
|
||||||
|
.then(target => {
|
||||||
|
// Display a new toolbox, in a new window, with debugger by default
|
||||||
|
return gDevTools.showToolbox(target, "jsdebugger",
|
||||||
|
devtools.Toolbox.HostType.WINDOW);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install WebIDE widget
|
* Install WebIDE widget
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -268,6 +268,11 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||||
<!ENTITY browserToolboxMenu.label "Browser Toolbox">
|
<!ENTITY browserToolboxMenu.label "Browser Toolbox">
|
||||||
<!ENTITY browserToolboxMenu.accesskey "e">
|
<!ENTITY browserToolboxMenu.accesskey "e">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (browserContentToolboxMenu.label): This is the label for the
|
||||||
|
- application menu item that opens the browser content toolbox UI in the Tools menu.
|
||||||
|
- This toolbox allows to debug the chrome of the content process in multiprocess builds. -->
|
||||||
|
<!ENTITY browserContentToolboxMenu.label "Browser Content Toolbox">
|
||||||
|
|
||||||
<!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
|
<!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
|
||||||
<!ENTITY devToolbarMenu.label "Developer Toolbar">
|
<!ENTITY devToolbarMenu.label "Developer Toolbar">
|
||||||
<!ENTITY devToolbarMenu.accesskey "v">
|
<!ENTITY devToolbarMenu.accesskey "v">
|
||||||
|
|
|
@ -78,3 +78,8 @@ options.darkTheme.label=Dark theme
|
||||||
# LOCALIZATION NOTE (options.lightTheme.label)
|
# LOCALIZATION NOTE (options.lightTheme.label)
|
||||||
# Used as a label for light theme
|
# Used as a label for light theme
|
||||||
options.lightTheme.label=Light theme
|
options.lightTheme.label=Light theme
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (toolbox.noContentProcess.message)
|
||||||
|
# Used as a message in the alert displayed when trying to open a browser
|
||||||
|
# content toolbox and there is no content process running
|
||||||
|
toolbox.noContentProcess.message=No content process running.
|
||||||
|
|
|
@ -25,6 +25,7 @@ ContentProcessSingleton.prototype = {
|
||||||
case "app-startup": {
|
case "app-startup": {
|
||||||
Services.obs.addObserver(this, "console-api-log-event", false);
|
Services.obs.addObserver(this, "console-api-log-event", false);
|
||||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||||
|
cpmm.addMessageListener("DevTools:InitDebuggerServer", this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "console-api-log-event": {
|
case "console-api-log-event": {
|
||||||
|
@ -56,9 +57,19 @@ ContentProcessSingleton.prototype = {
|
||||||
case "xpcom-shutdown":
|
case "xpcom-shutdown":
|
||||||
Services.obs.removeObserver(this, "console-api-log-event");
|
Services.obs.removeObserver(this, "console-api-log-event");
|
||||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||||
|
cpmm.removeMessageListener("DevTools:InitDebuggerServer", this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
receiveMessage: function (message) {
|
||||||
|
// load devtools component on-demand
|
||||||
|
// Only reply if we are in a real content process
|
||||||
|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||||
|
let {init} = Cu.import("resource://gre/modules/devtools/content-server.jsm", {});
|
||||||
|
init(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentProcessSingleton]);
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentProcessSingleton]);
|
||||||
|
|
|
@ -5191,6 +5191,20 @@
|
||||||
"n_buckets": "1000",
|
"n_buckets": "1000",
|
||||||
"description": "The time (in milliseconds) that it took a 'listAddons' request to go round trip."
|
"description": "The time (in milliseconds) that it took a 'listAddons' request to go round trip."
|
||||||
},
|
},
|
||||||
|
"DEVTOOLS_DEBUGGER_RDP_LOCAL_LISTPROCESSES_MS": {
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "exponential",
|
||||||
|
"high": "10000",
|
||||||
|
"n_buckets": "1000",
|
||||||
|
"description": "The time (in milliseconds) that it took a 'listProcesses' request to go round trip."
|
||||||
|
},
|
||||||
|
"DEVTOOLS_DEBUGGER_RDP_REMOTE_LISTPROCESSES_MS": {
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "exponential",
|
||||||
|
"high": "10000",
|
||||||
|
"n_buckets": "1000",
|
||||||
|
"description": "The time (in milliseconds) that it took a 'listProcesses' request to go round trip."
|
||||||
|
},
|
||||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_DELETE_MS": {
|
"DEVTOOLS_DEBUGGER_RDP_LOCAL_DELETE_MS": {
|
||||||
"expires_in_version": "never",
|
"expires_in_version": "never",
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
|
|
|
@ -594,6 +594,21 @@ DebuggerClient.prototype = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach to a process in order to get the form of a ChildProcessActor.
|
||||||
|
*
|
||||||
|
* @param string aId
|
||||||
|
* The ID for the process to attach (returned by `listProcesses`).
|
||||||
|
*/
|
||||||
|
attachProcess: function (aId) {
|
||||||
|
let packet = {
|
||||||
|
to: 'root',
|
||||||
|
type: 'attachProcess',
|
||||||
|
id: aId
|
||||||
|
}
|
||||||
|
return this.request(packet);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release an object actor.
|
* Release an object actor.
|
||||||
*
|
*
|
||||||
|
@ -1292,6 +1307,15 @@ RootClient.prototype = {
|
||||||
listAddons: DebuggerClient.requester({ type: "listAddons" },
|
listAddons: DebuggerClient.requester({ type: "listAddons" },
|
||||||
{ telemetry: "LISTADDONS" }),
|
{ telemetry: "LISTADDONS" }),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the running processes.
|
||||||
|
*
|
||||||
|
* @param function aOnResponse
|
||||||
|
* Called with the response packet.
|
||||||
|
*/
|
||||||
|
listProcesses: DebuggerClient.requester({ type: "listProcesses" },
|
||||||
|
{ telemetry: "LISTPROCESSES" }),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of protocol's actors and methods.
|
* Description of protocol's actors and methods.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/* 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 { Cc, Ci, Cu } = require("chrome");
|
||||||
|
|
||||||
|
const { ChromeDebuggerActor } = require("devtools/server/actors/script");
|
||||||
|
const { WebConsoleActor } = require("devtools/server/actors/webconsole");
|
||||||
|
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
|
||||||
|
const { ActorPool } = require("devtools/server/main");
|
||||||
|
const Services = require("Services");
|
||||||
|
|
||||||
|
function ChildProcessActor(aConnection) {
|
||||||
|
this.conn = aConnection;
|
||||||
|
this._contextPool = new ActorPool(this.conn);
|
||||||
|
this.conn.addActorPool(this._contextPool);
|
||||||
|
this._threadActor = null;
|
||||||
|
|
||||||
|
// Use a see-everything debugger
|
||||||
|
this.makeDebugger = makeDebugger.bind(null, {
|
||||||
|
findDebuggees: dbg => dbg.findAllGlobals(),
|
||||||
|
shouldAddNewGlobalAsDebuggee: global => true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Scope into which the webconsole executes:
|
||||||
|
// An empty sandbox with chrome privileges
|
||||||
|
let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
|
||||||
|
.createInstance(Ci.nsIPrincipal);
|
||||||
|
let sandbox = Cu.Sandbox(systemPrincipal);
|
||||||
|
this._consoleScope = sandbox;
|
||||||
|
}
|
||||||
|
exports.ChildProcessActor = ChildProcessActor;
|
||||||
|
|
||||||
|
ChildProcessActor.prototype = {
|
||||||
|
actorPrefix: "process",
|
||||||
|
|
||||||
|
get isRootActor() true,
|
||||||
|
|
||||||
|
get exited() {
|
||||||
|
return !this._contextPool;
|
||||||
|
},
|
||||||
|
|
||||||
|
get url() {
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
get window() {
|
||||||
|
return this._consoleScope;
|
||||||
|
},
|
||||||
|
|
||||||
|
form: function() {
|
||||||
|
if (!this._consoleActor) {
|
||||||
|
this._consoleActor = new WebConsoleActor(this.conn, this);
|
||||||
|
this._contextPool.addActor(this._consoleActor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._threadActor) {
|
||||||
|
this._threadActor = new ChromeDebuggerActor(this.conn, this);
|
||||||
|
this._contextPool.addActor(this._threadActor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
actor: this.actorID,
|
||||||
|
name: "Content process",
|
||||||
|
|
||||||
|
consoleActor: this._consoleActor.actorID,
|
||||||
|
chromeDebugger: this._threadActor.actorID,
|
||||||
|
|
||||||
|
traits: {
|
||||||
|
highlightable: false,
|
||||||
|
networkMonitor: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnect: function() {
|
||||||
|
this.conn.removeActorPool(this._contextPool);
|
||||||
|
this._contextPool = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
preNest: function() {
|
||||||
|
// TODO: freeze windows
|
||||||
|
// window mediator doesn't work in child.
|
||||||
|
// it doesn't throw, but doesn't return any window
|
||||||
|
},
|
||||||
|
|
||||||
|
postNest: function() {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ChildProcessActor.prototype.requestTypes = {
|
||||||
|
};
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { Ci, Cu } = require("chrome");
|
const { Cc, Ci, Cu } = require("chrome");
|
||||||
const Services = require("Services");
|
const Services = require("Services");
|
||||||
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
|
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
|
||||||
const { DebuggerServer } = require("devtools/server/main");
|
const { DebuggerServer } = require("devtools/server/main");
|
||||||
|
@ -18,6 +18,10 @@ DevToolsUtils.defineLazyGetter(this, "StyleSheetActor", () => {
|
||||||
return require("devtools/server/actors/stylesheets").StyleSheetActor;
|
return require("devtools/server/actors/stylesheets").StyleSheetActor;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DevToolsUtils.defineLazyGetter(this, "ppmm", () => {
|
||||||
|
return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
|
||||||
|
});
|
||||||
|
|
||||||
/* Root actor for the remote debugging protocol. */
|
/* Root actor for the remote debugging protocol. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -358,6 +362,28 @@ RootActor.prototype = {
|
||||||
this._parameters.addonList.onListChanged = null;
|
this._parameters.addonList.onListChanged = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onListProcesses: function () {
|
||||||
|
let processes = [];
|
||||||
|
for (let i = 0; i < ppmm.childCount; i++) {
|
||||||
|
processes.push({
|
||||||
|
id: i, // XXX: may not be a perfect id, but process message manager doesn't expose anything...
|
||||||
|
parent: i == 0, // XXX Weak, but appear to be stable
|
||||||
|
tabCount: undefined, // TODO: exposes process message manager on frameloaders in order to compute this
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { processes: processes };
|
||||||
|
},
|
||||||
|
|
||||||
|
onAttachProcess: function (aRequest) {
|
||||||
|
let mm = ppmm.getChildAt(aRequest.id);
|
||||||
|
if (!mm) {
|
||||||
|
return { error: "noProcess",
|
||||||
|
message: "There is no process with id '" + aRequest.id + "'." };
|
||||||
|
}
|
||||||
|
return DebuggerServer.connectToContent(this.conn, mm)
|
||||||
|
.then(form => ({ form: form }));
|
||||||
|
},
|
||||||
|
|
||||||
/* This is not in the spec, but it's used by tests. */
|
/* This is not in the spec, but it's used by tests. */
|
||||||
onEcho: function (aRequest) {
|
onEcho: function (aRequest) {
|
||||||
/*
|
/*
|
||||||
|
@ -431,6 +457,8 @@ RootActor.prototype = {
|
||||||
RootActor.prototype.requestTypes = {
|
RootActor.prototype.requestTypes = {
|
||||||
"listTabs": RootActor.prototype.onListTabs,
|
"listTabs": RootActor.prototype.onListTabs,
|
||||||
"listAddons": RootActor.prototype.onListAddons,
|
"listAddons": RootActor.prototype.onListAddons,
|
||||||
|
"listProcesses": RootActor.prototype.onListProcesses,
|
||||||
|
"attachProcess": RootActor.prototype.onAttachProcess,
|
||||||
"echo": RootActor.prototype.onEcho,
|
"echo": RootActor.prototype.onEcho,
|
||||||
"protocolDescription": RootActor.prototype.onProtocolDescription
|
"protocolDescription": RootActor.prototype.onProtocolDescription
|
||||||
};
|
};
|
||||||
|
|
|
@ -559,6 +559,7 @@ WebConsoleActor.prototype =
|
||||||
startedListeners.push(listener);
|
startedListeners.push(listener);
|
||||||
break;
|
break;
|
||||||
case "FileActivity":
|
case "FileActivity":
|
||||||
|
if (this.window instanceof Ci.nsIDOMWindow) {
|
||||||
if (!this.consoleProgressListener) {
|
if (!this.consoleProgressListener) {
|
||||||
this.consoleProgressListener =
|
this.consoleProgressListener =
|
||||||
new ConsoleProgressListener(this.window, this);
|
new ConsoleProgressListener(this.window, this);
|
||||||
|
@ -566,6 +567,7 @@ WebConsoleActor.prototype =
|
||||||
this.consoleProgressListener.startMonitor(this.consoleProgressListener.
|
this.consoleProgressListener.startMonitor(this.consoleProgressListener.
|
||||||
MONITOR_FILE_ACTIVITY);
|
MONITOR_FILE_ACTIVITY);
|
||||||
startedListeners.push(listener);
|
startedListeners.push(listener);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "ReflowActivity":
|
case "ReflowActivity":
|
||||||
if (!this.consoleReflowListener) {
|
if (!this.consoleReflowListener) {
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/* 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 Ci = Components.interfaces;
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||||
|
const { DevToolsLoader } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["init"];
|
||||||
|
|
||||||
|
let started = false;
|
||||||
|
|
||||||
|
function init(msg) {
|
||||||
|
if (started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
started = true;
|
||||||
|
|
||||||
|
// Init a custom, invisible DebuggerServer, in order to not pollute
|
||||||
|
// the debugger with all devtools modules, nor break the debugger itself with using it
|
||||||
|
// in the same process.
|
||||||
|
let devtools = new DevToolsLoader();
|
||||||
|
devtools.invisibleToDebugger = true;
|
||||||
|
devtools.main("devtools/server/main");
|
||||||
|
let { DebuggerServer, ActorPool } = devtools;
|
||||||
|
|
||||||
|
if (!DebuggerServer.initialized) {
|
||||||
|
DebuggerServer.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case of apps being loaded in parent process, DebuggerServer is already
|
||||||
|
// initialized, but child specific actors are not registered.
|
||||||
|
// Otherwise, for child process, we need to load actors the first
|
||||||
|
// time we load child.js
|
||||||
|
DebuggerServer.addChildActors();
|
||||||
|
|
||||||
|
let mm = msg.target;
|
||||||
|
mm.QueryInterface(Ci.nsISyncMessageSender);
|
||||||
|
let prefix = msg.data.prefix;
|
||||||
|
|
||||||
|
// Connect both parent/child processes debugger servers RDP via message managers
|
||||||
|
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||||
|
|
||||||
|
let { ChildProcessActor } = devtools.require("devtools/server/actors/child-process");
|
||||||
|
let actor = new ChildProcessActor(conn);
|
||||||
|
let actorPool = new ActorPool(conn);
|
||||||
|
actorPool.addActor(actor);
|
||||||
|
conn.addActorPool(actorPool);
|
||||||
|
|
||||||
|
let response = {actor: actor.form()};
|
||||||
|
mm.sendAsyncMessage("debug:content-process-actor", response);
|
||||||
|
|
||||||
|
mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
|
||||||
|
mm.removeMessageListener("debug:content-process-destroy", onDestroy);
|
||||||
|
|
||||||
|
DebuggerServer.destroy();
|
||||||
|
started = false;
|
||||||
|
});
|
||||||
|
}
|
|
@ -704,6 +704,69 @@ var DebuggerServer = {
|
||||||
return this._onConnection(transport, aPrefix, true);
|
return this._onConnection(transport, aPrefix, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
connectToContent: function (aConnection, aMm) {
|
||||||
|
let deferred = defer();
|
||||||
|
|
||||||
|
let prefix = aConnection.allocID("content-process");
|
||||||
|
let actor, childTransport;
|
||||||
|
|
||||||
|
aMm.addMessageListener("debug:content-process-actor", function listener(msg) {
|
||||||
|
// Arbitrarily choose the first content process to reply
|
||||||
|
// XXX: This code needs to be updated if we use more than one content process
|
||||||
|
aMm.removeMessageListener("debug:content-process-actor", listener);
|
||||||
|
|
||||||
|
// Pipe Debugger message from/to parent/child via the message manager
|
||||||
|
childTransport = new ChildDebuggerTransport(aMm, prefix);
|
||||||
|
childTransport.hooks = {
|
||||||
|
onPacket: aConnection.send.bind(aConnection),
|
||||||
|
onClosed: function () {}
|
||||||
|
};
|
||||||
|
childTransport.ready();
|
||||||
|
|
||||||
|
aConnection.setForwarding(prefix, childTransport);
|
||||||
|
|
||||||
|
dumpn("establishing forwarding for process with prefix " + prefix);
|
||||||
|
|
||||||
|
actor = msg.json.actor;
|
||||||
|
|
||||||
|
deferred.resolve(actor);
|
||||||
|
});
|
||||||
|
|
||||||
|
aMm.sendAsyncMessage("DevTools:InitDebuggerServer", {
|
||||||
|
prefix: prefix
|
||||||
|
});
|
||||||
|
|
||||||
|
function onDisconnect() {
|
||||||
|
Services.obs.removeObserver(onMessageManagerDisconnect, "message-manager-disconnect");
|
||||||
|
events.off(aConnection, "closed", onDisconnect);
|
||||||
|
if (childTransport) {
|
||||||
|
// If we have a child transport, the actor has already
|
||||||
|
// been created. We need to stop using this message manager.
|
||||||
|
childTransport.close();
|
||||||
|
childTransport = null;
|
||||||
|
aConnection.cancelForwarding(prefix);
|
||||||
|
|
||||||
|
// ... and notify the child process to clean the tab actors.
|
||||||
|
try {
|
||||||
|
aMm.sendAsyncMessage("debug:content-process-destroy");
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let onMessageManagerDisconnect = DevToolsUtils.makeInfallible(function (subject, topic, data) {
|
||||||
|
if (subject == aMm) {
|
||||||
|
onDisconnect();
|
||||||
|
aConnection.send({ from: actor.actor, type: "tabDetached" });
|
||||||
|
}
|
||||||
|
}).bind(this);
|
||||||
|
Services.obs.addObserver(onMessageManagerDisconnect,
|
||||||
|
"message-manager-disconnect", false);
|
||||||
|
|
||||||
|
events.on(aConnection, "closed", onDisconnect);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a child process.
|
* Connect to a child process.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,6 +21,7 @@ SOURCES += [
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
||||||
EXTRA_JS_MODULES.devtools += [
|
EXTRA_JS_MODULES.devtools += [
|
||||||
|
'content-server.jsm',
|
||||||
'dbg-server.jsm',
|
'dbg-server.jsm',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ EXTRA_JS_MODULES.devtools.server += [
|
||||||
EXTRA_JS_MODULES.devtools.server.actors += [
|
EXTRA_JS_MODULES.devtools.server.actors += [
|
||||||
'actors/call-watcher.js',
|
'actors/call-watcher.js',
|
||||||
'actors/canvas.js',
|
'actors/canvas.js',
|
||||||
|
'actors/child-process.js',
|
||||||
'actors/childtab.js',
|
'actors/childtab.js',
|
||||||
'actors/common.js',
|
'actors/common.js',
|
||||||
'actors/csscoverage.js',
|
'actors/csscoverage.js',
|
||||||
|
|
|
@ -73,3 +73,5 @@ skip-if = buildapp == 'mulet'
|
||||||
[test_preference.html]
|
[test_preference.html]
|
||||||
[test_connectToChild.html]
|
[test_connectToChild.html]
|
||||||
skip-if = buildapp == 'mulet'
|
skip-if = buildapp == 'mulet'
|
||||||
|
[test_attachProcess.html]
|
||||||
|
skip-if = buildapp == 'mulet'
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
<SDOCTYPv HTM.>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
Bug 1060093 - Test DebuggerServer.attachProcess
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Mozilla Bug</title>
|
||||||
|
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script type="application/javascript;version=1.8">
|
||||||
|
|
||||||
|
let Cu = Components.utils;
|
||||||
|
let Cc = Components.classes;
|
||||||
|
let Ci = Components.interfaces;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||||
|
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
SpecialPowers.pushPrefEnv({
|
||||||
|
"set": [
|
||||||
|
// Always log packets when running tests.
|
||||||
|
["devtools.debugger.log", true],
|
||||||
|
["dom.mozBrowserFramesEnabled", true]
|
||||||
|
]
|
||||||
|
}, runTests);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runTests() {
|
||||||
|
// Create a remote iframe with a message manager
|
||||||
|
let iframe = document.createElement("iframe");
|
||||||
|
iframe.mozbrowser = true;
|
||||||
|
iframe.setAttribute("remote", "true");
|
||||||
|
iframe.setAttribute("src", "data:text/html,foo");
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
let mm = iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||||
|
|
||||||
|
// Instantiate a minimal server
|
||||||
|
if (!DebuggerServer.initialized) {
|
||||||
|
DebuggerServer.init(function () { return true; });
|
||||||
|
}
|
||||||
|
if (!DebuggerServer.createRootActor) {
|
||||||
|
DebuggerServer.addBrowserActors();
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstClient() {
|
||||||
|
// Fake a first connection to the content process
|
||||||
|
let transport = DebuggerServer.connectPipe();
|
||||||
|
let client = new DebuggerClient(transport);
|
||||||
|
client.connect(() => {
|
||||||
|
client.mainRoot.listProcesses(response => {
|
||||||
|
ok(response.processes.length >= 2, "Got at least the parent process and one child");
|
||||||
|
|
||||||
|
// Connect to the first content processe available
|
||||||
|
let content = response.processes.filter(p => (!p.parent))[0];
|
||||||
|
|
||||||
|
client.attachProcess(content.id).then(response => {
|
||||||
|
let actor = response.form;
|
||||||
|
ok(actor.consoleActor, "Got the console actor");
|
||||||
|
ok(actor.chromeDebugger, "Got the thread actor");
|
||||||
|
|
||||||
|
// Ensure sending at least one request to an actor...
|
||||||
|
client.request({
|
||||||
|
to: actor.consoleActor,
|
||||||
|
type: "evaluateJS",
|
||||||
|
text: "var a = 42; a"
|
||||||
|
}, function (response) {
|
||||||
|
ok(response.result, 42, "console.eval worked");
|
||||||
|
|
||||||
|
client.close(cleanup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
DebuggerServer.destroy();
|
||||||
|
iframe.remove();
|
||||||
|
SimpleTest.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
firstClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
Загрузка…
Ссылка в новой задаче