diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index 029f2f8915d..9551174c73a 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -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. diff --git a/browser/devtools/debugger/debugger-panes.js b/browser/devtools/debugger/debugger-panes.js index 52131ea36bd..ed9f1a44e81 100644 --- a/browser/devtools/debugger/debugger-panes.js +++ b/browser/devtools/debugger/debugger-panes.js @@ -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) { diff --git a/browser/devtools/debugger/debugger-toolbar.js b/browser/devtools/debugger/debugger-toolbar.js index ca2dc6c5822..230fdbe25cf 100644 --- a/browser/devtools/debugger/debugger-toolbar.js +++ b/browser/devtools/debugger/debugger-toolbar.js @@ -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, diff --git a/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu-add.js b/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu-add.js index f025b3efe25..ab2acf7003a 100644 --- a/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu-add.js +++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu-add.js @@ -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(() => { diff --git a/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-01.js b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-01.js index 892e2cc74fe..bd4d1d89a7c 100644 --- a/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-01.js +++ b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-01.js @@ -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 diff --git a/browser/devtools/debugger/test/browser_dbg_location-changes-04-breakpoint.js b/browser/devtools/debugger/test/browser_dbg_location-changes-04-breakpoint.js index 29922e9a7db..a79f3d110c1 100644 --- a/browser/devtools/debugger/test/browser_dbg_location-changes-04-breakpoint.js +++ b/browser/devtools/debugger/test/browser_dbg_location-changes-04-breakpoint.js @@ -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); } diff --git a/browser/devtools/debugger/test/browser_dbg_server-conditional-bp-01.js b/browser/devtools/debugger/test/browser_dbg_server-conditional-bp-01.js index 402b45f1523..e045050a76f 100644 --- a/browser/devtools/debugger/test/browser_dbg_server-conditional-bp-01.js +++ b/browser/devtools/debugger/test/browser_dbg_server-conditional-bp-01.js @@ -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 => { diff --git a/browser/devtools/debugger/test/browser_dbg_stack-03.js b/browser/devtools/debugger/test/browser_dbg_stack-03.js index b450d22ad7b..37b0a411af4 100644 --- a/browser/devtools/debugger/test/browser_dbg_stack-03.js +++ b/browser/devtools/debugger/test/browser_dbg_stack-03.js @@ -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, diff --git a/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js b/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js index 655bd01003f..151fc83c6ef 100644 --- a/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js +++ b/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js @@ -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);