Bug 862558 - Web Console should always be available / visible;r=msucan

This commit is contained in:
Brian Grinstead 2013-11-11 15:13:28 -06:00
parent 4102c084a6
commit be53323151
5 changed files with 329 additions and 6 deletions

View File

@ -1516,6 +1516,7 @@ WatchExpressionsView.prototype = Heritage.extend(WidgetMethods, {
case e.DOM_VK_RETURN:
case e.DOM_VK_ENTER:
case e.DOM_VK_ESCAPE:
e.stopPropagation();
DebuggerView.editor.focus();
return;
}

View File

@ -67,6 +67,7 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._toolRegistered = this._toolRegistered.bind(this);
this._toolUnregistered = this._toolUnregistered.bind(this);
this._refreshHostTitle = this._refreshHostTitle.bind(this);
this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this)
this.destroy = this.destroy.bind(this);
this._target.on("close", this.destroy);
@ -229,11 +230,55 @@ Toolbox.prototype = {
}, true);
},
_splitConsoleOnKeypress: function(e) {
if (e.keyCode === e.DOM_VK_ESCAPE) {
this.toggleSplitConsole();
}
},
_addToolSwitchingKeys: function() {
let nextKey = this.doc.getElementById("toolbox-next-tool-key");
nextKey.addEventListener("command", this.selectNextTool.bind(this), true);
let prevKey = this.doc.getElementById("toolbox-previous-tool-key");
prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
// Split console uses keypress instead of command so the event can be
// cancelled with stopPropagation on the keypress, and not preventDefault.
this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
},
/**
* Make sure that the console is showing up properly based on all the
* possible conditions.
* 1) If the console tab is selected, then regardless of split state
* it should take up the full height of the deck, and we should
* hide the deck and splitter.
* 2) If the console tab is not selected and it is split, then we should
* show the splitter, deck, and console.
* 3) If the console tab is not selected and it is *not* split,
* then we should hide the console and splitter, and show the deck
* at full height.
*/
_refreshConsoleDisplay: function() {
let deck = this.doc.getElementById("toolbox-deck");
let webconsolePanel = this.doc.getElementById("toolbox-panel-webconsole");
let splitter = this.doc.getElementById("toolbox-console-splitter");
let openedConsolePanel = this.currentToolId === "webconsole";
if (openedConsolePanel) {
deck.setAttribute("collapsed", "true");
splitter.setAttribute("hidden", "true");
webconsolePanel.removeAttribute("collapsed");
} else {
deck.removeAttribute("collapsed");
if (this._splitConsole) {
webconsolePanel.removeAttribute("collapsed");
splitter.removeAttribute("hidden");
} else {
webconsolePanel.setAttribute("collapsed", "true");
splitter.setAttribute("hidden", "true");
}
}
},
/**
@ -502,8 +547,11 @@ Toolbox.prototype = {
}
let vbox = this.doc.createElement("vbox");
vbox.className = "toolbox-panel " + toolDefinition.bgTheme;
vbox.id = "toolbox-panel-" + id;
// There is already a container for the webconsole frame.
if (!this.doc.getElementById("toolbox-panel-" + id)) {
vbox.id = "toolbox-panel-" + id;
}
// If there is no tab yet, or the ordinal to be added is the largest one.
if (tabs.childNodes.length == 0 ||
@ -613,7 +661,6 @@ Toolbox.prototype = {
let tab = this.doc.getElementById("toolbox-tab-" + id);
tab.setAttribute("selected", "true");
if (this.currentToolId == id) {
// re-focus tool to get key events again
this.focusTool(id);
@ -656,6 +703,7 @@ Toolbox.prototype = {
deck.selectedIndex = index;
this.currentToolId = id;
this._refreshConsoleDisplay();
if (id != "options") {
Services.prefs.setCharPref(this._prefs.LAST_TOOL, id);
}
@ -680,6 +728,27 @@ Toolbox.prototype = {
iframe.focus();
},
/**
* Toggles the split state of the webconsole. If the webconsole panel
* is already selected, then this command is ignored.
*/
toggleSplitConsole: function() {
let openedConsolePanel = this.currentToolId === "webconsole";
// Don't allow changes when console is open, since it could be confusing
if (!openedConsolePanel) {
this._splitConsole = !this._splitConsole;
this._refreshConsoleDisplay();
this.emit("split-console");
if (this._splitConsole) {
this.loadTool("webconsole").then(() => {
this.focusTool("webconsole");
});
}
}
},
/**
* Loads the tool next to the currently selected tool.
*/
@ -789,7 +858,7 @@ Toolbox.prototype = {
iframe.swapFrameLoaders(this.frame);
this._host.off("window-closed", this.destroy);
this._host.destroy();
this.destroyHost();
this._host = newHost;
@ -876,6 +945,17 @@ Toolbox.prototype = {
return this.doc.getElementById("toolbox-notificationbox");
},
/**
* Destroy the current host, and remove event listeners from its frame.
*
* @return {promise} to be resolved when the host is destroyed.
*/
destroyHost: function() {
this.doc.removeEventListener("keypress",
this._splitConsoleOnKeypress, false);
return this._host.destroy();
},
/**
* Remove all UI elements, detach from target and clear up
*/
@ -917,7 +997,7 @@ Toolbox.prototype = {
container.removeChild(container.firstChild);
}
outstanding.push(this._host.destroy());
outstanding.push(this.destroyHost());
this._telemetry.destroy();

View File

@ -74,7 +74,10 @@
</hbox>
#endif
</toolbar>
<deck id="toolbox-deck" flex="1">
</deck>
<vbox flex="1">
<deck id="toolbox-deck" flex="1" minheight="75" />
<splitter id="toolbox-console-splitter" class="devtools-horizontal-splitter" hidden="true" />
<box minheight="75" flex="1" id="toolbox-panel-webconsole" collapsed="true" />
</vbox>
</notificationbox>
</window>

View File

@ -236,6 +236,7 @@ skip-if = os == "linux"
[browser_webconsole_output_order.js]
[browser_webconsole_property_provider.js]
[browser_webconsole_scratchpad_panel_link.js]
[browser_webconsole_split.js]
[browser_webconsole_view_source.js]
[browser_webconsole_reflow.js]
[browser_webconsole_log_file_filter.js]

View File

@ -0,0 +1,238 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
function test()
{
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
let Toolbox = devtools.Toolbox;
let toolbox;
addTab("data:text/html;charset=utf-8,Web Console test for splitting");
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
testConsoleLoadOnDifferentPanel()
}, true);
function testConsoleLoadOnDifferentPanel()
{
info("About to check console loads even when non-webconsole panel is open");
openPanel("inspector").then(() => {
toolbox.on("webconsole-ready", () => {
ok(true, "Webconsole has been triggered as loaded while another tool is active");
testKeyboardShortcuts();
});
// Opens split console.
toolbox.toggleSplitConsole();
});
}
function testKeyboardShortcuts()
{
info("About to check that panel responds to ESCAPE keyboard shortcut");
toolbox.once("split-console", () => {
ok(true, "Split console has been triggered via ESCAPE keypress");
checkAllTools();
});
// Closes split console.
EventUtils.sendKey("ESCAPE", toolbox.frame.contentWindow);
}
function checkAllTools()
{
info("About to check split console with each panel individually.");
Task.spawn(function() {
yield openAndCheckPanel("jsdebugger");
yield openAndCheckPanel("inspector");
yield openAndCheckPanel("styleeditor");
yield openAndCheckPanel("jsprofiler");
yield openAndCheckPanel("netmonitor");
yield checkWebconsolePanelOpened();
testBottomHost();
});
}
function getCurrentUIState()
{
let win = toolbox.doc.defaultView;
let deck = toolbox.doc.getElementById("toolbox-deck");
let webconsolePanel = toolbox.doc.getElementById("toolbox-panel-webconsole");
let splitter = toolbox.doc.getElementById("toolbox-console-splitter");
let containerHeight = parseFloat(win.getComputedStyle(deck.parentNode).getPropertyValue("height"));
let deckHeight = parseFloat(win.getComputedStyle(deck).getPropertyValue("height"));
let webconsoleHeight = parseFloat(win.getComputedStyle(webconsolePanel).getPropertyValue("height"));
let splitterVisibility = !splitter.getAttribute("hidden");
let openedConsolePanel = toolbox.currentToolId === "webconsole";
return {
deckHeight: deckHeight,
containerHeight: containerHeight,
webconsoleHeight: webconsoleHeight,
splitterVisibility: splitterVisibility,
openedConsolePanel: openedConsolePanel
};
}
function checkWebconsolePanelOpened()
{
info("About to check special cases when webconsole panel is open.");
let deferred = promise.defer();
// Start with console split, so we can test for transition to main panel.
toolbox.toggleSplitConsole();
let currentUIState = getCurrentUIState();
ok (currentUIState.splitterVisibility, "Splitter is visible when console is split");
ok (currentUIState.deckHeight > 0, "Deck has a height > 0 when console is split");
ok (currentUIState.webconsoleHeight > 0, "Web console has a height > 0 when console is split");
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
openPanel("webconsole").then(() => {
let currentUIState = getCurrentUIState();
ok (!currentUIState.splitterVisibility, "Splitter is hidden when console is opened.");
is (currentUIState.deckHeight, 0, "Deck has a height == 0 when console is opened.");
is (currentUIState.webconsoleHeight, currentUIState.containerHeight, "Web console is full height.");
ok (currentUIState.openedConsolePanel, "The console panel is the current tool");
// Make sure splitting console does nothing while webconsole is opened
toolbox.toggleSplitConsole();
let currentUIState = getCurrentUIState();
ok (!currentUIState.splitterVisibility, "Splitter is hidden when console is opened.");
is (currentUIState.deckHeight, 0, "Deck has a height == 0 when console is opened.");
is (currentUIState.webconsoleHeight, currentUIState.containerHeight, "Web console is full height.");
ok (currentUIState.openedConsolePanel, "The console panel is the current tool");
// Make sure that split state is saved after opening another panel
openPanel("inspector").then(() => {
let currentUIState = getCurrentUIState();
ok (currentUIState.splitterVisibility, "Splitter is visible when console is split");
ok (currentUIState.deckHeight > 0, "Deck has a height > 0 when console is split");
ok (currentUIState.webconsoleHeight > 0, "Web console has a height > 0 when console is split");
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
toolbox.toggleSplitConsole();
deferred.resolve();
});
});
return deferred.promise;
}
function openPanel(toolId, callback)
{
let deferred = promise.defer();
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, toolId).then(function(box) {
toolbox = box;
deferred.resolve();
}).then(null, console.error);
return deferred.promise;
}
function openAndCheckPanel(toolId)
{
let deferred = promise.defer();
openPanel(toolId).then(() => {
info ("Checking toolbox for " + toolId);
checkToolboxUI(toolbox.getCurrentPanel());
deferred.resolve();
});
return deferred.promise;
}
function checkToolboxUI()
{
let currentUIState = getCurrentUIState();
ok (!currentUIState.splitterVisibility, "Splitter is hidden by default");
is (currentUIState.deckHeight, currentUIState.containerHeight, "Deck has a height > 0 by default");
is (currentUIState.webconsoleHeight, 0, "Web console is collapsed by default");
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
toolbox.toggleSplitConsole();
let currentUIState = getCurrentUIState();
ok (currentUIState.splitterVisibility, "Splitter is visible when console is split");
ok (currentUIState.deckHeight > 0, "Deck has a height > 0 when console is split");
ok (currentUIState.webconsoleHeight > 0, "Web console has a height > 0 when console is split");
is (currentUIState.deckHeight + currentUIState.webconsoleHeight,
currentUIState.containerHeight,
"Everything adds up to container height");
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
toolbox.toggleSplitConsole();
let currentUIState = getCurrentUIState();
ok (!currentUIState.splitterVisibility, "Splitter is hidden after toggling");
is (currentUIState.deckHeight, currentUIState.containerHeight, "Deck has a height > 0 after toggling");
is (currentUIState.webconsoleHeight, 0, "Web console is collapsed after toggling");
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
}
function testBottomHost()
{
checkHostType(Toolbox.HostType.BOTTOM);
checkToolboxUI();
toolbox.switchHost(Toolbox.HostType.SIDE).then(testSidebarHost);
}
function testSidebarHost()
{
checkHostType(Toolbox.HostType.SIDE);
checkToolboxUI();
toolbox.switchHost(Toolbox.HostType.WINDOW).then(testWindowHost);
}
function testWindowHost()
{
checkHostType(Toolbox.HostType.WINDOW);
checkToolboxUI();
testDestroy();
}
function checkHostType(hostType)
{
is(toolbox.hostType, hostType, "host type is " + hostType);
let pref = Services.prefs.getCharPref("devtools.toolbox.host");
is(pref, hostType, "host pref is " + hostType);
}
function testDestroy()
{
toolbox.destroy().then(function() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target).then(finish);
});
}
function finish()
{
toolbox = null;
finishTest();
}
}