Bug 876277 - Convert the debugger frontend to use the EventEmitter instead of relying on DOM events, r=past

This commit is contained in:
Victor Porof 2013-09-13 16:23:14 +03:00
parent 144c810b07
commit 751773a2b4
4 changed files with 110 additions and 34 deletions

View File

@ -14,10 +14,64 @@ const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
const FRAME_STEP_CLEAR_DELAY = 100; // ms
const CALL_STACK_PAGE_SIZE = 25; // frames
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
// When the debugger's source editor instance finishes loading or unloading.
EDITOR_LOADED: "Debugger:EditorLoaded",
EDITOR_UNLOADED: "Debugger:EditorUnoaded",
// When new sources are received from the debugger server.
NEW_SOURCE: "Debugger:NewSource",
SOURCES_ADDED: "Debugger:SourcesAdded",
// When a source is shown in the source editor.
SOURCE_SHOWN: "Debugger:EditorSourceShown",
SOURCE_ERROR_SHOWN: "Debugger:EditorSourceErrorShown",
// When scopes, variables, properties and watch expressions are fetched and
// displayed in the variables view.
FETCHED_SCOPES: "Debugger:FetchedScopes",
FETCHED_VARIABLES: "Debugger:FetchedVariables",
FETCHED_PROPERTIES: "Debugger:FetchedProperties",
FETCHED_WATCH_EXPRESSIONS: "Debugger:FetchedWatchExpressions",
// When a breakpoint has been added or removed on the debugger server.
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 conditional breakpoint's popup is showing or hiding.
CONDITIONAL_BREAKPOINT_POPUP_SHOWING: "Debugger:ConditionalBreakpointPopupShowing",
CONDITIONAL_BREAKPOINT_POPUP_HIDING: "Debugger:ConditionalBreakpointPopupHiding",
// When a file search was performed.
FILE_SEARCH_MATCH_FOUND: "Debugger:FileSearch:MatchFound",
FILE_SEARCH_MATCH_NOT_FOUND: "Debugger:FileSearch:MatchNotFound",
// When a function search was performed.
FUNCTION_SEARCH_MATCH_FOUND: "Debugger:FunctionSearch:MatchFound",
FUNCTION_SEARCH_MATCH_NOT_FOUND: "Debugger:FunctionSearch:MatchNotFound",
// When a global text search was performed.
GLOBAL_SEARCH_MATCH_FOUND: "Debugger:GlobalSearch:MatchFound",
GLOBAL_SEARCH_MATCH_NOT_FOUND: "Debugger:GlobalSearch:MatchNotFound",
// After the stackframes are cleared and debugger won't pause anymore.
AFTER_FRAMES_CLEARED: "Debugger:AfterFramesCleared",
// When the options popup is showing or hiding.
OPTIONS_POPUP_SHOWING: "Debugger:OptionsPopupShowing",
OPTIONS_POPUP_HIDDEN: "Debugger:OptionsPopupHidden",
};
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
@ -643,7 +697,6 @@ StackFrames.prototype = {
let title = StackFrameUtils.getFrameTitle(frame);
DebuggerView.StackFrames.addFrame(title, location, line, depth, isBlackBoxed);
}
if (this.currentFrameDepth == -1) {
DebuggerView.StackFrames.selectedDepth = 0;
}
@ -691,7 +744,8 @@ StackFrames.prototype = {
DebuggerView.Sources.unhighlightBreakpoint();
DebuggerView.WatchExpressions.toggleContents(true);
DebuggerView.Variables.empty(0);
window.dispatchEvent(document, "Debugger:AfterFramesCleared");
window.emit(EVENTS.AFTER_FRAMES_CLEARED);
},
/**
@ -771,8 +825,10 @@ StackFrames.prototype = {
}
} while ((environment = environment.parent));
// Signal that variables have been fetched.
window.dispatchEvent(document, "Debugger:FetchedVariables");
// Signal that scope environments have been shown and commit the current
// variables view hierarchy to briefly flash items that changed between the
// previous and current scope/variables/properties.
window.emit(EVENTS.FETCHED_SCOPES);
DebuggerView.Variables.commitHierarchy();
},
@ -859,8 +915,10 @@ StackFrames.prototype = {
expRef.separatorStr = L10N.getStr("variablesSeparatorLabel");
}
// Signal that watch expressions have been fetched.
window.dispatchEvent(document, "Debugger:FetchedWatchExpressions");
// Signal that watch expressions have been fetched and commit the
// current variables view hierarchy to briefly flash items that changed
// between the previous and current scope/variables/properties.
window.emit(EVENTS.FETCHED_WATCH_EXPRESSIONS);
DebuggerView.Variables.commitHierarchy();
});
},
@ -1012,7 +1070,7 @@ SourceScripts.prototype = {
DebuggerController.Breakpoints.updatePaneBreakpoints();
// Signal that a new source has been added.
window.dispatchEvent(document, "Debugger:AfterNewSource");
window.emit(EVENTS.NEW_SOURCE);
},
/**
@ -1053,7 +1111,7 @@ SourceScripts.prototype = {
DebuggerController.Breakpoints.updatePaneBreakpoints();
// Signal that sources have been added.
window.dispatchEvent(document, "Debugger:AfterSourcesAdded");
window.emit(EVENTS.SOURCES_ADDED);
},
/**
@ -1274,7 +1332,7 @@ Breakpoints.prototype = {
DebuggerView.editor.addBreakpoint(aBreakpointClient.location.line - 1);
}
// Notify that we've shown a breakpoint in the source editor.
window.dispatchEvent(document, "Debugger:BreakpointShown", aEditorBreakpoint);
window.emit(EVENTS.BREAKPOINT_SHOWN, aEditorBreakpoint);
});
},
@ -1293,7 +1351,7 @@ Breakpoints.prototype = {
// 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.dispatchEvent(document, "Debugger:BreakpointHidden", aEditorBreakpoint);
window.emit(EVENTS.BREAKPOINT_HIDDEN, aEditorBreakpoint);
});
},
@ -1406,6 +1464,9 @@ Breakpoints.prototype = {
// Show the breakpoint in the editor and breakpoints pane, and resolve.
this._showBreakpoint(aBreakpointClient, aOptions);
// Notify that we've added a breakpoint.
window.emit(EVENTS.BREAKPOINT_ADDED, aBreakpointClient);
deferred.resolve(aBreakpointClient);
});
@ -1466,6 +1527,9 @@ Breakpoints.prototype = {
// Hide the breakpoint from the editor and breakpoints pane, and resolve.
this._hideBreakpoint(aLocation, aOptions);
// Notify that we've removed a breakpoint.
window.emit(EVENTS.BREAKPOINT_REMOVED, aLocation);
deferred.resolve(aLocation);
});
});
@ -1631,6 +1695,11 @@ XPCOMUtils.defineLazyGetter(window, "_isChromeDebugger", function() {
return !(window.frameElement instanceof XULElement);
});
/**
* Convenient way of emitting events from the panel window.
*/
EventEmitter.decorate(this);
/**
* Preliminary setup for the DebuggerController object.
*/

