Bug 1059308 - Make frame selection button to work in browser toolbox. r=jryans,past

This commit is contained in:
Alexandre Poirot 2015-03-03 04:19:00 +01:00
parent 62e01ad84b
commit da8dcc2524
20 changed files with 468 additions and 303 deletions

View File

@ -178,6 +178,11 @@ let RemoteDebugger = {
let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
// Allow debugging of chrome for any process
if (!restrictPrivileges) {
DebuggerServer.allowChromeProcess = true;
}
/**
* Construct a root actor appropriate for use in a server running in B2G.
* The returned root actor respects the factories registered with

View File

@ -110,6 +110,7 @@ devtoolsCommandlineHandler.prototype = {
let debuggerServer = serverLoader.DebuggerServer;
debuggerServer.init();
debuggerServer.addBrowserActors();
debuggerServer.allowChromeProcess = true;
let listener = debuggerServer.createListener();
listener.portOrPath = portOrPath;

View File

@ -116,29 +116,31 @@ BrowserToolboxProcess.prototype = {
* Initializes the debugger server.
*/
_initServer: function() {
if (this.debuggerServer) {
dumpn("The chrome toolbox server is already running.");
return;
}
dumpn("Initializing the chrome toolbox server.");
if (!this.loader) {
// Create a separate loader instance, so that we can be sure to receive a
// separate instance of the DebuggingServer from the rest of the devtools.
// This allows us to safely use the tools against even the actors and
// DebuggingServer itself, especially since we can mark this loader as
// invisible to the debugger (unlike the usual loader settings).
this.loader = new DevToolsLoader();
this.loader.invisibleToDebugger = true;
this.loader.main("devtools/server/main");
this.debuggerServer = this.loader.DebuggerServer;
dumpn("Created a separate loader instance for the DebuggerServer.");
// Create a separate loader instance, so that we can be sure to receive a
// separate instance of the DebuggingServer from the rest of the devtools.
// This allows us to safely use the tools against even the actors and
// DebuggingServer itself, especially since we can mark this loader as
// invisible to the debugger (unlike the usual loader settings).
this.loader = new DevToolsLoader();
this.loader.invisibleToDebugger = true;
this.loader.main("devtools/server/main");
this.debuggerServer = this.loader.DebuggerServer;
dumpn("Created a separate loader instance for the DebuggerServer.");
// Forward interesting events.
this.debuggerServer.on("connectionchange", this.emit.bind(this));
}
// Forward interesting events.
this.debuggerServer.on("connectionchange", this.emit.bind(this));
if (!this.debuggerServer.initialized) {
this.debuggerServer.init();
this.debuggerServer.addBrowserActors();
dumpn("initialized and added the browser actors for the DebuggerServer.");
}
this.debuggerServer.init();
this.debuggerServer.addBrowserActors();
this.debuggerServer.allowChromeProcess = true;
dumpn("initialized and added the browser actors for the DebuggerServer.");
let chromeDebuggingPort =
Services.prefs.getIntPref("devtools.debugger.chrome-debugging-port");

View File

@ -129,11 +129,19 @@ let onConnectionReady = Task.async(function*(aType, aTraits) {
let gParent = document.getElementById("globalActors");
// Build the Remote Process button
if (Object.keys(globals).length > 1) {
// If Fx<37, tab actors were used to be exposed on RootActor
// but in Fx>=37, chrome is debuggable via attachProcess() and ChromeActor
if (globals.consoleActor || gClient.mainRoot.traits.allowChromeProcess) {
let a = document.createElement("a");
a.onclick = function() {
openToolbox(globals, true);
if (gClient.mainRoot.traits.allowChromeProcess) {
gClient.attachProcess()
.then(aResponse => {
openToolbox(aResponse.form, true);
});
} else if (globals.consoleActor) {
openToolbox(globals, true, "webconsole", false);
}
}
a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
a.className = "remote-process";

View File

@ -697,6 +697,7 @@ let gDevToolsBrowser = {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let transport = DebuggerServer.connectPipe();
let client = new DebuggerClient(transport);

View File

@ -41,7 +41,9 @@ let connect = Task.async(function*() {
openToolbox({ form: addonActor, chrome: true, isTabActor: false });
});
} else {
gClient.listTabs(openToolbox);
gClient.attachProcess().then(aResponse => {
openToolbox({ form: aResponse.form, chrome: true });
});
}
});
});
@ -52,6 +54,7 @@ function setPrefDefaults() {
Services.prefs.setBoolPref("devtools.profiler.ui.show-platform-data", true);
Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", false);
Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
}
window.addEventListener("load", function() {

View File

@ -2152,16 +2152,17 @@ ScratchpadWindow.prototype = Heritage.extend(ScratchpadTab.prototype, {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() => {
client.listTabs(aResponse => {
client.attachProcess().then(aResponse => {
if (aResponse.error) {
reportError("listTabs", aResponse);
deferred.reject(aResponse);
}
else {
deferred.resolve({ form: aResponse, client: client });
deferred.resolve({ form: aResponse.form, client: client });
}
});
});

View File

@ -187,22 +187,16 @@ HUD_SERVICE.prototype =
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() =>
client.listTabs((aResponse) => {
// Add Global Process debugging...
let globals = Cu.cloneInto(aResponse, {});
delete globals.tabs;
delete globals.selected;
// ...only if there are appropriate actors (a 'from' property will
// always be there).
if (Object.keys(globals).length > 1) {
deferred.resolve({ form: globals, client: client, chrome: true });
} else {
deferred.reject("Global console not found!");
}
}));
client.connect(() => {
client.attachProcess().then(aResponse => {
// Set chrome:false in order to attach to the target
// (i.e. send an `attach` request to the chrome actor)
deferred.resolve({ form: aResponse.form, client: client, chrome: false });
}, deferred.reject);
});
return deferred.promise;
}
@ -210,13 +204,7 @@ HUD_SERVICE.prototype =
let target;
function getTarget(aConnection)
{
let options = {
form: aConnection.form,
client: aConnection.client,
chrome: true,
};
return devtools.TargetFactory.forRemoteTab(options);
return devtools.TargetFactory.forRemoteTab(aConnection);
}
function openWindow(aTarget)
@ -241,12 +229,12 @@ HUD_SERVICE.prototype =
}
connect().then(getTarget).then(openWindow).then((aWindow) => {
this.openBrowserConsole(target, aWindow, aWindow)
return this.openBrowserConsole(target, aWindow, aWindow)
.then((aBrowserConsole) => {
this._browserConsoleDefer.resolve(aBrowserConsole);
this._browserConsoleDefer = null;
})
}, console.error);
}, console.error.bind(console));
return this._browserConsoleDefer.promise;
},
@ -640,10 +628,13 @@ WebConsole.prototype = {
this._destroyer = promise.defer();
let popupset = this.mainPopupSet;
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
for (let panel of panels) {
panel.hidePopup();
// The document may already be removed
if (this.chromeUtilsWindow && this.mainPopupSet) {
let popupset = this.mainPopupSet;
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
for (let panel of panels) {
panel.hidePopup();
}
}
let onDestroy = function WC_onDestroyUI() {

View File

@ -440,7 +440,11 @@ WebConsoleFrame.prototype = {
* @type boolean
*/
get persistLog() {
return Services.prefs.getBoolPref(PREF_PERSISTLOG);
// For the browser console, we receive tab navigation
// when the original top level window we attached to is closed,
// but we don't want to reset console history and just switch to
// the next available window.
return this.owner._browserConsole || Services.prefs.getBoolPref(PREF_PERSISTLOG);
},
/**
@ -3426,7 +3430,7 @@ JSTerm.prototype = {
let selectedNodeActor = null;
let inspectorSelection = this.hud.owner.getInspectorSelection();
if (inspectorSelection) {
if (inspectorSelection && inspectorSelection.nodeFront) {
selectedNodeActor = inspectorSelection.nodeFront.actorID;
}

View File

@ -939,9 +939,9 @@ let UI = {
this.toolboxPromise = null;
return toolboxPromise.then(toolbox => {
return toolbox.destroy();
}).catch(console.error)
}).then(null, console.error)
.then(() => this._closeToolboxUI())
.catch(console.error);
.then(null, console.error);
}
return promise.resolve();
},

View File

@ -217,11 +217,25 @@ let AppManager = exports.AppManager = {
getTarget: function() {
if (this.selectedProject.type == "mainProcess") {
return devtools.TargetFactory.forRemoteTab({
form: this._listTabsResponse,
client: this.connection.client,
chrome: true
});
// Fx >=37 exposes a ChromeActor to debug the main process
if (this.connection.client.mainRoot.traits.allowChromeProcess) {
return this.connection.client.attachProcess()
.then(aResponse => {
return devtools.TargetFactory.forRemoteTab({
form: aResponse.form,
client: this.connection.client,
chrome: true
});
});
} else {
// Fx <37 exposes tab actors on the root actor
return devtools.TargetFactory.forRemoteTab({
form: this._listTabsResponse,
client: this.connection.client,
chrome: true,
isTabActor: false
});
}
}
if (this.selectedProject.type == "tab") {
@ -414,8 +428,12 @@ let AppManager = exports.AppManager = {
},
isMainProcessDebuggable: function() {
return this._listTabsResponse &&
this._listTabsResponse.consoleActor;
// Fx <37 exposes chrome tab actors on RootActor
// Fx >=37 exposes a dedicated actor via attachProcess request
return this.connection.client &&
this.connection.client.mainRoot.traits.allowChromeProcess ||
(this._listTabsResponse &&
this._listTabsResponse.consoleActor);
},
get deviceFront() {

View File

@ -575,6 +575,7 @@ let gLocalRuntime = {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.allowChromeProcess = true;
connection.host = null; // Force Pipe transport
connection.port = null;
connection.connect();

View File

@ -7416,6 +7416,7 @@ var RemoteDebugger = {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
DebuggerServer.registerModule("resource://gre/modules/dbg-browser-actors.js");
DebuggerServer.allowChromeProcess = true;
}
let pathOrPort = this._getPath();

View File

@ -624,14 +624,17 @@ DebuggerClient.prototype = {
/**
* Attach to a process in order to get the form of a ChildProcessActor.
*
* @param string aId
* @param number aId
* The ID for the process to attach (returned by `listProcesses`).
* Connected to the main process if omitted, or is 0.
*/
attachProcess: function (aId) {
let packet = {
to: 'root',
type: 'attachProcess',
id: aId
to: "root",
type: "attachProcess"
}
if (typeof(aId) == "number") {
packet.id = aId;
}
return this.request(packet);
},

View File

@ -0,0 +1,185 @@
/* 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 } = require("chrome");
const Services = require("Services");
const { DebuggerServer } = require("../main");
const { getChildDocShells, TabActor } = require("./webbrowser");
const makeDebugger = require("./utils/make-debugger");
/**
* Creates a TabActor for debugging all the chrome content in the
* current process. Most of the implementation is inherited from TabActor.
* ChromeActor is a child of RootActor, it can be instanciated via
* RootActor.attachProcess request.
* ChromeActor exposes all tab actors via its form() request, like TabActor.
*
* History lecture:
* All tab actors used to also be registered as global actors,
* so that the root actor was also exposing tab actors for the main process.
* Tab actors ended up having RootActor as parent actor,
* but more and more features of the tab actors were relying on TabActor.
* So we are now exposing a process actor that offers the same API as TabActor
* by inheriting its functionality.
* Global actors are now only the actors that are meant to be global,
* and are no longer related to any specific scope/document.
*
* @param aConnection DebuggerServerConnection
* The connection to the client.
*/
function ChromeActor(aConnection) {
TabActor.call(this, aConnection);
// This creates a Debugger instance for chrome debugging all globals.
this.makeDebugger = makeDebugger.bind(null, {
findDebuggees: dbg => dbg.findAllGlobals(),
shouldAddNewGlobalAsDebuggee: () => true
});
// Ensure catching the creation of any new content docshell
this.listenForNewDocShells = true;
// Defines the default docshell selected for the tab actor
let window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
// Default to any available top level window if there is no expected window
// (for example when we open firefox with -webide argument)
if (!window) {
window = Services.wm.getMostRecentWindow(null);
}
// On xpcshell, there is no window/docshell
let docShell = window ? window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
: null;
Object.defineProperty(this, "docShell", {
value: docShell,
configurable: true
});
}
exports.ChromeActor = ChromeActor;
ChromeActor.prototype = Object.create(TabActor.prototype);
ChromeActor.prototype.constructor = ChromeActor;
ChromeActor.prototype.isRootActor = true;
/**
* Getter for the list of all docshells in this tabActor
* @return {Array}
*/
Object.defineProperty(ChromeActor.prototype, "docShells", {
get: function () {
// Iterate over all top-level windows and all their docshells.
let docShells = [];
let e = Services.ww.getWindowEnumerator();
while (e.hasMoreElements()) {
let window = e.getNext();
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docShells = docShells.concat(getChildDocShells(docShell));
}
return docShells;
}
});
ChromeActor.prototype.observe = function(aSubject, aTopic, aData) {
TabActor.prototype.observe.call(this, aSubject, aTopic, aData);
if (!this.attached) {
return;
}
if (aTopic == "chrome-webnavigation-create") {
aSubject.QueryInterface(Ci.nsIDocShell);
this._onDocShellCreated(aSubject);
} else if (aTopic == "chrome-webnavigation-destroy") {
this._onDocShellDestroy(aSubject);
}
}
ChromeActor.prototype._attach = function() {
if (this.attached) {
return false;
}
TabActor.prototype._attach.call(this);
// Listen for any new/destroyed chrome docshell
Services.obs.addObserver(this, "chrome-webnavigation-create", false);
Services.obs.addObserver(this, "chrome-webnavigation-destroy", false);
// Iterate over all top-level windows.
let docShells = [];
let e = Services.ww.getWindowEnumerator();
while (e.hasMoreElements()) {
let window = e.getNext();
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
if (docShell == this.docShell) {
continue;
}
this._progressListener.watch(docShell);
}
};
ChromeActor.prototype._detach = function() {
if (!this.attached) {
return false;
}
Services.obs.removeObserver(this, "chrome-webnavigation-create");
Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
// Iterate over all top-level windows.
let docShells = [];
let e = Services.ww.getWindowEnumerator();
while (e.hasMoreElements()) {
let window = e.getNext();
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
if (docShell == this.docShell) {
continue;
}
this._progressListener.unwatch(docShell);
}
TabActor.prototype._detach.call(this);
};
/* ThreadActor hooks. */
/**
* Prepare to enter a nested event loop by disabling debuggee events.
*/
ChromeActor.prototype.preNest = function() {
// Disable events in all open windows.
let e = Services.wm.getEnumerator(null);
while (e.hasMoreElements()) {
let win = e.getNext();
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.suppressEventHandling(true);
windowUtils.suspendTimeouts();
}
}
/**
* Prepare to exit a nested event loop by enabling debuggee events.
*/
ChromeActor.prototype.postNest = function(aNestData) {
// Enable events in all open windows.
let e = Services.wm.getEnumerator(null);
while (e.hasMoreElements()) {
let win = e.getNext();
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.resumeTimeouts();
windowUtils.suppressEventHandling(false);
}
}

View File

@ -10,9 +10,6 @@ const { Cc, Ci, Cu } = require("chrome");
const Services = require("Services");
const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");
const makeDebugger = require("./utils/make-debugger");
loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
loader.lazyGetter(this, "ppmm", () => {
return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
@ -99,14 +96,10 @@ function RootActor(aConnection, aParameters) {
this._onAddonListChanged = this.onAddonListChanged.bind(this);
this._extraActors = {};
// Map of DOM stylesheets to StyleSheetActors
this._styleSheetActors = new Map();
this._globalActorPool = new ActorPool(this.conn);
this.conn.addActorPool(this._globalActorPool);
// This creates a Debugger instance for chrome debugging all globals.
this.makeDebugger = makeDebugger.bind(null, {
findDebuggees: dbg => dbg.findAllGlobals(),
shouldAddNewGlobalAsDebuggee: () => true
});
this._chromeActor = null;
}
RootActor.prototype = {
@ -160,7 +153,16 @@ RootActor.prototype = {
getUsedFontFaces: true,
// Trait added in Gecko 38, indicating that all features necessary for
// grabbing allocations from the MemoryActor are available for the performance tool
memoryActorAllocations: true
memoryActorAllocations: true,
// Whether root actor exposes tab actors
// if allowChromeProcess is true, you can fetch a ChromeActor instance
// to debug chrome and any non-content ressource via attachProcess request
// if allocChromeProcess is defined, but not true, it means that root actor
// no longer expose tab actors, but also that attachProcess forbids
// exposing actors for security reasons
get allowChromeProcess() {
return DebuggerServer.allowChromeProcess;
},
},
/**
@ -176,66 +178,6 @@ RootActor.prototype = {
};
},
/**
* This is true for the root actor only, used by some child actors
*/
get isRootActor() true,
/**
* The (chrome) window, for use by child actors
*/
get window() isWorker ? null : Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType),
/**
* The list of all windows
*/
get windows() {
return this.docShells.map(docShell => {
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
});
},
/**
* URL of the chrome window.
*/
get url() { return this.window ? this.window.document.location.href : null; },
/**
* The top level window's docshell
*/
get docShell() {
return this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
},
/**
* The list of all docshells
*/
get docShells() {
let docShellsEnum = this.docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let docShells = [];
while (docShellsEnum.hasMoreElements()) {
docShells.push(docShellsEnum.getNext());
}
return docShells;
},
/**
* Getter for the best nsIWebProgress for to watching this window.
*/
get webProgress() {
return this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
},
/**
* Disconnects the actor from the browser window.
*/
@ -251,8 +193,6 @@ RootActor.prototype = {
this._parameters.onShutdown();
}
this._extraActors = null;
this._styleSheetActors.clear();
this._styleSheetActors = null;
},
/* The 'listTabs' request and the 'tabListChanged' notification. */
@ -380,13 +320,34 @@ RootActor.prototype = {
},
onAttachProcess: function (aRequest) {
let mm = ppmm.getChildAt(aRequest.id);
if (!mm) {
return { error: "noProcess",
message: "There is no process with id '" + aRequest.id + "'." };
if (!DebuggerServer.allowChromeProcess) {
return { error: "forbidden",
message: "You are not allowed to debug chrome." };
}
if (("id" in aRequest) && typeof(aRequest.id) != "number") {
return { error: "wrongParameter",
message: "attachProcess requires a valid `id` attribute." };
}
// If the request doesn't contains id parameter or id is 0
// (id == 0, based on onListProcesses implementation)
if ((!("id" in aRequest)) || aRequest.id === 0) {
if (!this._chromeActor) {
// Create a ChromeActor for the parent process
let { ChromeActor } = require("devtools/server/actors/chrome");
this._chromeActor = new ChromeActor(this.conn);
this._globalActorPool.addActor(this._chromeActor);
}
return { form: this._chromeActor.form() };
} else {
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 }));
}
return DebuggerServer.connectToContent(this.conn, mm)
.then(form => ({ form: form }));
},
/* This is not in the spec, but it's used by tests. */
@ -406,60 +367,6 @@ RootActor.prototype = {
_createExtraActors: createExtraActors,
_appendExtraActors: appendExtraActors,
/* ThreadActor hooks. */
/**
* Prepare to enter a nested event loop by disabling debuggee events.
*/
preNest: function() {
// Disable events in all open windows.
let e = Services.wm.getEnumerator(null);
while (e.hasMoreElements()) {
let win = e.getNext();
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.suppressEventHandling(true);
windowUtils.suspendTimeouts();
}
},
/**
* Prepare to exit a nested event loop by enabling debuggee events.
*/
postNest: function(aNestData) {
// Enable events in all open windows.
let e = Services.wm.getEnumerator(null);
while (e.hasMoreElements()) {
let win = e.getNext();
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.resumeTimeouts();
windowUtils.suppressEventHandling(false);
}
},
/**
* Create or return the StyleSheetActor for a style sheet. This method
* is here because the Style Editor and Inspector share style sheet actors.
*
* @param DOMStyleSheet styleSheet
* The style sheet to create an actor for.
* @return StyleSheetActor actor
* The actor for this style sheet.
*
*/
createStyleSheetActor: function(styleSheet) {
if (this._styleSheetActors.has(styleSheet)) {
return this._styleSheetActors.get(styleSheet);
}
let actor = new StyleSheetActor(styleSheet, this);
this._styleSheetActors.set(styleSheet, actor);
this._globalActorPool.addActor(actor);
return actor;
},
/**
* Remove the extra actor (added by DebuggerServer.addGlobalActor or
* DebuggerServer.addTabActor) name |aName|.

View File

@ -36,6 +36,35 @@ function getWindowID(window) {
.currentInnerWindowID;
}
function getDocShellChromeEventHandler(docShell) {
let handler = docShell.chromeEventHandler;
if (!handler) {
try {
// toplevel xul window's docshell doesn't have chromeEventHandler attribute
// the chrome event handler is just the global window object
handler = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
} catch(e) {}
}
return handler;
}
function getChildDocShells(docShell) {
let docShellsEnum = docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let docShells = [];
while (docShellsEnum.hasMoreElements()) {
let docShell = docShellsEnum.getNext();
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
docShells.push(docShell);
}
return docShells;
}
exports.getChildDocShells = getChildDocShells;
/**
* Browser-specific actors.
*/
@ -580,6 +609,10 @@ function TabActor(aConnection)
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
});
// Flag eventually overloaded by sub classes in order to watch new docshells
// Used on b2g to catch activity frames and in chrome to list all frames
this.listenForNewDocShells = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
this.traits = { reconfigure: true, frames: true };
}
@ -607,10 +640,7 @@ TabActor.prototype = {
* An object on which listen for DOMWindowCreated and pageshow events.
*/
get chromeEventHandler() {
// TODO: bug 992778, fix docShell.chromeEventHandler in child processes
return this.docShell.chromeEventHandler ||
this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
return getDocShellChromeEventHandler(this.docShell);
},
/**
@ -634,29 +664,20 @@ TabActor.prototype = {
* @return {Array}
*/
get docShells() {
let docShellsEnum = this.docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let docShells = [];
while (docShellsEnum.hasMoreElements()) {
let docShell = docShellsEnum.getNext();
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
docShells.push(docShell);
}
return docShells;
return getChildDocShells(this.docShell);
},
/**
* Getter for the tab content's DOM window.
*/
get window() {
return this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
// On xpcshell, there is no document
if (this.docShell) {
return this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
return null;
},
/**
@ -761,17 +782,21 @@ TabActor.prototype = {
dbg_assert(this.actorID,
"tab should have an actorID.");
let windowUtils = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let response = {
actor: this.actorID,
title: this.title,
url: this.url,
outerWindowID: windowUtils.outerWindowID
actor: this.actorID
};
// On xpcshell we are using tabactor even if there is no valid document.
// Actors like chrome debugger can work.
if (this.window) {
response.title = this.title;
response.url = this.url;
let windowUtils = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
response.outerWindowID = windowUtils.outerWindowID;
}
// Walk over tab actors added by extensions and add them to a new ActorPool.
let actorPool = new ActorPool(this.conn);
this._createExtraActors(DebuggerServer.tabActorFactories, actorPool);
@ -869,21 +894,24 @@ TabActor.prototype = {
// ... and a pool for context-lifetime actors.
this._pushContext();
this._progressListener = new DebuggerProgressListener(this);
// on xpcshell, there is no document
if (this.window) {
this._progressListener = new DebuggerProgressListener(this);
// Save references to the original document we attached to
this._originalWindow = this.window;
// Save references to the original document we attached to
this._originalWindow = this.window;
// Ensure replying to attach() request first
// before notifying about new docshells.
DevToolsUtils.executeSoon(() => this._watchDocshells());
// Ensure replying to attach() request first
// before notifying about new docshells.
DevToolsUtils.executeSoon(() => this._watchDocshells());
}
this._attached = true;
},
_watchDocshells: function BTA_watchDocshells() {
// In child processes, we watch all docshells living in the process.
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
if (this.listenForNewDocShells) {
Services.obs.addObserver(this, "webnavigation-create", false);
}
Services.obs.addObserver(this, "webnavigation-destroy", false);
@ -927,28 +955,36 @@ TabActor.prototype = {
}
if (aTopic == "webnavigation-create") {
aSubject.QueryInterface(Ci.nsIDocShell);
// webnavigation-create is fired very early during docshell construction.
// In new root docshells within child processes, involving TabChild,
// this event is from within this call:
// http://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l912
// whereas the chromeEventHandler (and most likely other stuff) is set later:
// http://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l944
// So wait a tick before watching it:
DevToolsUtils.executeSoon(() => {
// In child processes, we have new root docshells,
// let's watch them and all their child docshells.
if (this._isRootDocShell(aSubject)) {
this._progressListener.watch(aSubject);
}
this._notifyDocShellsUpdate([aSubject]);
});
this._onDocShellCreated(aSubject);
} else if (aTopic == "webnavigation-destroy") {
let webProgress = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
this._notifyDocShellDestroy(webProgress);
this._onDocShellDestroy(aSubject);
}
},
_onDocShellCreated: function (docShell) {
// (chrome-)webnavigation-create is fired very early during docshell construction.
// In new root docshells within child processes, involving TabChild,
// this event is from within this call:
// http://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l912
// whereas the chromeEventHandler (and most likely other stuff) is set later:
// http://hg.mozilla.org/mozilla-central/annotate/74d7fb43bb44/dom/ipc/TabChild.cpp#l944
// So wait a tick before watching it:
DevToolsUtils.executeSoon(() => {
// In child processes, we have new root docshells,
// let's watch them and all their child docshells.
if (this._isRootDocShell(docShell)) {
this._progressListener.watch(docShell);
}
this._notifyDocShellsUpdate([docShell]);
});
},
_onDocShellDestroy: function (docShell) {
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
this._notifyDocShellDestroy(webProgress);
},
_isRootDocShell: function (docShell) {
// Root docshells like top level xul windows don't have chromeEventHandler.
// Root docshells in child processes have one, it is TabChildGlobal,
@ -962,7 +998,9 @@ TabActor.prototype = {
// Convert docShell list to windows objects list being sent to the client
_docShellsToWindows: function (docshells) {
return docshells.map(docShell => {
let window = docShell.DOMWindow;
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
let window = webProgress.DOMWindow;
let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
@ -997,6 +1035,7 @@ TabActor.prototype = {
},
_notifyDocShellDestroy: function (webProgress) {
webProgress = webProgress.QueryInterface(Ci.nsIWebProgress);
let id = webProgress.DOMWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
@ -1096,15 +1135,17 @@ TabActor.prototype = {
if (this.docShell) {
this._progressListener.unwatch(this.docShell);
}
this._progressListener.destroy();
this._progressListener = null;
this._originalWindow = null;
if (this._processListener) {
this._progressListener.destroy();
this._progressListener = null;
this._originalWindow = null;
// Removes the observers being set in _watchDocShells
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
Services.obs.removeObserver(this, "webnavigation-create", false);
// Removes the observers being set in _watchDocShells
if (this.listenForNewDocShells) {
Services.obs.removeObserver(this, "webnavigation-create");
}
Services.obs.removeObserver(this, "webnavigation-destroy");
}
Services.obs.removeObserver(this, "webnavigation-destroy", false);
this._popContext();
@ -1304,9 +1345,11 @@ TabActor.prototype = {
this._windowDestroyed(this.window, null, true);
DevToolsUtils.executeSoon(() => {
this._setWindow(window);
// Immediately change the window as this window, if in process of unload
// may already be non working on the next cycle and start throwing
this._setWindow(window);
DevToolsUtils.executeSoon(() => {
// Then fake window-ready and navigate on the given document
this._windowReady(window, true);
DevToolsUtils.executeSoon(() => {
@ -2018,11 +2061,7 @@ DebuggerProgressListener.prototype = {
Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
// TODO: fix docShell.chromeEventHandler in child processes!
let handler = docShell.chromeEventHandler ||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
let handler = getDocShellChromeEventHandler(docShell);
handler.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
handler.addEventListener("pageshow", this._onWindowCreated, true);
handler.addEventListener("pagehide", this._onWindowHidden, true);
@ -2042,11 +2081,7 @@ DebuggerProgressListener.prototype = {
webProgress.removeProgressListener(this);
} catch(e) {}
// TODO: fix docShell.chromeEventHandler in child processes!
let handler = docShell.chromeEventHandler ||
docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
let handler = getDocShellChromeEventHandler(docShell);
handler.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
handler.removeEventListener("pageshow", this._onWindowCreated, true);
handler.removeEventListener("pagehide", this._onWindowHidden, true);
@ -2057,18 +2092,10 @@ DebuggerProgressListener.prototype = {
},
_getWindowsInDocShell: function(docShell) {
let docShellsEnum = docShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeAll,
Ci.nsIDocShell.ENUMERATE_FORWARDS
);
let windows = [];
while (docShellsEnum.hasMoreElements()) {
let w = docShellsEnum.getNext().QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
windows.push(w);
}
return windows;
return getChildDocShells(docShell).map(d => {
return d.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
});
},
onWindowCreated: DevToolsUtils.makeInfallible(function(evt) {

View File

@ -1503,6 +1503,9 @@ WebConsoleActor.prototype =
// This method is called after this.window is changed,
// so we register new listener on this new window
this.onStartListeners({listeners: listeners});
// Also reset the cached top level chrome window being targeted
this._lastChromeWindow = null;
},
};

View File

@ -165,6 +165,11 @@ var DebuggerServer = {
*/
chromeWindowType: null,
/**
* Allow debugging chrome of (parent or child) processes.
*/
allowChromeProcess: false,
/**
* Initialize the debugger server.
*/
@ -366,8 +371,6 @@ var DebuggerServer = {
if (!restrictPrivileges) {
this.addTabActors();
let { ChromeDebuggerActor } = require("devtools/server/actors/script");
this.addGlobalActor(ChromeDebuggerActor, "chromeDebugger");
this.registerModule("devtools/server/actors/preference", {
prefix: "preference",
constructor: "PreferenceActor",
@ -430,12 +433,12 @@ var DebuggerServer = {
this.registerModule("devtools/server/actors/webconsole", {
prefix: "console",
constructor: "WebConsoleActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/inspector", {
prefix: "inspector",
constructor: "InspectorActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/call-watcher", {
prefix: "callWatcher",
@ -445,27 +448,27 @@ var DebuggerServer = {
this.registerModule("devtools/server/actors/canvas", {
prefix: "canvas",
constructor: "CanvasActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/webgl", {
prefix: "webgl",
constructor: "WebGLActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/webaudio", {
prefix: "webaudio",
constructor: "WebAudioActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/stylesheets", {
prefix: "styleSheets",
constructor: "StyleSheetsActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/styleeditor", {
prefix: "styleEditor",
constructor: "StyleEditorActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/storage", {
prefix: "storage",
@ -485,17 +488,17 @@ var DebuggerServer = {
this.registerModule("devtools/server/actors/memory", {
prefix: "memory",
constructor: "MemoryActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/framerate", {
prefix: "framerate",
constructor: "FramerateActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/eventlooplag", {
prefix: "eventLoopLag",
constructor: "EventLoopLagActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/layout", {
prefix: "reflow",
@ -505,17 +508,17 @@ var DebuggerServer = {
this.registerModule("devtools/server/actors/csscoverage", {
prefix: "cssUsage",
constructor: "CSSUsageActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/monitor", {
prefix: "monitor",
constructor: "MonitorActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/timeline", {
prefix: "timeline",
constructor: "TimelineActor",
type: { global: true, tab: true }
type: { tab: true }
});
this.registerModule("devtools/server/actors/director-manager", {
prefix: "directorManager",
@ -526,13 +529,13 @@ var DebuggerServer = {
this.registerModule("devtools/server/actors/profiler", {
prefix: "profiler",
constructor: "ProfilerActor",
type: { global: true, tab: true }
type: { tab: true }
});
}
this.registerModule("devtools/server/actors/animation", {
prefix: "animations",
constructor: "AnimationsActor",
type: { global: true, tab: true }
type: { tab: true }
});
},

View File

@ -39,6 +39,7 @@ EXTRA_JS_MODULES.devtools.server.actors += [
'actors/canvas.js',
'actors/child-process.js',
'actors/childtab.js',
'actors/chrome.js',
'actors/common.js',
'actors/csscoverage.js',
'actors/device.js',