Bug 927375 - Debugger UI should be responsive when docked to the side, r=fitzgen

This commit is contained in:
Victor Porof 2013-10-19 12:26:46 +03:00
Родитель fd8bcaa0b2
Коммит 2e49a7a653
11 изменённых файлов: 364 добавлений и 89 удалений

Просмотреть файл

@ -70,6 +70,9 @@ const EVENTS = {
// When the options popup is showing or hiding.
OPTIONS_POPUP_SHOWING: "Debugger:OptionsPopupShowing",
OPTIONS_POPUP_HIDDEN: "Debugger:OptionsPopupHidden",
// When the widgets layout has been changed.
LAYOUT_CHANGED: "Debugger:LayoutChanged"
};
Cu.import("resource://gre/modules/Services.jsm");
@ -277,7 +280,7 @@ let DebuggerController = {
switch (aType) {
case "will-navigate": {
// Reset UI.
DebuggerView._handleTabNavigation();
DebuggerView.handleTabNavigation();
// Discard all the cached sources *before* the target starts navigating.
// Sources may be fetched during navigation, in which case we don't
@ -293,9 +296,9 @@ let DebuggerController = {
break;
}
case "navigate": {
this.ThreadState._handleTabNavigation();
this.StackFrames._handleTabNavigation();
this.SourceScripts._handleTabNavigation();
this.ThreadState.handleTabNavigation();
this.StackFrames.handleTabNavigation();
this.SourceScripts.handleTabNavigation();
break;
}
}
@ -401,8 +404,8 @@ let DebuggerController = {
}
// Reset the view and fetch all the sources again.
DebuggerView._handleTabNavigation();
this.SourceScripts._handleTabNavigation();
DebuggerView.handleTabNavigation();
this.SourceScripts.handleTabNavigation();
// Update the stack frame list.
this.activeThread._clearFrames();
@ -464,7 +467,7 @@ ThreadState.prototype = {
this.activeThread.addListener("resumed", this._update);
this.activeThread.pauseOnExceptions(Prefs.pauseOnExceptions,
Prefs.ignoreCaughtExceptions);
this._handleTabNavigation();
this.handleTabNavigation();
},
/**
@ -482,7 +485,7 @@ ThreadState.prototype = {
/**
* Handles any initialization on a tab navigation event issued by the client.
*/
_handleTabNavigation: function() {
handleTabNavigation: function() {
if (!this.activeThread) {
return;
}
@ -538,7 +541,7 @@ StackFrames.prototype = {
this.activeThread.addListener("framesadded", this._onFrames);
this.activeThread.addListener("framescleared", this._onFramesCleared);
this.activeThread.addListener("blackboxchange", this._onBlackBoxChange);
this._handleTabNavigation();
this.handleTabNavigation();
},
/**
@ -559,7 +562,7 @@ StackFrames.prototype = {
/**
* Handles any initialization on a tab navigation event issued by the client.
*/
_handleTabNavigation: function() {
handleTabNavigation: function() {
dumpn("Handling tab navigation in the StackFrames");
// Nothing to do here yet.
},
@ -1007,7 +1010,7 @@ SourceScripts.prototype = {
this.debuggerClient.addListener("newGlobal", this._onNewGlobal);
this.debuggerClient.addListener("newSource", this._onNewSource);
this.activeThread.addListener("blackboxchange", this._onBlackBoxChange);
this._handleTabNavigation();
this.handleTabNavigation();
},
/**
@ -1033,7 +1036,7 @@ SourceScripts.prototype = {
/**
* Handles any initialization on a tab navigation event issued by the client.
*/
_handleTabNavigation: function() {
handleTabNavigation: function() {
if (!this.activeThread) {
return;
}
@ -1902,6 +1905,9 @@ Object.defineProperties(window, {
"gTarget": {
get: function() DebuggerController._target
},
"gHostType": {
get: function() DebuggerView._hostType
},
"gClient": {
get: function() DebuggerController.client
},

Просмотреть файл

@ -16,8 +16,10 @@ function DebuggerPanel(iframeWindow, toolbox) {
this._view = this.panelWin.DebuggerView;
this._controller = this.panelWin.DebuggerController;
this._view._hostType = this._toolbox.hostType;
this._controller._target = this.target;
this.handleHostChanged = this.handleHostChanged.bind(this);
this.highlightWhenPaused = this.highlightWhenPaused.bind(this);
this.unhighlightWhenResumed = this.unhighlightWhenResumed.bind(this);
@ -47,6 +49,7 @@ DebuggerPanel.prototype = {
.then(() => this._controller.startupDebugger())
.then(() => this._controller.connect())
.then(() => {
this._toolbox.on("host-changed", this.handleHostChanged);
this.target.on("thread-paused", this.highlightWhenPaused);
this.target.on("thread-resumed", this.unhighlightWhenResumed);
this.isReady = true;
@ -87,6 +90,10 @@ DebuggerPanel.prototype = {
return this._controller.Breakpoints.removeBreakpoint(aLocation);
},
handleHostChanged: function() {
this._view.handleHostChanged(this._toolbox.hostType);
},
highlightWhenPaused: function() {
this._toolbox.highlightTool("jsdebugger");

Просмотреть файл

@ -110,6 +110,8 @@ let DebuggerView = {
_initializePanes: function() {
dumpn("Initializing the DebuggerView panes");
this._body = document.getElementById("body");
this._editorDeck = document.getElementById("editor-deck");
this._sourcesPane = document.getElementById("sources-pane");
this._instrumentsPane = document.getElementById("instruments-pane");
this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
@ -118,7 +120,6 @@ let DebuggerView = {
this.showBlackBoxMessage = this.showBlackBoxMessage.bind(this);
this.showProgressBar = this.showProgressBar.bind(this);
this.maybeShowBlackBoxMessage = this.maybeShowBlackBoxMessage.bind(this);
this._editorDeck = document.getElementById("editor-deck");
this._onTabSelect = this._onInstrumentsPaneTabSelect.bind(this);
this._instrumentsPane.tabpanels.addEventListener("select", this._onTabSelect);
@ -129,6 +130,11 @@ let DebuggerView = {
this._sourcesPane.setAttribute("width", Prefs.sourcesWidth);
this._instrumentsPane.setAttribute("width", Prefs.instrumentsWidth);
this.toggleInstrumentsPane({ visible: Prefs.panesVisibleOnStartup });
// Side hosts requires a different arrangement of the debugger widgets.
if (gHostType == "side") {
this.handleHostChanged(gHostType);
}
},
/**
@ -137,10 +143,10 @@ let DebuggerView = {
_destroyPanes: function() {
dumpn("Destroying the DebuggerView panes");
Prefs.sourcesWidth = this._sourcesPane.getAttribute("width");
Prefs.instrumentsWidth = this._instrumentsPane.getAttribute("width");
this._instrumentsPane.tabpanels.removeEventListener("select", this._onTabSelect);
if (gHostType != "side") {
Prefs.sourcesWidth = this._sourcesPane.getAttribute("width");
Prefs.instrumentsWidth = this._instrumentsPane.getAttribute("width");
}
this._sourcesPane = null;
this._instrumentsPane = null;
@ -516,10 +522,69 @@ let DebuggerView = {
}
},
/**
* Handles a host change event issued by the parent toolbox.
*
* @param string aType
* The host type, either "bottom", "side" or "window".
*/
handleHostChanged: function(aType) {
let newLayout = "";
if (aType == "side") {
newLayout = "vertical";
this._enterVerticalLayout();
} else {
newLayout = "horizontal";
this._enterHorizontalLayout();
}
this._hostType = aType;
this._body.setAttribute("layout", newLayout);
window.emit(EVENTS.LAYOUT_CHANGED, newLayout);
},
/**
* Switches the debugger widgets to a horizontal layout.
*/
_enterVerticalLayout: function() {
let normContainer = document.getElementById("debugger-widgets");
let vertContainer = document.getElementById("vertical-layout-panes-container");
// Move the soruces and instruments panes in a different container.
let splitter = document.getElementById("sources-and-instruments-splitter");
vertContainer.insertBefore(this._sourcesPane, splitter);
vertContainer.appendChild(this._instrumentsPane);
// Make sure the vertical layout container's height doesn't repeatedly
// grow or shrink based on the displayed sources, variables etc.
vertContainer.setAttribute("height",
vertContainer.getBoundingClientRect().height);
},
/**
* Switches the debugger widgets to a vertical layout.
*/
_enterHorizontalLayout: function() {
let normContainer = document.getElementById("debugger-widgets");
let vertContainer = document.getElementById("vertical-layout-panes-container");
// The sources and instruments pane need to be inserted at their
// previous locations in their normal container.
let splitter = document.getElementById("sources-and-editor-splitter");
normContainer.insertBefore(this._sourcesPane, splitter);
normContainer.appendChild(this._instrumentsPane);
// Revert to the preferred sources and instruments widths, because
// they flexed in the vertical layout.
this._sourcesPane.setAttribute("width", Prefs.sourcesWidth);
this._instrumentsPane.setAttribute("width", Prefs.instrumentsWidth);
},
/**
* Handles any initialization on a tab navigation event issued by the client.
*/
_handleTabNavigation: function() {
handleTabNavigation: function() {
dumpn("Handling tab navigation in the DebuggerView");
this.Filtering.clearSearch();
@ -557,12 +622,13 @@ let DebuggerView = {
editor: null,
_editorSource: {},
_loadingText: "",
_body: null,
_editorDeck: null,
_sourcesPane: null,
_instrumentsPane: null,
_instrumentsPaneToggleButton: null,
_collapsePaneString: "",
_expandPaneString: "",
_editorDeck: null,
_expandPaneString: ""
};
/**

Просмотреть файл

@ -26,3 +26,31 @@
.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
display: none;
}
/* Horizontal vs. vertical layout */
#body[layout=vertical] #debugger-widgets {
-moz-box-orient: vertical;
}
#body[layout=vertical] #sources-pane {
-moz-box-flex: 1;
}
#body[layout=vertical] #instruments-pane {
-moz-box-flex: 2;
}
#body[layout=vertical] #instruments-pane-toggle {
display: none;
}
#body[layout=vertical] #sources-and-editor-splitter,
#body[layout=vertical] #editor-and-instruments-splitter {
display: none;
}
#body[layout=horizontal] #vertical-layout-splitter,
#body[layout=horizontal] #vertical-layout-panes-container {
display: none;
}

Просмотреть файл

@ -283,8 +283,9 @@
command="removeAllWatchExpressionsCommand"/>
</keyset>
<vbox id="body" flex="1">
<toolbar class="devtools-toolbar">
<vbox id="body" layout="horizontal" flex="1">
<toolbar id="debugger-toolbar"
class="devtools-toolbar">
<hbox id="debugger-controls">
<toolbarbutton id="resume"
class="devtools-toolbarbutton"
@ -315,62 +316,68 @@
popup="debuggerPrefsContextMenu"
tabindex="0"/>
</toolbar>
<vbox flex="1">
<scrollbox id="globalsearch" orient="vertical" hidden="true"/>
<splitter class="devtools-horizontal-splitter" hidden="true"/>
<hbox flex="1">
<vbox id="sources-container">
<vbox id="sources-pane" flex="1">
<vbox id="sources" flex="1"/>
</vbox>
<toolbar id="sources-toolbar" class="devtools-toolbar">
<toolbarbutton id="pretty-print"
label="{}"
tooltiptext="&debuggerUI.sources.prettyPrint;"
class="devtools-toolbarbutton devtools-monospace"
command="prettyPrintCommand"
hidden="true"/>
</toolbar>
<scrollbox id="globalsearch" orient="vertical" hidden="true"/>
<splitter class="devtools-horizontal-splitter" hidden="true"/>
<hbox id="debugger-widgets" flex="1">
<vbox id="sources-pane">
<vbox id="sources" flex="1"/>
<toolbar id="sources-toolbar" class="devtools-toolbar">
<toolbarbutton id="pretty-print"
label="{}"
tooltiptext="&debuggerUI.sources.prettyPrint;"
class="devtools-toolbarbutton devtools-monospace"
command="prettyPrintCommand"
hidden="true"/>
</toolbar>
</vbox>
<splitter id="sources-and-editor-splitter"
class="devtools-side-splitter"/>
<deck id="editor-deck" flex="4">
<vbox id="editor"/>
<vbox id="black-boxed-message" align="center" pack="center">
<label id="black-boxed-message-label">
&debuggerUI.blackBoxMessage.label;
</label>
<button id="black-boxed-message-button"
class="devtools-toolbarbutton"
label="&debuggerUI.blackBoxMessage.unBlackBoxButton;"
image="chrome://browser/skin/devtools/blackBoxMessageEye.png"
command="unBlackBoxCommand"/>
</vbox>
<splitter class="devtools-side-splitter"/>
<deck id="editor-deck" flex="1" selectedIndex="0">
<vbox id="editor"/>
<vbox id="black-boxed-message" align="center">
<label id="black-boxed-message-label">
&debuggerUI.blackBoxMessage.label;
</label>
<button id="black-boxed-message-button"
class="devtools-toolbarbutton"
label="&debuggerUI.blackBoxMessage.unBlackBoxButton;"
image="chrome://browser/skin/devtools/blackBoxMessageEye.png"
command="unBlackBoxCommand"/>
</vbox>
<vbox id="source-progress-container" align="center" pack="center">
<progressmeter id="source-progress"
mode="undetermined"/>
</vbox>
</deck>
<splitter class="devtools-side-splitter"/>
<tabbox id="instruments-pane"
class="devtools-sidebar-tabs"
hidden="true">
<tabs>
<tab id="variables-tab" label="&debuggerUI.tabs.variables;"/>
<tab id="events-tab" label="&debuggerUI.tabs.events;"/>
</tabs>
<tabpanels flex="1">
<tabpanel id="variables-tabpanel">
<vbox id="expressions"/>
<splitter class="devtools-horizontal-splitter"/>
<vbox id="variables" flex="1"/>
</tabpanel>
<tabpanel id="events-tabpanel">
<vbox id="event-listeners" flex="1"/>
</tabpanel>
</tabpanels>
</tabbox>
<vbox id="source-progress-container" align="center" pack="center">
<progressmeter id="source-progress"
mode="undetermined"/>
</vbox>
</deck>
<splitter id="editor-and-instruments-splitter"
class="devtools-side-splitter"/>
<tabbox id="instruments-pane"
class="devtools-sidebar-tabs"
hidden="true">
<tabs>
<tab id="variables-tab" label="&debuggerUI.tabs.variables;"/>
<tab id="events-tab" label="&debuggerUI.tabs.events;"/>
</tabs>
<tabpanels flex="1">
<tabpanel id="variables-tabpanel">
<vbox id="expressions"/>
<splitter class="devtools-horizontal-splitter"/>
<vbox id="variables" flex="1"/>
</tabpanel>
<tabpanel id="events-tabpanel">
<vbox id="event-listeners" flex="1"/>
</tabpanel>
</tabpanels>
</tabbox>
<splitter id="vertical-layout-splitter"
class="devtools-horizontal-splitter"/>
<hbox id="vertical-layout-panes-container">
<splitter id="sources-and-instruments-splitter"
class="devtools-side-splitter"/>
<!-- The sources-pane and instruments-pane will be moved in this
container if the toolbox's host requires it. -->
</hbox>
</vbox>
</hbox>
</vbox>
<panel id="searchbox-help-panel"

Просмотреть файл

@ -96,6 +96,7 @@ support-files =
[browser_dbg_editor-mode.js]
[browser_dbg_function-display-name.js]
[browser_dbg_globalactor.js]
[browser_dbg_host-layout.js]
[browser_dbg_iframes.js]
[browser_dbg_instruments-pane-collapse.js]
[browser_dbg_listaddons.js]

Просмотреть файл

@ -0,0 +1,104 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This if the debugger's layout is correctly modified when the toolbox's
* host changes.
*/
let gDefaultHostType = Services.prefs.getCharPref("devtools.toolbox.host");
function test() {
Task.spawn(function() {
yield testHosts(["bottom", "side", "window"], ["horizontal", "vertical", "horizontal"]);
yield testHosts(["side", "bottom", "side"], ["vertical", "horizontal", "vertical"]);
yield testHosts(["bottom", "side", "bottom"], ["horizontal", "vertical", "horizontal"]);
yield testHosts(["side", "window", "side"], ["vertical", "horizontal", "vertical"]);
yield testHosts(["window", "side", "window"], ["horizontal", "vertical", "horizontal"]);
finish();
});
}
function testHosts(aHostTypes, aLayoutTypes) {
let [firstHost, secondHost, thirdHost] = aHostTypes;
let [firstLayout, secondLayout, thirdLayout] = aLayoutTypes;
Services.prefs.setCharPref("devtools.toolbox.host", firstHost);
return Task.spawn(function() {
let [tab, debuggee, panel] = yield initDebugger("about:blank");
yield testHost(tab, debuggee, panel, firstHost, firstLayout);
yield switchAndTestHost(tab, debuggee, panel, secondHost, secondLayout);
yield switchAndTestHost(tab, debuggee, panel, thirdHost, thirdLayout);
yield teardown(panel);
});
}
function switchAndTestHost(aTab, aDebuggee, aPanel, aHostType, aLayoutType) {
let gToolbox = aPanel._toolbox;
let gDebugger = aPanel.panelWin;
return Task.spawn(function() {
let layoutChanged = once(gDebugger, gDebugger.EVENTS.LAYOUT_CHANGED);
let hostChanged = gToolbox.switchHost(aHostType);
yield hostChanged;
ok(true, "The toolbox's host has changed.");
yield layoutChanged;
ok(true, "The debugger's layout has changed.");
yield testHost(aTab, aDebuggee, aPanel, aHostType, aLayoutType);
});
function once(aTarget, aEvent) {
let deferred = promise.defer();
aTarget.once(aEvent, deferred.resolve);
return deferred.promise;
}
}
function testHost(aTab, aDebuggee, aPanel, aHostType, aLayoutType) {
let gDebugger = aPanel.panelWin;
let gView = gDebugger.DebuggerView;
is(gView._hostType, aHostType,
"The default host type should've been set on the panel window (1).");
is(gDebugger.gHostType, aHostType,
"The default host type should've been set on the panel window (2).");
is(gView._body.getAttribute("layout"), aLayoutType,
"The default host type is present as an attribute on the panel's body.");
if (aLayoutType == "horizontal") {
is(gView._sourcesPane.parentNode.id, "debugger-widgets",
"The sources pane's parent is correct for the horizontal layout.");
is(gView._instrumentsPane.parentNode.id, "debugger-widgets",
"The instruments pane's parent is correct for the horizontal layout.");
} else {
is(gView._sourcesPane.parentNode.id, "vertical-layout-panes-container",
"The sources pane's parent is correct for the vertical layout.");
is(gView._instrumentsPane.parentNode.id, "vertical-layout-panes-container",
"The instruments pane's parent is correct for the vertical layout.");
}
let widgets = gDebugger.document.getElementById("debugger-widgets").childNodes;
let panes = gDebugger.document.getElementById("vertical-layout-panes-container").childNodes;
if (aLayoutType == "horizontal") {
is(widgets.length, 7, // 2 panes, 1 editor, 3 splitters and a phantom box.
"Found the correct number of debugger widgets.");
is(panes.length, 1, // 1 lonely splitter in the phantom box.
"Found the correct number of debugger panes.");
} else {
is(widgets.length, 5, // 1 editor, 3 splitters and a phantom box.
"Found the correct number of debugger widgets.");
is(panes.length, 3, // 2 panes and 1 splitter in the phantom box.
"Found the correct number of debugger panes.");
}
}
registerCleanupFunction(function() {
Services.prefs.setCharPref("devtools.toolbox.host", gDefaultHostType);
gDefaultHostType = null;
});

Просмотреть файл

@ -61,7 +61,6 @@ this.SideMenuWidget = function SideMenuWidget(aNode, aOptions={}) {
this._list.addEventListener("keypress", e => this.emit("keyPress", e), false);
this._list.addEventListener("mousedown", e => this.emit("mousePress", e), false);
this._parent.appendChild(this._list);
this._boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
// Menu items can optionally be grouped.
this._groupsByName = new Map(); // Can't use a WeakMap because keys are strings.
@ -245,8 +244,9 @@ SideMenuWidget.prototype = {
}
// Ensure the element is visible but not scrolled horizontally.
this._boxObject.ensureElementIsVisible(aElement);
this._boxObject.scrollBy(-aElement.clientWidth, 0);
let boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
boxObject.ensureElementIsVisible(aElement);
boxObject.scrollBy(-aElement.clientWidth, 0);
},
/**
@ -432,7 +432,6 @@ SideMenuWidget.prototype = {
_showGroupCheckboxes: false,
_parent: null,
_list: null,
_boxObject: null,
_selectedItem: null,
_groupsByName: null,
_orderedGroupElementsArray: null,

Просмотреть файл

@ -13,7 +13,7 @@
min-width: 50px;
}
#sources-container + .devtools-side-splitter {
#sources-and-editor-splitter {
-moz-border-start-color: transparent;
}
@ -83,7 +83,6 @@
background: url(background-noise-toolbar.png) rgb(61,69,76);
/* Prevent the container deck from aquiring the height from this message. */
min-height: 1px;
padding: 25vh 0;
color: white;
}
@ -170,7 +169,7 @@
/* Instruments pane (watch expressions, variables, event listeners...) */
#instruments-pane > tabs > tab {
min-height: 2em !important;
min-height: 25px !important;
padding: 0 !important;
}
@ -440,3 +439,23 @@
#instruments-pane-toggle:active {
-moz-image-region: rect(0px,32px,16px,16px);
}
/* Horizontal vs. vertical layout */
#vertical-layout-panes-container {
min-height: 35vh;
max-height: 80vh;
}
#body[layout=vertical] #instruments-pane {
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar. */
}
#body[layout=vertical] .side-menu-widget-container {
box-shadow: none !important;
}
#body[layout=vertical] .side-menu-widget-item-arrow {
background-image: none !important;
}

Просмотреть файл

@ -15,7 +15,7 @@
min-width: 50px;
}
#sources-container + .devtools-side-splitter {
#sources-and-editor-splitter {
-moz-border-start-color: transparent;
}
@ -81,7 +81,6 @@
background: url(background-noise-toolbar.png) rgb(61,69,76);
/* Prevent the container deck from aquiring the height from this message. */
min-height: 1px;
padding: 25vh 0;
color: white;
}
@ -168,7 +167,7 @@
/* Instruments pane (watch expressions, variables, event listeners...) */
#instruments-pane > tabs > tab {
min-height: 2em !important;
min-height: 1em !important;
padding: 0 !important;
}
@ -438,3 +437,23 @@
#instruments-pane-toggle:active {
-moz-image-region: rect(0px,32px,16px,16px);
}
/* Horizontal vs. vertical layout */
#vertical-layout-panes-container {
min-height: 35vh;
max-height: 80vh;
}
#body[layout=vertical] #instruments-pane {
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar. */
}
#body[layout=vertical] .side-menu-widget-container {
box-shadow: none !important;
}
#body[layout=vertical] .side-menu-widget-item-arrow {
background-image: none !important;
}

Просмотреть файл

@ -13,7 +13,7 @@
min-width: 50px;
}
#sources-container + .devtools-side-splitter {
#sources-and-editor-splitter {
-moz-border-start-color: transparent;
}
@ -81,7 +81,6 @@
background: url(background-noise-toolbar.png) rgb(61,69,76);
/* Prevent the container deck from aquiring the height from this message. */
min-height: 1px;
padding: 25vh 0;
color: white;
}
@ -168,7 +167,7 @@
/* Instruments pane (watch expressions, variables, event listeners...) */
#instruments-pane > tabs > tab {
min-height: 2em !important;
min-height: 25px !important;
padding: 0 !important;
}
@ -443,3 +442,23 @@
#instruments-pane-toggle:hover:active {
-moz-image-region: rect(0px,48px,16px,32px);
}
/* Horizontal vs. vertical layout */
#vertical-layout-panes-container {
min-height: 35vh;
max-height: 80vh;
}
#body[layout=vertical] #instruments-pane {
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar. */
}
#body[layout=vertical] .side-menu-widget-container {
box-shadow: none !important;
}
#body[layout=vertical] .side-menu-widget-item-arrow {
background-image: none !important;
}