mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 991797 - convert most of the debugger frontend to use Task.jsm and fix discovered async errors r=victorporof
This commit is contained in:
parent
7c96c27167
commit
5c2407243c
@ -44,9 +44,12 @@ const EVENTS = {
|
||||
BREAKPOINT_ADDED: "Debugger:BreakpointAdded",
|
||||
BREAKPOINT_REMOVED: "Debugger:BreakpointRemoved",
|
||||
|
||||
// When a breakpoint has been shown or hidden in the source editor.
|
||||
BREAKPOINT_SHOWN: "Debugger:BreakpointShown",
|
||||
BREAKPOINT_HIDDEN: "Debugger:BreakpointHidden",
|
||||
// When a breakpoint has been shown or hidden in the source editor
|
||||
// or the pane.
|
||||
BREAKPOINT_SHOWN_IN_EDITOR: "Debugger:BreakpointShownInEditor",
|
||||
BREAKPOINT_SHOWN_IN_PANE: "Debugger:BreakpointShownInPane",
|
||||
BREAKPOINT_HIDDEN_IN_EDITOR: "Debugger:BreakpointHiddenInEditor",
|
||||
BREAKPOINT_HIDDEN_IN_PANE: "Debugger:BreakpointHiddenInPane",
|
||||
|
||||
// When a conditional breakpoint's popup is showing or hiding.
|
||||
CONDITIONAL_BREAKPOINT_POPUP_SHOWING: "Debugger:ConditionalBreakpointPopupShowing",
|
||||
@ -68,6 +71,9 @@ const EVENTS = {
|
||||
GLOBAL_SEARCH_MATCH_FOUND: "Debugger:GlobalSearch:MatchFound",
|
||||
GLOBAL_SEARCH_MATCH_NOT_FOUND: "Debugger:GlobalSearch:MatchNotFound",
|
||||
|
||||
// After the the StackFrames object has been filled with frames
|
||||
AFTER_FRAMES_REFILLED: "Debugger:AfterFramesRefilled",
|
||||
|
||||
// After the stackframes are cleared and debugger won't pause anymore.
|
||||
AFTER_FRAMES_CLEARED: "Debugger:AfterFramesCleared",
|
||||
|
||||
@ -90,7 +96,6 @@ const FRAME_TYPE = {
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource:///modules/devtools/SimpleListWidget.jsm");
|
||||
Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
|
||||
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
|
||||
@ -105,6 +110,9 @@ const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
const FastListWidget = require("devtools/shared/widgets/FastListWidget");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Parser",
|
||||
"resource:///modules/devtools/Parser.jsm");
|
||||
|
||||
@ -147,13 +155,14 @@ let DebuggerController = {
|
||||
* @return object
|
||||
* A promise that is resolved when the debugger finishes startup.
|
||||
*/
|
||||
startupDebugger: function() {
|
||||
startupDebugger: Task.async(function*() {
|
||||
if (this._startup) {
|
||||
return this._startup;
|
||||
return;
|
||||
}
|
||||
|
||||
return this._startup = DebuggerView.initialize();
|
||||
},
|
||||
yield DebuggerView.initialize();
|
||||
this._startup = true;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Destroys the view and disconnects the debugger client from the server.
|
||||
@ -161,20 +170,20 @@ let DebuggerController = {
|
||||
* @return object
|
||||
* A promise that is resolved when the debugger finishes shutdown.
|
||||
*/
|
||||
shutdownDebugger: function() {
|
||||
shutdownDebugger: Task.async(function*() {
|
||||
if (this._shutdown) {
|
||||
return this._shutdown;
|
||||
return;
|
||||
}
|
||||
|
||||
return this._shutdown = DebuggerView.destroy().then(() => {
|
||||
DebuggerView.destroy();
|
||||
this.SourceScripts.disconnect();
|
||||
this.StackFrames.disconnect();
|
||||
this.ThreadState.disconnect();
|
||||
this.Tracer.disconnect();
|
||||
this.disconnect();
|
||||
});
|
||||
},
|
||||
yield DebuggerView.destroy();
|
||||
this.SourceScripts.disconnect();
|
||||
this.StackFrames.disconnect();
|
||||
this.ThreadState.disconnect();
|
||||
this.Tracer.disconnect();
|
||||
this.disconnect();
|
||||
|
||||
this._shutdown = true;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Initiates remote debugging based on the current target, wiring event
|
||||
@ -183,14 +192,11 @@ let DebuggerController = {
|
||||
* @return object
|
||||
* A promise that is resolved when the debugger finishes connecting.
|
||||
*/
|
||||
connect: function() {
|
||||
if (this._connection) {
|
||||
return this._connection;
|
||||
connect: Task.async(function*() {
|
||||
if (this._connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
let startedDebugging = promise.defer();
|
||||
this._connection = startedDebugging.promise;
|
||||
|
||||
let target = this._target;
|
||||
let { client, form: { chromeDebugger, traceActor, addonActor } } = target;
|
||||
target.on("close", this._onTabDetached);
|
||||
@ -199,23 +205,17 @@ let DebuggerController = {
|
||||
this.client = client;
|
||||
|
||||
if (addonActor) {
|
||||
this._startAddonDebugging(addonActor, startedDebugging.resolve);
|
||||
yield this._startAddonDebugging(addonActor);
|
||||
} else if (target.chrome) {
|
||||
this._startChromeDebugging(chromeDebugger, startedDebugging.resolve);
|
||||
yield this._startChromeDebugging(chromeDebugger);
|
||||
} else {
|
||||
this._startDebuggingTab(startedDebugging.resolve);
|
||||
const startedTracing = promise.defer();
|
||||
yield this._startDebuggingTab();
|
||||
|
||||
if (Prefs.tracerEnabled && traceActor) {
|
||||
this._startTracingTab(traceActor, startedTracing.resolve);
|
||||
} else {
|
||||
startedTracing.resolve();
|
||||
yield this._startTracingTab(traceActor);
|
||||
}
|
||||
|
||||
return promise.all([startedDebugging.promise, startedTracing.promise]);
|
||||
}
|
||||
|
||||
return startedDebugging.promise;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Disconnects the debugger client and removes event handlers as necessary.
|
||||
@ -226,7 +226,7 @@ let DebuggerController = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._connection = null;
|
||||
this._connected = false;
|
||||
this.client = null;
|
||||
this.activeThread = null;
|
||||
},
|
||||
@ -286,30 +286,33 @@ let DebuggerController = {
|
||||
/**
|
||||
* Sets up a debugging session.
|
||||
*
|
||||
* @param function aCallback
|
||||
* A function to invoke once the client attaches to the active thread.
|
||||
* @return object
|
||||
* A promise resolved once the client attaches to the active thread.
|
||||
*/
|
||||
_startDebuggingTab: function(aCallback) {
|
||||
this._target.activeTab.attachThread({
|
||||
_startDebuggingTab: function() {
|
||||
let deferred = promise.defer();
|
||||
let threadOptions = {
|
||||
useSourceMaps: Prefs.sourceMapsEnabled
|
||||
}, (aResponse, aThreadClient) => {
|
||||
};
|
||||
|
||||
this._target.activeTab.attachThread(threadOptions, (aResponse, aThreadClient) => {
|
||||
if (!aThreadClient) {
|
||||
Cu.reportError("Couldn't attach to thread: " + aResponse.error);
|
||||
deferred.reject(new Error("Couldn't attach to thread: " + aResponse.error));
|
||||
return;
|
||||
}
|
||||
this.activeThread = aThreadClient;
|
||||
|
||||
this.ThreadState.connect();
|
||||
this.StackFrames.connect();
|
||||
this.SourceScripts.connect();
|
||||
|
||||
if (aThreadClient.paused) {
|
||||
aThreadClient.resume(this._ensureResumptionOrder);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -317,13 +320,17 @@ let DebuggerController = {
|
||||
*
|
||||
* @param object aAddonActor
|
||||
* The actor for the addon that is being debugged.
|
||||
* @param function aCallback
|
||||
* A function to invoke once the client attaches to the active thread.
|
||||
* @return object
|
||||
* A promise resolved once the client attaches to the active thread.
|
||||
*/
|
||||
_startAddonDebugging: function(aAddonActor, aCallback) {
|
||||
this.client.attachAddon(aAddonActor, (aResponse) => {
|
||||
return this._startChromeDebugging(aResponse.threadActor, aCallback);
|
||||
_startAddonDebugging: function(aAddonActor) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
this.client.attachAddon(aAddonActor, aResponse => {
|
||||
this._startChromeDebugging(aResponse.threadActor).then(deferred.resolve);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -331,28 +338,33 @@ let DebuggerController = {
|
||||
*
|
||||
* @param object aChromeDebugger
|
||||
* The remote protocol grip of the chrome debugger.
|
||||
* @param function aCallback
|
||||
* A function to invoke once the client attaches to the active thread.
|
||||
* @return object
|
||||
* A promise resolved once the client attaches to the active thread.
|
||||
*/
|
||||
_startChromeDebugging: function(aChromeDebugger, aCallback) {
|
||||
_startChromeDebugging: function(aChromeDebugger) {
|
||||
let deferred = promise.defer();
|
||||
let threadOptions = {
|
||||
useSourceMaps: Prefs.sourceMapsEnabled
|
||||
};
|
||||
|
||||
this.client.attachThread(aChromeDebugger, (aResponse, aThreadClient) => {
|
||||
if (!aThreadClient) {
|
||||
Cu.reportError("Couldn't attach to thread: " + aResponse.error);
|
||||
deferred.reject(new Error("Couldn't attach to thread: " + aResponse.error));
|
||||
return;
|
||||
}
|
||||
this.activeThread = aThreadClient;
|
||||
|
||||
this.ThreadState.connect();
|
||||
this.StackFrames.connect();
|
||||
this.SourceScripts.connect();
|
||||
|
||||
if (aThreadClient.paused) {
|
||||
aThreadClient.resume(this._ensureResumptionOrder);
|
||||
}
|
||||
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
}, { useSourceMaps: Prefs.sourceMapsEnabled });
|
||||
deferred.resolve();
|
||||
}, threadOptions);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -360,24 +372,24 @@ let DebuggerController = {
|
||||
*
|
||||
* @param object aTraceActor
|
||||
* The remote protocol grip of the trace actor.
|
||||
* @param function aCallback
|
||||
* A function to invoke once the client attaches to the tracer.
|
||||
* @return object
|
||||
* A promise resolved once the client attaches to the tracer.
|
||||
*/
|
||||
_startTracingTab: function(aTraceActor, aCallback) {
|
||||
_startTracingTab: function(aTraceActor) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
this.client.attachTracer(aTraceActor, (response, traceClient) => {
|
||||
if (!traceClient) {
|
||||
DevToolsUtils.reportException("DebuggerController._startTracingTab",
|
||||
new Error("Failed to attach to tracing actor."));
|
||||
deferred.reject(new Error("Failed to attach to tracing actor."));
|
||||
return;
|
||||
}
|
||||
|
||||
this.traceClient = traceClient;
|
||||
this.Tracer.connect();
|
||||
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -405,9 +417,9 @@ let DebuggerController = {
|
||||
});
|
||||
},
|
||||
|
||||
_startup: null,
|
||||
_shutdown: null,
|
||||
_connection: null,
|
||||
_startup: false,
|
||||
_shutdown: false,
|
||||
_connected: false,
|
||||
client: null,
|
||||
activeThread: null
|
||||
};
|
||||
@ -587,79 +599,21 @@ StackFrames.prototype = {
|
||||
/**
|
||||
* Handler for the thread client's framesadded notification.
|
||||
*/
|
||||
_onFrames: function() {
|
||||
_onFrames: Task.async(function*() {
|
||||
// Ignore useless notifications.
|
||||
if (!this.activeThread || !this.activeThread.cachedFrames.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let waitForNextPause = false;
|
||||
let breakLocation = this._currentBreakpointLocation;
|
||||
let watchExpressions = this._currentWatchExpressions;
|
||||
let client = DebuggerController.activeThread.client;
|
||||
|
||||
// We moved conditional breakpoint handling to the server, but
|
||||
// need to support it in the client for a while until most of the
|
||||
// server code in production is updated with it. bug 990137 is
|
||||
// filed to mark this code to be removed.
|
||||
if (!client.mainRoot.traits.conditionalBreakpoints) {
|
||||
// Conditional breakpoints are { breakpoint, expression } tuples. The
|
||||
// boolean evaluation of the expression decides if the active thread
|
||||
// automatically resumes execution or not.
|
||||
if (breakLocation) {
|
||||
// Make sure a breakpoint actually exists at the specified url and line.
|
||||
let breakpointPromise = DebuggerController.Breakpoints._getAdded(breakLocation);
|
||||
if (breakpointPromise) {
|
||||
breakpointPromise.then(({ conditionalExpression: e }) => { if (e) {
|
||||
// Evaluating the current breakpoint's conditional expression will
|
||||
// cause the stack frames to be cleared and active thread to pause,
|
||||
// sending a 'clientEvaluated' packed and adding the frames again.
|
||||
this.evaluate(e, { depth: 0, meta: FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL });
|
||||
waitForNextPause = true;
|
||||
}});
|
||||
}
|
||||
}
|
||||
// We'll get our evaluation of the current breakpoint's conditional
|
||||
// expression the next time the thread client pauses...
|
||||
if (waitForNextPause) {
|
||||
return;
|
||||
}
|
||||
if (this._currentFrameDescription == FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL) {
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
// If the breakpoint's conditional expression evaluation is falsy,
|
||||
// automatically resume execution.
|
||||
if (VariablesView.isFalsy({ value: this._currentEvaluation.return })) {
|
||||
this.activeThread.resume(DebuggerController._ensureResumptionOrder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Watch expressions are evaluated in the context of the topmost frame,
|
||||
// and the results are displayed in the variables view.
|
||||
// TODO: handle all of this server-side: Bug 832470, comment 14.
|
||||
if (watchExpressions) {
|
||||
// Evaluation causes the stack frames to be cleared and active thread to
|
||||
// pause, sending a 'clientEvaluated' packet and adding the frames again.
|
||||
this.evaluate(watchExpressions, { depth: 0, meta: FRAME_TYPE.WATCH_EXPRESSIONS_EVAL });
|
||||
waitForNextPause = true;
|
||||
}
|
||||
// We'll get our evaluation of the current watch expressions the next time
|
||||
// the thread client pauses...
|
||||
if (waitForNextPause) {
|
||||
if (this._currentFrameDescription != FRAME_TYPE.NORMAL &&
|
||||
this._currentFrameDescription != FRAME_TYPE.PUBLIC_CLIENT_EVAL) {
|
||||
return;
|
||||
}
|
||||
if (this._currentFrameDescription == FRAME_TYPE.WATCH_EXPRESSIONS_EVAL) {
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
// If an error was thrown during the evaluation of the watch expressions,
|
||||
// then at least one expression evaluation could not be performed. So
|
||||
// remove the most recent watch expression and try again.
|
||||
if (this._currentEvaluation.throw) {
|
||||
DebuggerView.WatchExpressions.removeAt(0);
|
||||
DebuggerController.StackFrames.syncWatchExpressions();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove all of this deprecated code: Bug 990137.
|
||||
yield this._handleConditionalBreakpoint();
|
||||
|
||||
// TODO: handle all of this server-side: Bug 832470, comment 14.
|
||||
yield this._handleWatchExpressions();
|
||||
|
||||
// Make sure the debugger view panes are visible, then refill the frames.
|
||||
DebuggerView.showInstrumentsPane();
|
||||
@ -669,7 +623,7 @@ StackFrames.prototype = {
|
||||
if (this._currentFrameDescription != FRAME_TYPE.NORMAL) {
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Fill the StackFrames view with the frames we have in the cache, compressing
|
||||
@ -678,7 +632,6 @@ StackFrames.prototype = {
|
||||
_refillFrames: function() {
|
||||
// Make sure all the previous stackframes are removed before re-adding them.
|
||||
DebuggerView.StackFrames.empty();
|
||||
|
||||
for (let frame of this.activeThread.cachedFrames) {
|
||||
let { depth, where: { url, line }, source } = frame;
|
||||
let isBlackBoxed = source ? this.activeThread.source(source).isBlackBoxed : false;
|
||||
@ -689,6 +642,8 @@ StackFrames.prototype = {
|
||||
|
||||
DebuggerView.StackFrames.selectedDepth = Math.max(this.currentFrameDepth, 0);
|
||||
DebuggerView.StackFrames.dirty = this.activeThread.moreFrames;
|
||||
|
||||
window.emit(EVENTS.AFTER_FRAMES_REFILLED);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -731,14 +686,16 @@ StackFrames.prototype = {
|
||||
* Handler for the debugger's prettyprintchange notification.
|
||||
*/
|
||||
_onPrettyPrintChange: function() {
|
||||
if (this.activeThread.state != "paused") {
|
||||
return;
|
||||
}
|
||||
// Makes sure the selected source remains selected
|
||||
// after the fillFrames is called.
|
||||
const source = DebuggerView.Sources.selectedValue;
|
||||
if (this.activeThread.state == "paused") {
|
||||
this.activeThread.fillFrames(
|
||||
CALL_STACK_PAGE_SIZE,
|
||||
() => DebuggerView.Sources.selectedValue = source);
|
||||
}
|
||||
|
||||
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE, () => {
|
||||
DebuggerView.Sources.selectedValue = source;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -926,6 +883,87 @@ StackFrames.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles conditional breakpoints when the debugger pauses and the
|
||||
* stackframes are received.
|
||||
*
|
||||
* We moved conditional breakpoint handling to the server, but
|
||||
* need to support it in the client for a while until most of the
|
||||
* server code in production is updated with it.
|
||||
* TODO: remove all of this deprecated code: Bug 990137.
|
||||
*
|
||||
* @return object
|
||||
* A promise that is resolved after a potential breakpoint's
|
||||
* conditional expression is evaluated. If there's no breakpoint
|
||||
* where the debugger is paused, the promise is resolved immediately.
|
||||
*/
|
||||
_handleConditionalBreakpoint: Task.async(function*() {
|
||||
if (gClient.mainRoot.traits.conditionalBreakpoints) {
|
||||
return;
|
||||
}
|
||||
let breakLocation = this._currentBreakpointLocation;
|
||||
if (!breakLocation) {
|
||||
return;
|
||||
}
|
||||
let breakpointPromise = DebuggerController.Breakpoints._getAdded(breakLocation);
|
||||
if (!breakpointPromise) {
|
||||
return;
|
||||
}
|
||||
let breakpointClient = yield breakpointPromise;
|
||||
let conditionalExpression = breakpointClient.conditionalExpression;
|
||||
if (!conditionalExpression) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Evaluating the current breakpoint's conditional expression will
|
||||
// cause the stack frames to be cleared and active thread to pause,
|
||||
// sending a 'clientEvaluated' packed and adding the frames again.
|
||||
let evaluationOptions = { depth: 0, meta: FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL };
|
||||
yield this.evaluate(conditionalExpression, evaluationOptions);
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
|
||||
// If the breakpoint's conditional expression evaluation is falsy,
|
||||
// automatically resume execution.
|
||||
if (VariablesView.isFalsy({ value: this._currentEvaluation.return })) {
|
||||
this.activeThread.resume(DebuggerController._ensureResumptionOrder);
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Handles watch expressions when the debugger pauses and the stackframes
|
||||
* are received.
|
||||
*
|
||||
* @return object
|
||||
* A promise that is resolved after the potential watch expressions
|
||||
* are evaluated. If there are no watch expressions where the debugger
|
||||
* is paused, the promise is resolved immediately.
|
||||
*/
|
||||
_handleWatchExpressions: Task.async(function*() {
|
||||
// Ignore useless notifications.
|
||||
if (!this.activeThread || !this.activeThread.cachedFrames.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let watchExpressions = this._currentWatchExpressions;
|
||||
if (!watchExpressions) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Evaluation causes the stack frames to be cleared and active thread to
|
||||
// pause, sending a 'clientEvaluated' packet and adding the frames again.
|
||||
let evaluationOptions = { depth: 0, meta: FRAME_TYPE.WATCH_EXPRESSIONS_EVAL };
|
||||
yield this.evaluate(watchExpressions, evaluationOptions);
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
|
||||
// If an error was thrown during the evaluation of the watch expressions,
|
||||
// then at least one expression evaluation could not be performed. So
|
||||
// remove the most recent watch expression and try again.
|
||||
if (this._currentEvaluation.throw) {
|
||||
DebuggerView.WatchExpressions.removeAt(0);
|
||||
yield DebuggerController.StackFrames.syncWatchExpressions();
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Adds the watch expressions evaluation results to a scope in the view.
|
||||
*
|
||||
@ -986,30 +1024,29 @@ StackFrames.prototype = {
|
||||
}
|
||||
});
|
||||
|
||||
if (sanitizedExpressions.length) {
|
||||
this._syncedWatchExpressions =
|
||||
this._currentWatchExpressions =
|
||||
"[" +
|
||||
sanitizedExpressions.map(aString =>
|
||||
"eval(\"" +
|
||||
"try {" +
|
||||
// Make sure all quotes are escaped in the expression's syntax,
|
||||
// and add a newline after the statement to avoid comments
|
||||
// breaking the code integrity inside the eval block.
|
||||
aString.replace(/"/g, "\\$&") + "\" + " + "'\\n'" + " + \"" +
|
||||
"} catch (e) {" +
|
||||
"e.name + ': ' + e.message;" + // TODO: Bug 812765, 812764.
|
||||
"}" +
|
||||
"\")"
|
||||
).join(",") +
|
||||
"]";
|
||||
if (!sanitizedExpressions.length) {
|
||||
this._currentWatchExpressions = null;
|
||||
this._syncedWatchExpressions = null;
|
||||
} else {
|
||||
this._syncedWatchExpressions =
|
||||
this._currentWatchExpressions = null;
|
||||
this._currentWatchExpressions = "[" +
|
||||
sanitizedExpressions.map(aString =>
|
||||
"eval(\"" +
|
||||
"try {" +
|
||||
// Make sure all quotes are escaped in the expression's syntax,
|
||||
// and add a newline after the statement to avoid comments
|
||||
// breaking the code integrity inside the eval block.
|
||||
aString.replace(/"/g, "\\$&") + "\" + " + "'\\n'" + " + \"" +
|
||||
"} catch (e) {" +
|
||||
"e.name + ': ' + e.message;" + // TODO: Bug 812765, 812764.
|
||||
"}" +
|
||||
"\")"
|
||||
).join(",") +
|
||||
"]";
|
||||
}
|
||||
|
||||
this.currentFrameDepth = -1;
|
||||
this._onFrames();
|
||||
return this._onFrames();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1310,13 +1347,12 @@ SourceScripts.prototype = {
|
||||
}
|
||||
|
||||
// Get the source text from the active thread.
|
||||
this.activeThread.source(aSource)
|
||||
.source(({ error, message, source: text, contentType }) => {
|
||||
this.activeThread.source(aSource).source(({ error, source: text, contentType }) => {
|
||||
if (aOnTimeout) {
|
||||
window.clearTimeout(fetchTimeout);
|
||||
}
|
||||
if (error) {
|
||||
deferred.reject([aSource, message || error]);
|
||||
deferred.reject([aSource, error]);
|
||||
} else {
|
||||
deferred.resolve([aSource, text, contentType]);
|
||||
}
|
||||
@ -1429,12 +1465,14 @@ Tracer.prototype = {
|
||||
* Instructs the tracer actor to start tracing.
|
||||
*/
|
||||
startTracing: function(aCallback = () => {}) {
|
||||
DebuggerView.Tracer.selectTab();
|
||||
if (this.tracing) {
|
||||
return;
|
||||
}
|
||||
this._trace = "dbg.trace" + Math.random();
|
||||
this.traceClient.startTrace([
|
||||
|
||||
DebuggerView.Tracer.selectTab();
|
||||
|
||||
let id = this._trace = "dbg.trace" + Math.random();
|
||||
let fields = [
|
||||
"name",
|
||||
"location",
|
||||
"parameterNames",
|
||||
@ -1443,7 +1481,9 @@ Tracer.prototype = {
|
||||
"return",
|
||||
"throw",
|
||||
"yield"
|
||||
], this._trace, (aResponse) => {
|
||||
];
|
||||
|
||||
this.traceClient.startTrace(fields, id, aResponse => {
|
||||
const { error } = aResponse;
|
||||
if (error) {
|
||||
DevToolsUtils.reportException("Tracer.prototype.startTracing", error);
|
||||
@ -1475,11 +1515,11 @@ Tracer.prototype = {
|
||||
onTraces: function (aEvent, { traces }) {
|
||||
const tracesLength = traces.length;
|
||||
let tracesToShow;
|
||||
|
||||
if (tracesLength > TracerView.MAX_TRACES) {
|
||||
tracesToShow = traces.slice(tracesLength - TracerView.MAX_TRACES,
|
||||
tracesLength);
|
||||
DebuggerView.Tracer.empty();
|
||||
tracesToShow = traces.slice(tracesLength - TracerView.MAX_TRACES, tracesLength);
|
||||
this._stack.splice(0, this._stack.length);
|
||||
DebuggerView.Tracer.empty();
|
||||
} else {
|
||||
tracesToShow = traces;
|
||||
}
|
||||
@ -1602,7 +1642,6 @@ Tracer.prototype = {
|
||||
* Handles breaking on event listeners in the currently debugged target.
|
||||
*/
|
||||
function EventListeners() {
|
||||
this._onEventListeners = this._onEventListeners.bind(this);
|
||||
}
|
||||
|
||||
EventListeners.prototype = {
|
||||
@ -1630,65 +1669,71 @@ EventListeners.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches the currently attached event listeners from the debugee.
|
||||
* Schedules fetching the currently attached event listeners from the debugee.
|
||||
*/
|
||||
scheduleEventListenersFetch: function() {
|
||||
let getListeners = aCallback => gThreadClient.eventListeners(aResponse => {
|
||||
if (aResponse.error) {
|
||||
let msg = "Error getting event listeners: " + aResponse.message;
|
||||
DevToolsUtils.reportException("scheduleEventListenersFetch", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
let outstandingListenersDefinitionSite = aResponse.listeners.map(aListener => {
|
||||
const deferred = promise.defer();
|
||||
|
||||
gThreadClient.pauseGrip(aListener.function).getDefinitionSite(aResponse => {
|
||||
if (aResponse.error) {
|
||||
const msg = "Error getting function definition site: " + aResponse.message;
|
||||
DevToolsUtils.reportException("scheduleEventListenersFetch", msg);
|
||||
} else {
|
||||
aListener.function.url = aResponse.url;
|
||||
}
|
||||
|
||||
deferred.resolve(aListener);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
promise.all(outstandingListenersDefinitionSite).then(aListeners => {
|
||||
this._onEventListeners(aListeners);
|
||||
|
||||
// Notify that event listeners were fetched and shown in the view,
|
||||
// and callback to resume the active thread if necessary.
|
||||
window.emit(EVENTS.EVENT_LISTENERS_FETCHED);
|
||||
aCallback && aCallback();
|
||||
});
|
||||
});
|
||||
|
||||
// Make sure we're not sending a batch of closely repeated requests.
|
||||
// This can easily happen whenever new sources are fetched.
|
||||
setNamedTimeout("event-listeners-fetch", FETCH_EVENT_LISTENERS_DELAY, () => {
|
||||
if (gThreadClient.state != "paused") {
|
||||
gThreadClient.interrupt(() => getListeners(() => gThreadClient.resume()));
|
||||
gThreadClient.interrupt(() => this._getListeners(() => gThreadClient.resume()));
|
||||
} else {
|
||||
getListeners();
|
||||
this._getListeners();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for a debugger's successful active thread eventListeners() call.
|
||||
* Fetches the currently attached event listeners from the debugee.
|
||||
* The thread client state is assumed to be "paused".
|
||||
*
|
||||
* @param function aCallback
|
||||
* Invoked once the event listeners are fetched and displayed.
|
||||
*/
|
||||
_onEventListeners: function(aListeners) {
|
||||
// Add all the listeners in the debugger view event linsteners container.
|
||||
for (let listener of aListeners) {
|
||||
DebuggerView.EventListeners.addListener(listener, { staged: true });
|
||||
}
|
||||
_getListeners: function(aCallback) {
|
||||
gThreadClient.eventListeners(Task.async(function*(aResponse) {
|
||||
if (aResponse.error) {
|
||||
throw "Error getting event listeners: " + aResponse.message;
|
||||
}
|
||||
|
||||
// Flushes all the prepared events into the event listeners container.
|
||||
DebuggerView.EventListeners.commit();
|
||||
// Add all the listeners in the debugger view event linsteners container.
|
||||
for (let listener of aResponse.listeners) {
|
||||
let definitionSite = yield this._getDefinitionSite(listener.function);
|
||||
listener.function.url = definitionSite;
|
||||
DebuggerView.EventListeners.addListener(listener, { staged: true });
|
||||
}
|
||||
|
||||
// Flushes all the prepared events into the event listeners container.
|
||||
DebuggerView.EventListeners.commit();
|
||||
|
||||
// Notify that event listeners were fetched and shown in the view,
|
||||
// and callback to resume the active thread if necessary.
|
||||
window.emit(EVENTS.EVENT_LISTENERS_FETCHED);
|
||||
aCallback && aCallback();
|
||||
}.bind(this)));
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a function's source-mapped definiton site.
|
||||
*
|
||||
* @param object aFunction
|
||||
* The grip of the function to get the definition site for.
|
||||
* @return object
|
||||
* A promise that is resolved with the function's owner source url,
|
||||
* or rejected if an error occured.
|
||||
*/
|
||||
_getDefinitionSite: function(aFunction) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
gThreadClient.pauseGrip(aFunction).getDefinitionSite(aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject("Error getting function definition site: " + aResponse.message);
|
||||
} else {
|
||||
deferred.resolve(aResponse.url);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1744,9 +1789,10 @@ Breakpoints.prototype = {
|
||||
* @param number aLine
|
||||
* Line number where breakpoint was set.
|
||||
*/
|
||||
_onEditorBreakpointAdd: function(_, aLine) {
|
||||
_onEditorBreakpointAdd: Task.async(function*(_, aLine) {
|
||||
let url = DebuggerView.Sources.selectedValue;
|
||||
let location = { url: url, line: aLine + 1 };
|
||||
let breakpointClient = yield this.addBreakpoint(location, { noEditorUpdate: true });
|
||||
|
||||
// Initialize the breakpoint, but don't update the editor, since this
|
||||
// callback is invoked because a breakpoint was added in the editor itself.
|
||||
@ -1754,16 +1800,16 @@ Breakpoints.prototype = {
|
||||
// If the breakpoint client has a "requestedLocation" attached, then
|
||||
// the original requested placement for the breakpoint wasn't accepted.
|
||||
// In this case, we need to update the editor with the new location.
|
||||
if (aBreakpointClient.requestedLocation) {
|
||||
if (breakpointClient.requestedLocation) {
|
||||
DebuggerView.editor.moveBreakpoint(
|
||||
aBreakpointClient.requestedLocation.line - 1,
|
||||
aBreakpointClient.location.line - 1
|
||||
breakpointClient.requestedLocation.line - 1,
|
||||
breakpointClient.location.line - 1
|
||||
);
|
||||
}
|
||||
// Notify that we've shown a breakpoint in the source editor.
|
||||
window.emit(EVENTS.BREAKPOINT_SHOWN);
|
||||
window.emit(EVENTS.BREAKPOINT_SHOWN_IN_EDITOR);
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Event handler for breakpoints that are removed from the editor.
|
||||
@ -1771,17 +1817,14 @@ Breakpoints.prototype = {
|
||||
* @param number aLine
|
||||
* Line number where breakpoint was removed.
|
||||
*/
|
||||
_onEditorBreakpointRemove: function(_, aLine) {
|
||||
_onEditorBreakpointRemove: Task.async(function*(_, aLine) {
|
||||
let url = DebuggerView.Sources.selectedValue;
|
||||
let location = { url: url, line: aLine + 1 };
|
||||
yield this.removeBreakpoint(location, { noEditorUpdate: true });
|
||||
|
||||
// Destroy the breakpoint, but don't update the editor, since this callback
|
||||
// is invoked because a breakpoint was removed from the editor itself.
|
||||
this.removeBreakpoint(location, { noEditorUpdate: true }).then(() => {
|
||||
// Notify that we've hidden a breakpoint in the source editor.
|
||||
window.emit(EVENTS.BREAKPOINT_HIDDEN);
|
||||
});
|
||||
},
|
||||
// Notify that we've hidden a breakpoint in the source editor.
|
||||
window.emit(EVENTS.BREAKPOINT_HIDDEN_IN_EDITOR);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update the breakpoints in the editor view. This function takes the list of
|
||||
@ -1789,19 +1832,18 @@ Breakpoints.prototype = {
|
||||
* This is invoked when the selected script is changed, or when new sources
|
||||
* are received via the _onNewSource and _onSourcesAdded event listeners.
|
||||
*/
|
||||
updateEditorBreakpoints: function() {
|
||||
updateEditorBreakpoints: Task.async(function*() {
|
||||
for (let breakpointPromise of this._addedOrDisabled) {
|
||||
breakpointPromise.then(aBreakpointClient => {
|
||||
let currentSourceUrl = DebuggerView.Sources.selectedValue;
|
||||
let breakpointUrl = aBreakpointClient.location.url;
|
||||
let breakpointClient = yield breakpointPromise;
|
||||
let currentSourceUrl = DebuggerView.Sources.selectedValue;
|
||||
let breakpointUrl = breakpointClient.location.url;
|
||||
|
||||
// Update the view only if the breakpoint is in the currently shown source.
|
||||
if (currentSourceUrl == breakpointUrl) {
|
||||
this._showBreakpoint(aBreakpointClient, { noPaneUpdate: true });
|
||||
}
|
||||
});
|
||||
// Update the view only if the breakpoint is in the currently shown source.
|
||||
if (currentSourceUrl == breakpointUrl) {
|
||||
this._showBreakpoint(breakpointClient, { noPaneUpdate: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update the breakpoints in the pane view. This function takes the list of
|
||||
@ -1809,19 +1851,18 @@ Breakpoints.prototype = {
|
||||
* This is invoked when new sources are received via the _onNewSource and
|
||||
* _onSourcesAdded event listeners.
|
||||
*/
|
||||
updatePaneBreakpoints: function() {
|
||||
updatePaneBreakpoints: Task.async(function*() {
|
||||
for (let breakpointPromise of this._addedOrDisabled) {
|
||||
breakpointPromise.then(aBreakpointClient => {
|
||||
let container = DebuggerView.Sources;
|
||||
let breakpointUrl = aBreakpointClient.location.url;
|
||||
let breakpointClient = yield breakpointPromise;
|
||||
let container = DebuggerView.Sources;
|
||||
let breakpointUrl = breakpointClient.location.url;
|
||||
|
||||
// Update the view only if the breakpoint exists in a known source.
|
||||
if (container.containsValue(breakpointUrl)) {
|
||||
this._showBreakpoint(aBreakpointClient, { noEditorUpdate: true });
|
||||
}
|
||||
});
|
||||
// Update the view only if the breakpoint exists in a known source.
|
||||
if (container.containsValue(breakpointUrl)) {
|
||||
this._showBreakpoint(breakpointClient, { noEditorUpdate: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Add a breakpoint.
|
||||
@ -2036,22 +2077,19 @@ Breakpoints.prototype = {
|
||||
* @return object
|
||||
* A promise that will be resolved with the breakpoint client
|
||||
*/
|
||||
updateCondition: function(aLocation, aCondition) {
|
||||
updateCondition: Task.async(function*(aLocation, aCondition) {
|
||||
let addedPromise = this._getAdded(aLocation);
|
||||
if (!addedPromise) {
|
||||
return promise.reject(new Error('breakpoint does not exist ' +
|
||||
'in specified location'));
|
||||
throw new Error("Breakpoint does not exist at the specified location");
|
||||
}
|
||||
|
||||
var promise = addedPromise.then(aBreakpointClient => {
|
||||
return aBreakpointClient.setCondition(gThreadClient, aCondition);
|
||||
});
|
||||
let breakpointClient = yield addedPromise;
|
||||
let promise = breakpointClient.setCondition(gThreadClient, aCondition);
|
||||
|
||||
// `setCondition` returns a new breakpoint that has the condition,
|
||||
// so we need to update the store
|
||||
this._added.set(this.getIdentifier(aLocation), promise);
|
||||
return promise;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update the editor and breakpoints pane to show a specified breakpoint.
|
||||
|
@ -3,12 +3,8 @@
|
||||
/* 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";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
// Used to detect minification for automatic pretty printing
|
||||
const SAMPLE_SIZE = 50; // no of lines
|
||||
const INDENT_COUNT_THRESHOLD = 5; // percentage
|
||||
@ -41,8 +37,6 @@ function SourcesView() {
|
||||
this._onConditionalPopupShown = this._onConditionalPopupShown.bind(this);
|
||||
this._onConditionalPopupHiding = this._onConditionalPopupHiding.bind(this);
|
||||
this._onConditionalTextboxKeyPress = this._onConditionalTextboxKeyPress.bind(this);
|
||||
|
||||
this.updateToolbarButtonsState = this.updateToolbarButtonsState.bind(this);
|
||||
}
|
||||
|
||||
SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
@ -56,15 +50,6 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
showArrows: true
|
||||
});
|
||||
|
||||
// Sort known source groups towards the end of the list
|
||||
this.widget.groupSortPredicate = function(a, b) {
|
||||
if ((a in KNOWN_SOURCE_GROUPS) == (b in KNOWN_SOURCE_GROUPS)) {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
|
||||
return (a in KNOWN_SOURCE_GROUPS) ? 1 : -1;
|
||||
};
|
||||
|
||||
this.emptyText = L10N.getStr("noSourcesText");
|
||||
this._blackBoxCheckboxTooltip = L10N.getStr("blackBoxCheckboxTooltip");
|
||||
|
||||
@ -98,6 +83,14 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
return +(aFirst.attachment.label.toLowerCase() >
|
||||
aSecond.attachment.label.toLowerCase());
|
||||
});
|
||||
|
||||
// Sort known source groups towards the end of the list
|
||||
this.widget.groupSortPredicate = function(a, b) {
|
||||
if ((a in KNOWN_SOURCE_GROUPS) == (b in KNOWN_SOURCE_GROUPS)) {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
return (a in KNOWN_SOURCE_GROUPS) ? 1 : -1;
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@ -216,6 +209,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
if (aOptions.openPopup || !aOptions.noEditorUpdate) {
|
||||
this.highlightBreakpoint(location, aOptions);
|
||||
}
|
||||
|
||||
window.emit(EVENTS.BREAKPOINT_SHOWN_IN_PANE);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -239,6 +234,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
|
||||
// Clear the breakpoint view.
|
||||
sourceItem.remove(breakpointItem);
|
||||
|
||||
window.emit(EVENTS.BREAKPOINT_HIDDEN_IN_PANE);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -429,8 +426,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* Unhighlights the current breakpoint in this sources container.
|
||||
*/
|
||||
unhighlightBreakpoint: function() {
|
||||
this._unselectBreakpoint();
|
||||
this._hideConditionalPopup();
|
||||
this._unselectBreakpoint();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -459,7 +456,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
/**
|
||||
* Toggle the pretty printing of the selected source.
|
||||
*/
|
||||
togglePrettyPrint: function() {
|
||||
togglePrettyPrint: Task.async(function*() {
|
||||
if (this._prettyPrintButton.hasAttribute("disabled")) {
|
||||
return;
|
||||
}
|
||||
@ -486,25 +483,29 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this._prettyPrintButton.removeAttribute("checked");
|
||||
}
|
||||
|
||||
DebuggerController.SourceScripts.togglePrettyPrint(source)
|
||||
.then(resetEditor, printError)
|
||||
.then(DebuggerView.showEditor)
|
||||
.then(this.updateToolbarButtonsState);
|
||||
},
|
||||
try {
|
||||
let resolution = yield DebuggerController.SourceScripts.togglePrettyPrint(source);
|
||||
resetEditor(resolution);
|
||||
} catch (rejection) {
|
||||
printError(rejection);
|
||||
}
|
||||
|
||||
DebuggerView.showEditor();
|
||||
this.updateToolbarButtonsState();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Toggle the black boxed state of the selected source.
|
||||
*/
|
||||
toggleBlackBoxing: function() {
|
||||
toggleBlackBoxing: Task.async(function*() {
|
||||
const { source } = this.selectedItem.attachment;
|
||||
const sourceClient = gThreadClient.source(source);
|
||||
const shouldBlackBox = !sourceClient.isBlackBoxed;
|
||||
|
||||
// Be optimistic that the (un-)black boxing will succeed, so enable/disable
|
||||
// the pretty print button and check/uncheck the black box button
|
||||
// immediately. Then, once we actually get the results from the server, make
|
||||
// sure that it is in the correct state again by calling
|
||||
// `updateToolbarButtonsState`.
|
||||
// the pretty print button and check/uncheck the black box button immediately.
|
||||
// Then, once we actually get the results from the server, make sure that
|
||||
// it is in the correct state again by calling `updateToolbarButtonsState`.
|
||||
|
||||
if (shouldBlackBox) {
|
||||
this._prettyPrintButton.setAttribute("disabled", true);
|
||||
@ -514,10 +515,14 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this._blackBoxButton.removeAttribute("checked");
|
||||
}
|
||||
|
||||
DebuggerController.SourceScripts.setBlackBoxing(source, shouldBlackBox)
|
||||
.then(this.updateToolbarButtonsState,
|
||||
this.updateToolbarButtonsState);
|
||||
},
|
||||
try {
|
||||
yield DebuggerController.SourceScripts.setBlackBoxing(source, shouldBlackBox);
|
||||
} catch (e) {
|
||||
// Continue execution in this task even if blackboxing failed.
|
||||
}
|
||||
|
||||
this.updateToolbarButtonsState();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Toggles all breakpoints enabled/disabled.
|
||||
@ -806,7 +811,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
/**
|
||||
* The select listener for the sources container.
|
||||
*/
|
||||
_onSourceSelect: function({ detail: sourceItem }) {
|
||||
_onSourceSelect: Task.async(function*({ detail: sourceItem }) {
|
||||
if (!sourceItem) {
|
||||
return;
|
||||
}
|
||||
@ -816,12 +821,12 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
// The container is not empty and an actual item was selected.
|
||||
DebuggerView.setEditorLocation(sourceItem.value);
|
||||
|
||||
// Attempt to automatically pretty print minified source code.
|
||||
if (Prefs.autoPrettyPrint && !sourceClient.isPrettyPrinted) {
|
||||
DebuggerController.SourceScripts.getText(source).then(([, aText]) => {
|
||||
if (SourceUtils.isMinified(sourceClient, aText)) {
|
||||
this.togglePrettyPrint();
|
||||
}
|
||||
}).then(null, e => DevToolsUtils.reportException("_onSourceSelect", e));
|
||||
let isMinified = yield SourceUtils.isMinified(sourceClient);
|
||||
if (isMinified) {
|
||||
this.togglePrettyPrint();
|
||||
}
|
||||
}
|
||||
|
||||
// Set window title. No need to split the url by " -> " here, because it was
|
||||
@ -830,18 +835,22 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
|
||||
DebuggerView.maybeShowBlackBoxMessage();
|
||||
this.updateToolbarButtonsState();
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* The click listener for the "stop black boxing" button.
|
||||
*/
|
||||
_onStopBlackBoxing: function() {
|
||||
_onStopBlackBoxing: Task.async(function*() {
|
||||
const { source } = this.selectedItem.attachment;
|
||||
|
||||
DebuggerController.SourceScripts.setBlackBoxing(source, false)
|
||||
.then(this.updateToolbarButtonsState,
|
||||
this.updateToolbarButtonsState);
|
||||
},
|
||||
try {
|
||||
yield DebuggerController.SourceScripts.setBlackBoxing(source, false);
|
||||
} catch (e) {
|
||||
// Continue execution in this task even if blackboxing failed.
|
||||
}
|
||||
|
||||
this.updateToolbarButtonsState();
|
||||
}),
|
||||
|
||||
/**
|
||||
* The click listener for a breakpoint container.
|
||||
@ -913,6 +922,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
*/
|
||||
_onConditionalPopupHiding: Task.async(function*() {
|
||||
this._conditionalPopupVisible = false; // Used in tests.
|
||||
|
||||
let breakpointItem = this._selectedBreakpointItem;
|
||||
let attachment = breakpointItem.attachment;
|
||||
|
||||
@ -920,11 +930,9 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
// save the current conditional epression.
|
||||
let breakpointPromise = DebuggerController.Breakpoints._getAdded(attachment);
|
||||
if (breakpointPromise) {
|
||||
let breakpointClient = yield breakpointPromise;
|
||||
yield DebuggerController.Breakpoints.updateCondition(
|
||||
breakpointClient.location,
|
||||
this._cbTextbox.value
|
||||
);
|
||||
let { location } = yield breakpointPromise;
|
||||
let condition = this._cbTextbox.value;
|
||||
yield DebuggerController.Breakpoints.updateCondition(location, condition);
|
||||
}
|
||||
|
||||
window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_HIDING);
|
||||
@ -1124,7 +1132,8 @@ function TracerView() {
|
||||
DevToolsUtils.makeInfallible(this._onSelect.bind(this));
|
||||
this._onMouseOver =
|
||||
DevToolsUtils.makeInfallible(this._onMouseOver.bind(this));
|
||||
this._onSearch = DevToolsUtils.makeInfallible(this._onSearch.bind(this));
|
||||
this._onSearch =
|
||||
DevToolsUtils.makeInfallible(this._onSearch.bind(this));
|
||||
}
|
||||
|
||||
TracerView.MAX_TRACES = 200;
|
||||
@ -1257,6 +1266,7 @@ TracerView.prototype = Heritage.extend(WidgetMethods, {
|
||||
*/
|
||||
_populateVariable: function(aName, aParent, aValue) {
|
||||
let item = aParent.addItem(aName, { value: aValue });
|
||||
|
||||
if (aValue) {
|
||||
let wrappedValue = new DebuggerController.Tracer.WrappedObject(aValue);
|
||||
DebuggerView.Variables.controller.populate(item, wrappedValue);
|
||||
@ -1512,16 +1522,15 @@ let SourceUtils = {
|
||||
* Determines if the source text is minified by using
|
||||
* the percentage indented of a subset of lines
|
||||
*
|
||||
* @param string aText
|
||||
* The source text.
|
||||
* @return boolean
|
||||
* True if source text is minified.
|
||||
* @return object
|
||||
* A promise that resolves to true if source text is minified.
|
||||
*/
|
||||
isMinified: function(sourceClient, aText){
|
||||
isMinified: Task.async(function*(sourceClient) {
|
||||
if (this._minifiedCache.has(sourceClient)) {
|
||||
return this._minifiedCache.get(sourceClient);
|
||||
}
|
||||
|
||||
let [, text] = yield DebuggerController.SourceScripts.getText(sourceClient);
|
||||
let isMinified;
|
||||
let lineEndIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
@ -1530,14 +1539,14 @@ let SourceUtils = {
|
||||
let overCharLimit = false;
|
||||
|
||||
// Strip comments.
|
||||
aText = aText.replace(/\/\*[\S\s]*?\*\/|\/\/(.+|\n)/g, "");
|
||||
text = text.replace(/\/\*[\S\s]*?\*\/|\/\/(.+|\n)/g, "");
|
||||
|
||||
while (lines++ < SAMPLE_SIZE) {
|
||||
lineEndIndex = aText.indexOf("\n", lineStartIndex);
|
||||
lineEndIndex = text.indexOf("\n", lineStartIndex);
|
||||
if (lineEndIndex == -1) {
|
||||
break;
|
||||
}
|
||||
if (/^\s+/.test(aText.slice(lineStartIndex, lineEndIndex))) {
|
||||
if (/^\s+/.test(text.slice(lineStartIndex, lineEndIndex))) {
|
||||
indentCount++;
|
||||
}
|
||||
// For files with no indents but are not minified.
|
||||
@ -1547,12 +1556,13 @@ let SourceUtils = {
|
||||
}
|
||||
lineStartIndex = lineEndIndex + 1;
|
||||
}
|
||||
isMinified = ((indentCount / lines ) * 100) < INDENT_COUNT_THRESHOLD ||
|
||||
overCharLimit;
|
||||
|
||||
isMinified =
|
||||
((indentCount / lines) * 100) < INDENT_COUNT_THRESHOLD || overCharLimit;
|
||||
|
||||
this._minifiedCache.set(sourceClient, isMinified);
|
||||
return isMinified;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Clears the labels, groups and minify cache, populated by methods like
|
||||
@ -1590,6 +1600,7 @@ let SourceUtils = {
|
||||
if (!sourceLabel) {
|
||||
sourceLabel = this.trimUrl(aUrl);
|
||||
}
|
||||
|
||||
let unicodeLabel = NetworkHelper.convertToUnicode(unescape(sourceLabel));
|
||||
this._labelsCache.set(aUrl, unicodeLabel);
|
||||
return unicodeLabel;
|
||||
@ -2328,12 +2339,11 @@ WatchExpressionsView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The keypress listener for a watch expression's textbox.
|
||||
*/
|
||||
_onKeyPress: function(e) {
|
||||
switch(e.keyCode) {
|
||||
switch (e.keyCode) {
|
||||
case e.DOM_VK_RETURN:
|
||||
case e.DOM_VK_ESCAPE:
|
||||
e.stopPropagation();
|
||||
DebuggerView.editor.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -2404,6 +2414,7 @@ EventListenersView.prototype = Heritage.extend(WidgetMethods, {
|
||||
let eventItem = this.getItemForPredicate(aItem =>
|
||||
aItem.attachment.url == url &&
|
||||
aItem.attachment.type == type);
|
||||
|
||||
if (eventItem) {
|
||||
let { selectors, view: { targets } } = eventItem.attachment;
|
||||
if (selectors.indexOf(selector) == -1) {
|
||||
|
@ -120,6 +120,10 @@ ToolbarView.prototype = {
|
||||
* Listener handling the pause/resume button click event.
|
||||
*/
|
||||
_onResumePressed: function() {
|
||||
if (DebuggerController.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
let warn = DebuggerController._ensureResumptionOrder;
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
@ -144,6 +148,10 @@ ToolbarView.prototype = {
|
||||
* Listener handling the step in button click event.
|
||||
*/
|
||||
_onStepInPressed: function() {
|
||||
if (DebuggerController.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
let warn = DebuggerController._ensureResumptionOrder;
|
||||
@ -315,7 +323,7 @@ OptionsView.prototype = {
|
||||
window.setTimeout(() => {
|
||||
DebuggerController.reconfigureThread(pref);
|
||||
}, POPUP_HIDDEN_DELAY);
|
||||
}, false);
|
||||
});
|
||||
},
|
||||
|
||||
_button: null,
|
||||
|
@ -59,7 +59,7 @@ function test() {
|
||||
is(gBreakpointsAdded.size, 0, "no breakpoints added");
|
||||
|
||||
let cmd = gContextMenu.querySelector('menuitem[command=addBreakpointCommand]');
|
||||
let bpShown = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN);
|
||||
let bpShown = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_EDITOR);
|
||||
EventUtils.synthesizeMouseAtCenter(cmd, {}, gDebugger);
|
||||
return bpShown;
|
||||
}).then(() => {
|
||||
|
@ -51,7 +51,12 @@ function test() {
|
||||
return resumeAndTestBreakpoint(30);
|
||||
})
|
||||
.then(() => resumeAndTestNoBreakpoint())
|
||||
.then(() => reloadActiveTab(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN, 13))
|
||||
.then(() => {
|
||||
return promise.all([
|
||||
reloadActiveTab(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_EDITOR, 13),
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_PANE, 13)
|
||||
]);
|
||||
})
|
||||
.then(() => testAfterReload())
|
||||
.then(() => {
|
||||
// Reset traits back to default value
|
||||
|
@ -103,7 +103,8 @@ function afterDebuggerStatementHit() {
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE),
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCES_ADDED),
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN),
|
||||
reloadActiveTab(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN)
|
||||
reloadActiveTab(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_EDITOR),
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_PANE)
|
||||
]).then(testClickAgain);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,12 @@ function test() {
|
||||
.then(() => resumeAndTestBreakpoint(28))
|
||||
.then(() => resumeAndTestBreakpoint(29))
|
||||
.then(() => resumeAndTestNoBreakpoint())
|
||||
.then(() => reloadActiveTab(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN, 13))
|
||||
.then(() => {
|
||||
return promise.all([
|
||||
reloadActiveTab(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_EDITOR, 13),
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_SHOWN_IN_PANE, 13)
|
||||
]);
|
||||
})
|
||||
.then(() => testAfterReload())
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
|
@ -19,7 +19,9 @@ function test() {
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gClassicFrames = gDebugger.DebuggerView.StackFramesClassicList;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 26).then(performTest);
|
||||
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_REFILLED)
|
||||
.then(performTest);
|
||||
|
||||
gDebuggee.gRecurseLimit = (gDebugger.gCallStackPageSize * 2) + 1;
|
||||
gDebuggee.recurse();
|
||||
@ -32,15 +34,15 @@ function performTest() {
|
||||
is(gFrames.itemCount, gDebugger.gCallStackPageSize,
|
||||
"Should have only the max limit of frames.");
|
||||
is(gClassicFrames.itemCount, gDebugger.gCallStackPageSize,
|
||||
"Should have only the max limit of frames in the mirrored view as well.")
|
||||
"Should have only the max limit of frames in the mirrored view as well.");
|
||||
|
||||
gDebugger.gThreadClient.addOneTimeListener("framesadded", () => {
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_REFILLED).then(() => {
|
||||
is(gFrames.itemCount, gDebugger.gCallStackPageSize * 2,
|
||||
"Should now have twice the max limit of frames.");
|
||||
is(gClassicFrames.itemCount, gDebugger.gCallStackPageSize * 2,
|
||||
"Should now have twice the max limit of frames in the mirrored view as well.");
|
||||
|
||||
gDebugger.gThreadClient.addOneTimeListener("framesadded", () => {
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_REFILLED).then(() => {
|
||||
is(gFrames.itemCount, gDebuggee.gRecurseLimit,
|
||||
"Should have reached the recurse limit.");
|
||||
is(gClassicFrames.itemCount, gDebuggee.gRecurseLimit,
|
||||
|
@ -25,9 +25,9 @@ function test() {
|
||||
gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false });
|
||||
|
||||
waitForSourceShown(gPanel, ".html", 1)
|
||||
.then(() => addExpressions())
|
||||
.then(() => performTest())
|
||||
.then(() => finishTest())
|
||||
.then(addExpressions)
|
||||
.then(performTest)
|
||||
.then(finishTest)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
|
Loading…
Reference in New Issue
Block a user