View File

@ -52,8 +52,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
this._editorDeck = document.getElementById("editor-deck");
this._stopBlackBoxButton = document.getElementById("black-boxed-message-button");
window.addEventListener("Debugger:EditorLoaded", this._onEditorLoad, false);
window.addEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
window.on(EVENTS.EDITOR_LOADED, this._onEditorLoad, false);
window.on(EVENTS.EDITOR_UNLOADED, this._onEditorUnload, false);
this.widget.addEventListener("select", this._onSourceSelect, false);
this.widget.addEventListener("click", this._onSourceClick, false);
this.widget.addEventListener("check", this._onSourceCheck, false);
@ -76,8 +76,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
destroy: function() {
dumpn("Destroying the SourcesView");
window.removeEventListener("Debugger:EditorLoaded", this._onEditorLoad, false);
window.removeEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
window.off(EVENTS.EDITOR_LOADED, this._onEditorLoad, false);
window.off(EVENTS.EDITOR_UNLOADED, this._onEditorUnload, false);
this.widget.removeEventListener("select", this._onSourceSelect, false);
this.widget.removeEventListener("click", this._onSourceClick, false);
this.widget.removeEventListener("check", this._onSourceCheck, false);
@ -600,17 +600,17 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
/**
* The load listener for the source editor.
*/
_onEditorLoad: function({ detail: editor }) {
editor.addEventListener("Selection", this._onEditorSelection, false);
editor.addEventListener("ContextMenu", this._onEditorContextMenu, false);
_onEditorLoad: function(aName, aEditor) {
aEditor.addEventListener(SourceEditor.EVENTS.SELECTION, this._onEditorSelection, false);
aEditor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU, this._onEditorContextMenu, false);
},
/**
* The unload listener for the source editor.
*/
_onEditorUnload: function({ detail: editor }) {
editor.removeEventListener("Selection", this._onEditorSelection, false);
editor.removeEventListener("ContextMenu", this._onEditorContextMenu, false);
_onEditorUnload: function(aName, aEditor) {
aEditor.removeEventListener(SourceEditor.EVENTS.SELECTION, this._onEditorSelection, false);
aEditor.removeEventListener(SourceEditor.EVENTS.CONTEXT_MENU, this._onEditorContextMenu, false);
},
/**
@ -740,6 +740,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
*/
_onConditionalPopupShowing: function() {
this._conditionalPopupVisible = true; // Used in tests.
window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWING);
},
/**
@ -755,6 +756,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
*/
_onConditionalPopupHiding: function() {
this._conditionalPopupVisible = false; // Used in tests.
window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_HIDING);
},
/**
@ -1517,7 +1519,6 @@ GlobalSearchView.prototype = Heritage.extend(WidgetMethods, {
clearView: function() {
this.hidden = true;
this.empty();
window.dispatchEvent(document, "Debugger:GlobalSearch:ViewCleared");
},
/**
@ -1589,7 +1590,6 @@ GlobalSearchView.prototype = Heritage.extend(WidgetMethods, {
// Don't continue filtering if the searched token is an empty string.
if (!aToken) {
this.clearView();
window.dispatchEvent(document, "Debugger:GlobalSearch:TokenEmpty");
return;
}
@ -1657,9 +1657,9 @@ GlobalSearchView.prototype = Heritage.extend(WidgetMethods, {
this.hidden = false;
this._currentlyFocusedMatch = -1;
this._createGlobalResultsUI(globalResults);
window.dispatchEvent(document, "Debugger:GlobalSearch:MatchFound");
window.emit(EVENTS.GLOBAL_SEARCH_MATCH_FOUND);
} else {
window.dispatchEvent(document, "Debugger:GlobalSearch:MatchNotFound");
window.emit(EVENTS.GLOBAL_SEARCH_MATCH_NOT_FOUND);
}
},

View File

@ -233,6 +233,7 @@ OptionsView.prototype = {
*/
_onPopupShowing: function() {
this._button.setAttribute("open", "true");
window.emit(EVENTS.OPTIONS_POPUP_SHOWING);
},
/**
@ -246,7 +247,7 @@ OptionsView.prototype = {
* Listener handling the 'gear menu' popup hidden event.
*/
_onPopupHidden: function() {
window.dispatchEvent(document, "Debugger:OptionsPopupHidden");
window.emit(EVENTS.OPTIONS_POPUP_HIDDEN);
},
/**
@ -306,9 +307,7 @@ OptionsView.prototype = {
this._showOriginalSourceItem.getAttribute("checked") == "true";
// Don't block the UI while reconfiguring the server.
window.addEventListener("Debugger:OptionsPopupHidden", function onHidden() {
window.removeEventListener("Debugger:OptionsPopupHidden", onHidden, false);
window.once(EVENTS.OPTIONS_POPUP_HIDDEN, () => {
// The popup panel needs more time to hide after triggering onpopuphidden.
window.setTimeout(() => {
DebuggerController.reconfigureThread(pref);
@ -1308,6 +1307,7 @@ FilteredSourcesView.prototype = Heritage.extend(ResultsPanelContainer.prototype,
// If there are no matches found, keep the popup hidden and avoid
// creating the view.
if (!aSearchResults.length) {
window.emit(EVENTS.FILE_SEARCH_MATCH_NOT_FOUND);
return;
}
@ -1328,6 +1328,9 @@ FilteredSourcesView.prototype = Heritage.extend(ResultsPanelContainer.prototype,
// Select the first entry in this container.
this.selectedIndex = 0;
this.hidden = false;
// Signal that file search matches were found and displayed.
window.emit(EVENTS.FILE_SEARCH_MATCH_FOUND);
},
/**
@ -1479,6 +1482,7 @@ FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototyp
// If there are no matches found, keep the popup hidden and avoid
// creating the view.
if (!aSearchResults.length) {
window.emit(EVENTS.FUNCTION_SEARCH_MATCH_NOT_FOUND);
return;
}
@ -1523,6 +1527,9 @@ FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototyp
// Select the first entry in this container.
this.selectedIndex = 0;
this.hidden = false;
// Signal that function search matches were found and displayed.
window.emit(EVENTS.FUNCTION_SEARCH_MATCH_FOUND);
},
/**

View File

@ -155,10 +155,10 @@ let DebuggerView = {
this.Variables.on("fetched", (aEvent, aType) => {
switch (aType) {
case "variables":
window.dispatchEvent(document, "Debugger:FetchedVariables");
window.emit(EVENTS.FETCHED_VARIABLES);
break;
case "properties":
window.dispatchEvent(document, "Debugger:FetchedProperties");
window.emit(EVENTS.FETCHED_PROPERTIES);
break;
}
});
@ -191,7 +191,7 @@ let DebuggerView = {
dumpn("Finished loading the DebuggerView editor");
DebuggerController.Breakpoints.initialize().then(() => {
window.dispatchEvent(document, "Debugger:EditorLoaded", this.editor);
window.emit(EVENTS.EDITOR_LOADED, this.editor);
aCallback();
});
},
@ -207,7 +207,7 @@ let DebuggerView = {
dumpn("Destroying the DebuggerView editor");
DebuggerController.Breakpoints.destroy().then(() => {
window.dispatchEvent(document, "Debugger:EditorUnloaded", this.editor);
window.emit(EVENTS.EDITOR_UNLOADED, this.editor);
aCallback();
});
},
@ -300,7 +300,7 @@ let DebuggerView = {
DebuggerController.Breakpoints.updateEditorBreakpoints();
// Resolve and notify that a source file was shown.
window.dispatchEvent(document, "Debugger:SourceShown", aSource);
window.emit(EVENTS.SOURCE_SHOWN, aSource);
deferred.resolve([aSource, aText]);
},
([, aError]) => {
@ -310,7 +310,7 @@ let DebuggerView = {
dumpn(msg);
// Reject and notify that there was an error showing the source file.
window.dispatchEvent(document, "Debugger:SourceErrorShown", aError);
window.emit(EVENTS.SOURCE_ERROR_SHOWN, aSource);
deferred.reject([aSource, aError]);
});