зеркало из https://github.com/mozilla/gecko-dev.git
Bug 717749 - Part 2: Hook up the debugger to the slow script debug service. (r=past)
This commit is contained in:
Родитель
b02d702198
Коммит
48684ab5b7
|
@ -462,6 +462,13 @@ ThreadState.prototype = {
|
|||
* Update the UI after a thread state change.
|
||||
*/
|
||||
_update: function(aEvent) {
|
||||
// Ignore "interrupted" events, which are generated by the slow script
|
||||
// dialog and internal events such as setting breakpoints, to avoid UI
|
||||
// flicker.
|
||||
if (aEvent == "interrupted") {
|
||||
return;
|
||||
}
|
||||
|
||||
DebuggerView.Toolbar.toggleResumeButtonState(this.activeThread.state);
|
||||
|
||||
if (gTarget && (aEvent == "paused" || aEvent == "resumed")) {
|
||||
|
@ -568,6 +575,11 @@ StackFrames.prototype = {
|
|||
this._currentReturnedValue = aPacket.why.frameFinished.return;
|
||||
}
|
||||
break;
|
||||
// If paused by an explicit interrupt, which are generated by the slow
|
||||
// script dialog and internal events such as setting breakpoints, ignore
|
||||
// the event to avoid UI flicker.
|
||||
case "interrupted":
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
|
||||
|
|
|
@ -580,6 +580,79 @@ let gDevToolsBrowser = {
|
|||
mainKeyset.parentNode.insertBefore(devtoolsKeyset, mainKeyset);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hook the JS debugger tool to the "Debug Script" button of the slow script
|
||||
* dialog.
|
||||
*/
|
||||
setSlowScriptDebugHandler: function DT_setSlowScriptDebugHandler() {
|
||||
let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
|
||||
.getService(Ci.nsISlowScriptDebug);
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
|
||||
debugService.activationHandler = function(aWindow) {
|
||||
let chromeWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
let target = devtools.TargetFactory.forTab(chromeWindow.gBrowser.selectedTab);
|
||||
|
||||
let setupFinished = false;
|
||||
gDevTools.showToolbox(target, "jsdebugger").then(toolbox => {
|
||||
let threadClient = toolbox.getCurrentPanel().panelWin.gThreadClient;
|
||||
|
||||
// Break in place, which means resuming the debuggee thread and pausing
|
||||
// right before the next step happens.
|
||||
switch (threadClient.state) {
|
||||
case "paused":
|
||||
// When the debugger is already paused.
|
||||
threadClient.breakOnNext();
|
||||
setupFinished = true;
|
||||
break;
|
||||
case "attached":
|
||||
// When the debugger is already open.
|
||||
threadClient.interrupt(() => {
|
||||
threadClient.breakOnNext();
|
||||
setupFinished = true;
|
||||
});
|
||||
break;
|
||||
case "resuming":
|
||||
// The debugger is newly opened.
|
||||
threadClient.addOneTimeListener("resumed", () => {
|
||||
threadClient.interrupt(() => {
|
||||
threadClient.breakOnNext();
|
||||
setupFinished = true;
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw Error("invalid thread client state in slow script debug handler: " +
|
||||
threadClient.state);
|
||||
}
|
||||
});
|
||||
|
||||
// Don't return from the interrupt handler until the debugger is brought
|
||||
// up; no reason to continue executing the slow script.
|
||||
let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
utils.enterModalState();
|
||||
while (!setupFinished) {
|
||||
tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
utils.leaveModalState();
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Unset the slow script debug handler.
|
||||
*/
|
||||
unsetSlowScriptDebugHandler: function DT_unsetSlowScriptDebugHandler() {
|
||||
let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
|
||||
.getService(Ci.nsISlowScriptDebug);
|
||||
debugService.activationHandler = undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Detect the presence of a Firebug.
|
||||
|
@ -669,6 +742,10 @@ let gDevToolsBrowser = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toolDefinition.id === "jsdebugger") {
|
||||
gDevToolsBrowser.setSlowScriptDebugHandler();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -844,6 +921,10 @@ let gDevToolsBrowser = {
|
|||
for (let win of gDevToolsBrowser._trackedBrowserWindows) {
|
||||
gDevToolsBrowser._removeToolFromMenu(toolId, win.document);
|
||||
}
|
||||
|
||||
if (toolId === "jsdebugger") {
|
||||
gDevToolsBrowser.unsetSlowScriptDebugHandler();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -1493,6 +1493,16 @@ ThreadClient.prototype = {
|
|||
this._doResume(null, aOnResponse);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resume then pause without stepping.
|
||||
*
|
||||
* @param function aOnResponse
|
||||
* Called with the response packet.
|
||||
*/
|
||||
breakOnNext: function (aOnResponse) {
|
||||
this._doResume({ type: "break" }, aOnResponse);
|
||||
},
|
||||
|
||||
/**
|
||||
* Step over a function call.
|
||||
*
|
||||
|
|
|
@ -951,7 +951,15 @@ ThreadActor.prototype = {
|
|||
},
|
||||
|
||||
_makeOnStep: function ({ thread, pauseAndRespond, startFrame,
|
||||
startLocation }) {
|
||||
startLocation, steppingType }) {
|
||||
// Breaking in place: we should always pause.
|
||||
if (steppingType === "break") {
|
||||
return function () {
|
||||
return pauseAndRespond(this);
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise take what a "step" means into consideration.
|
||||
return function () {
|
||||
// onStep is called with 'this' set to the current frame.
|
||||
|
||||
|
@ -996,7 +1004,7 @@ ThreadActor.prototype = {
|
|||
/**
|
||||
* Define the JS hook functions for stepping.
|
||||
*/
|
||||
_makeSteppingHooks: function (aStartLocation) {
|
||||
_makeSteppingHooks: function (aStartLocation, steppingType) {
|
||||
// Bind these methods and state because some of the hooks are called
|
||||
// with 'this' set to the current frame. Rather than repeating the
|
||||
// binding in each _makeOnX method, just do it once here and pass it
|
||||
|
@ -1008,7 +1016,8 @@ ThreadActor.prototype = {
|
|||
createValueGrip: this.createValueGrip.bind(this),
|
||||
thread: this,
|
||||
startFrame: this.youngestFrame,
|
||||
startLocation: aStartLocation
|
||||
startLocation: aStartLocation,
|
||||
steppingType: steppingType
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -1029,7 +1038,7 @@ ThreadActor.prototype = {
|
|||
*/
|
||||
_handleResumeLimit: function (aRequest) {
|
||||
let steppingType = aRequest.resumeLimit.type;
|
||||
if (["step", "next", "finish"].indexOf(steppingType) == -1) {
|
||||
if (["break", "step", "next", "finish"].indexOf(steppingType) == -1) {
|
||||
return reject({ error: "badParameterType",
|
||||
message: "Unknown resumeLimit type" });
|
||||
}
|
||||
|
@ -1037,7 +1046,8 @@ ThreadActor.prototype = {
|
|||
const generatedLocation = getFrameLocation(this.youngestFrame);
|
||||
return this.sources.getOriginalLocation(generatedLocation)
|
||||
.then(originalLocation => {
|
||||
const { onEnterFrame, onPop, onStep } = this._makeSteppingHooks(originalLocation);
|
||||
const { onEnterFrame, onPop, onStep } = this._makeSteppingHooks(originalLocation,
|
||||
steppingType);
|
||||
|
||||
// Make sure there is still a frame on the stack if we are to continue
|
||||
// stepping.
|
||||
|
@ -1047,6 +1057,7 @@ ThreadActor.prototype = {
|
|||
case "step":
|
||||
this.dbg.onEnterFrame = onEnterFrame;
|
||||
// Fall through.
|
||||
case "break":
|
||||
case "next":
|
||||
if (stepFrame.script) {
|
||||
stepFrame.onStep = onStep;
|
||||
|
|
Загрузка…
Ссылка в новой задаче