Bug 1245921 - Turn toolbox toolbar into a React component r+miker r=miker

MozReview-Commit-ID: 4UZbcfw2YI9

--HG--
extra : rebase_source : 9e9e8bfed8c8511ade6c7307a0201b88b0781fba
This commit is contained in:
Greg Tatum 2016-11-18 15:02:21 -06:00
Родитель 8eca822d58
Коммит 555caa01f2
39 изменённых файлов: 1317 добавлений и 784 удалений

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

@ -27,11 +27,13 @@ const test = function(unit) {
return function*(assert) {
assert.isRendered = (panel, toolbox) => {
const doc = toolbox.doc;
assert.ok(doc.querySelector("[value='" + panel.label + "']"),
"panel.label is found in the developer toolbox DOM");
assert.ok(doc.querySelector("[tooltiptext='" + panel.tooltip + "']"),
"panel.tooltip is found in the developer toolbox DOM");
assert.ok(Array.from(doc.querySelectorAll(".devtools-tab"))
.find(el => el.textContent === panel.label),
"panel.label is found in the developer toolbox DOM " + panel.label);
if (panel.tooltip) {
assert.ok(doc.querySelector("[title='" + panel.tooltip + "']"),
`panel.tooltip is found in the developer toolbox DOM "${panel.tooltip}"`);
}
assert.ok(doc.querySelector("#toolbox-panel-" + panel.id),
"toolbar panel with a matching id is present");
};

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

@ -17,6 +17,7 @@ var PREFS = [
["devtools.netmonitor.enabled", true],
["devtools.scratchpad.enabled", true]
];
function test() {
Task.spawn(function* () {
// Store and enable all optional dev tools panels
@ -26,20 +27,19 @@ function test() {
let addonDebugger = yield initAddonDebugger(ADDON_ID);
// Check only valid tabs are shown
let tabs = addonDebugger.frame.contentDocument.getElementById("toolbox-tabs").children;
let tabs = addonDebugger.frame.contentDocument.querySelectorAll(".toolbox-tabs button")
let expectedTabs = ["webconsole", "jsdebugger", "scratchpad"];
is(tabs.length, expectedTabs.length, "displaying only " + expectedTabs.length + " tabs in addon debugger");
Array.forEach(tabs, (tab, i) => {
let toolName = expectedTabs[i];
is(tab.getAttribute("toolid"), toolName, "displaying " + toolName);
is(tab.getAttribute("data-id"), toolName, "displaying " + toolName);
});
// Check no toolbox buttons are shown
let buttons = addonDebugger.frame.contentDocument.getElementById("toolbox-buttons").children;
Array.forEach(buttons, (btn, i) => {
is(btn.hidden, true, "no toolbox buttons for the addon debugger -- " + btn.className);
});
let buttons = addonDebugger.frame.contentDocument.querySelectorAll("#toolbox-buttons-end button");
is(buttons.length, 0, "no toolbox buttons for the addon debugger");
yield addonDebugger.destroy();
yield removeAddon(addon);

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

@ -35,18 +35,14 @@ function testPause() {
gDebugger.gThreadClient.addOneTimeListener("paused", () => {
gToolbox.selectTool("webconsole").then(() => {
ok(gToolboxTab.hasAttribute("highlighted") &&
gToolboxTab.getAttribute("highlighted") == "true",
ok(gToolboxTab.classList.contains("highlighted"),
"The highlighted class is present");
ok(!gToolboxTab.hasAttribute("selected") ||
gToolboxTab.getAttribute("selected") != "true",
ok(!gToolboxTab.classList.contains("selected"),
"The tab is not selected");
}).then(() => gToolbox.selectTool("jsdebugger")).then(() => {
ok(gToolboxTab.hasAttribute("highlighted") &&
gToolboxTab.getAttribute("highlighted") == "true",
ok(gToolboxTab.classList.contains("highlighted"),
"The highlighted class is present");
ok(gToolboxTab.hasAttribute("selected") &&
gToolboxTab.getAttribute("selected") == "true",
ok(gToolboxTab.classList.contains("selected"),
"...and the tab is selected, so the glow will not be present.");
}).then(testResume);
});
@ -66,8 +62,7 @@ function testResume() {
gToolbox.selectTool("webconsole").then(() => {
ok(!gToolboxTab.classList.contains("highlighted"),
"The highlighted class is not present now after the resume");
ok(!gToolboxTab.hasAttribute("selected") ||
gToolboxTab.getAttribute("selected") != "true",
ok(!gToolboxTab.classList.contains("selected"),
"The tab is not selected");
}).then(() => closeDebuggerAndFinish(gPanel));
});

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

@ -79,18 +79,14 @@ add_task(function *() {
}
yield toolbox.selectTool("webconsole");
ok(toolboxTab.hasAttribute("highlighted") &&
toolboxTab.getAttribute("highlighted") == "true",
ok(toolboxTab.classList.contains("highlighted"),
"The highlighted class is present");
ok(!toolboxTab.hasAttribute("selected") ||
toolboxTab.getAttribute("selected") != "true",
ok(!toolboxTab.classList.contains("selected"),
"The tab is not selected");
yield toolbox.selectTool("jsdebugger");
ok(toolboxTab.hasAttribute("highlighted") &&
toolboxTab.getAttribute("highlighted") == "true",
ok(toolboxTab.classList.contains("highlighted"),
"The highlighted class is present");
ok(toolboxTab.hasAttribute("selected") &&
toolboxTab.getAttribute("selected") == "true",
ok(toolboxTab.classList.contains("selected"),
"...and the tab is selected, so the glow will not be present.");
}
@ -104,11 +100,9 @@ add_task(function *() {
yield onPaused;
yield toolbox.selectTool("webconsole");
ok(!toolboxTab.hasAttribute("highlighted") ||
toolboxTab.getAttribute("highlighted") != "true",
ok(!toolboxTab.classList.contains("highlighted"),
"The highlighted class is not present now after the resume");
ok(!toolboxTab.hasAttribute("selected") ||
toolboxTab.getAttribute("selected") != "true",
ok(!toolboxTab.classList.contains("selected"),
"The tab is not selected");
}
});

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

@ -48,7 +48,7 @@ add_task(function* () {
"worker URL in host title");
let toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
let activeTools = [...toolTabs].map(tab=>tab.getAttribute("toolid"));
let activeTools = [...toolTabs].map(tab=>tab.getAttribute("data-id"));
is(activeTools.join(","), "webconsole,jsdebugger,scratchpad,options",
"Correct set of tools supported by worker");

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

@ -470,6 +470,11 @@ exports.defaultThemes = [
// addons that have manually inserted toolbarbuttons into DOM.
// (By default, supported target is only local tab)
exports.ToolboxButtons = [
{ id: "command-button-pick",
isTargetSupported: target => {
return target.activeTab && target.activeTab.traits.frames;
}
},
{ id: "command-button-frames",
isTargetSupported: target => {
return target.activeTab && target.activeTab.traits.frames;

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

@ -0,0 +1,12 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'toolbox-controller.js',
'toolbox-tab.js',
'toolbox-toolbar.js',
)

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

@ -0,0 +1,151 @@
/* 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";
const {createClass, createFactory} = require("devtools/client/shared/vendor/react");
const ToolboxToolbar = createFactory(require("devtools/client/framework/components/toolbox-toolbar"));
const ELEMENT_PICKER_ID = "command-button-pick";
/**
* This component serves as a state controller for the toolbox React component. It's a
* thin layer for translating events and state of the outside world into the React update
* cycle. This solution was used to keep the amount of code changes to a minimimum while
* adapting the existing codebase to start using React.
*/
module.exports = createClass({
displayName: "ToolboxController",
getInitialState() {
// See the ToolboxToolbar propTypes for documentation on each of these items in state,
// and for the defintions of the props that are expected to be passed in.
return {
focusedButton: ELEMENT_PICKER_ID,
currentToolId: null,
canRender: false,
highlightedTool: "",
areDockButtonsEnabled: true,
panelDefinitions: [],
hostTypes: [],
canCloseToolbox: true,
toolboxButtons: [],
buttonIds: [],
checkedButtonsUpdated: () => {
this.forceUpdate();
}
};
},
componentWillUnmount() {
this.state.toolboxButtons.forEach(button => {
button.off("updatechecked", this.state.checkedButtonsUpdated);
});
},
/**
* The button and tab ids must be known in order to be able to focus left and right
* using the arrow keys.
*/
updateButtonIds() {
const {panelDefinitions, toolboxButtons, optionsPanel, hostTypes,
canCloseToolbox} = this.state;
// This is a little gnarly, but go through all of the state and extract the IDs.
this.setState({
buttonIds: [
...toolboxButtons.filter(btn => btn.isInStartContainer).map(({id}) => id),
...panelDefinitions.map(({id}) => id),
...toolboxButtons.filter(btn => !btn.isInStartContainer).map(({id}) => id),
optionsPanel ? optionsPanel.id : null,
...hostTypes.map(({position}) => "toolbox-dock-" + position),
canCloseToolbox ? "toolbox-close" : null
].filter(id => id)
});
this.updateFocusedButton();
},
updateFocusedButton() {
this.setFocusedButton(this.state.focusedButton);
},
setFocusedButton(focusedButton) {
const {buttonIds} = this.state;
this.setState({
focusedButton: focusedButton && buttonIds.includes(focusedButton)
? focusedButton
: buttonIds[0]
});
},
setCurrentToolId(currentToolId) {
this.setState({currentToolId});
// Also set the currently focused button to this tool.
this.setFocusedButton(currentToolId);
},
setCanRender() {
this.setState({ canRender: true });
this.updateButtonIds();
},
setOptionsPanel(optionsPanel) {
this.setState({ optionsPanel });
this.updateButtonIds();
},
highlightTool(highlightedTool) {
this.setState({ highlightedTool });
},
unhighlightTool(id) {
if (this.state.highlightedTool === id) {
this.setState({ highlightedTool: "" });
}
},
setDockButtonsEnabled(areDockButtonsEnabled) {
this.setState({ areDockButtonsEnabled });
this.updateButtonIds();
},
setHostTypes(hostTypes) {
this.setState({ hostTypes });
this.updateButtonIds();
},
setCanCloseToolbox(canCloseToolbox) {
this.setState({ canCloseToolbox });
this.updateButtonIds();
},
setPanelDefinitions(panelDefinitions) {
this.setState({ panelDefinitions });
this.updateButtonIds();
},
setToolboxButtons(toolboxButtons) {
// Listen for updates of the checked attribute.
this.state.toolboxButtons.forEach(button => {
button.off("updatechecked", this.state.checkedButtonsUpdated);
});
toolboxButtons.forEach(button => {
button.on("updatechecked", this.state.checkedButtonsUpdated);
});
this.setState({ toolboxButtons });
this.updateButtonIds();
},
setCanMinimize(canMinimize) {
/* Bug 1177463 - The minimize button is currently hidden until we agree on
the UI for it, and until bug 1173849 is fixed too. */
// this.setState({ canMinimize });
},
render() {
return ToolboxToolbar(Object.assign({}, this.props, this.state));
}
});

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

@ -0,0 +1,62 @@
/* 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";
const {DOM, createClass} = require("devtools/client/shared/vendor/react");
const {img, button} = DOM;
module.exports = createClass({
displayName: "ToolboxTab",
renderIcon(definition, isHighlighted) {
const {icon, highlightedicon} = definition;
if (!icon) {
return [];
}
return [
img({
className: "default-icon",
src: icon
}),
img({
className: "highlighted-icon",
src: highlightedicon || icon
})
];
},
render() {
const {panelDefinition, currentToolId, highlightedTool, selectTool,
focusedButton, focusButton} = this.props;
const {id, tooltip, label, iconOnly} = panelDefinition;
const isHighlighted = id === currentToolId;
const className = [
"devtools-tab",
panelDefinition.invertIconForLightTheme || panelDefinition.invertIconForDarkTheme
? "icon-invertable"
: "",
panelDefinition.invertIconForLightTheme ? "icon-invertable-light-theme" : "",
panelDefinition.invertIconForDarkTheme ? "icon-invertable-dark-theme" : "",
currentToolId === id ? "selected" : "",
highlightedTool === id ? "highlighted" : "",
iconOnly ? "devtools-tab-icon-only" : ""
].join(" ");
return button(
{
className,
id: `toolbox-tab-${id}`,
"data-id": id,
title: tooltip,
type: "button",
tabIndex: focusedButton === id ? "0" : "-1",
onFocus: () => focusButton(id),
onClick: () => selectTool(id),
},
...this.renderIcon(panelDefinition, isHighlighted),
iconOnly ? null : label
);
}
});

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

@ -0,0 +1,250 @@
/* 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";
const {DOM, createClass, createFactory, PropTypes} = require("devtools/client/shared/vendor/react");
const {div, button} = DOM;
const ToolboxTab = createFactory(require("devtools/client/framework/components/toolbox-tab"));
/**
* This is the overall component for the toolbox toolbar. It is designed to not know how
* the state is being managed, and attempts to be as pure as possible. The
* ToolboxController component controls the changing state, and passes in everything as
* props.
*/
module.exports = createClass({
displayName: "ToolboxToolbar",
propTypes: {
// The currently focused item (for arrow keyboard navigation)
// This ID determines the tabindex being 0 or -1.
focusedButton: PropTypes.string,
// List of command button definitions.
toolboxButtons: PropTypes.array,
// The id of the currently selected tool, e.g. "inspector"
currentToolId: PropTypes.string,
// An optionally highlighted tool, e.g. "inspector"
highlightedTool: PropTypes.string,
// List of tool panel definitions.
panelDefinitions: PropTypes.array,
// Function to select a tool based on its id.
selectTool: PropTypes.func,
// Keep a record of what button is focused.
focusButton: PropTypes.func,
// The options button definition.
optionsPanel: PropTypes.object,
// Hold off displaying the toolbar until enough information is ready for it to render
// nicely.
canRender: PropTypes.bool,
// Localization interface.
L10N: PropTypes.object,
},
/**
* The render function is kept fairly short for maintainability. See the individual
* render functions for how each of the sections is rendered.
*/
render() {
const containerProps = {className: "devtools-tabbar"};
return this.props.canRender
? (
div(
containerProps,
renderToolboxButtonsStart(this.props),
renderTabs(this.props),
renderToolboxButtonsEnd(this.props),
renderOptions(this.props),
renderSeparator(),
renderDockButtons(this.props)
)
)
: div(containerProps);
}
});
/**
* Render all of the tabs, this takes in the panel definitions and builds out
* the buttons for each of them.
*
* @param {Array} panelDefinitions - Array of objects that define panels.
* @param {String} currentToolId - The currently selected tool's id; e.g. "inspector".
* @param {String} highlightedTool - If a tool is highlighted, this is it's id.
* @param {Function} selectTool - Function to select a tool in the toolbox.
* @param {String} focusedButton - The id of the focused button.
* @param {Function} focusButton - Keep a record of the currently focused button.
*/
function renderTabs({panelDefinitions, currentToolId, highlightedTool, selectTool,
focusedButton, focusButton}) {
// A wrapper is needed to get flex sizing correct in XUL.
return div({className: "toolbox-tabs-wrapper"},
div({className: "toolbox-tabs"},
...panelDefinitions.map(panelDefinition => ToolboxTab({
panelDefinition,
currentToolId,
highlightedTool,
selectTool,
focusedButton,
focusButton,
}))
)
);
}
/**
* A little helper function to call renderToolboxButtons for buttons at the start
* of the toolbox.
*/
function renderToolboxButtonsStart(props) {
return renderToolboxButtons(props, true);
}
/**
* A little helper function to call renderToolboxButtons for buttons at the end
* of the toolbox.
*/
function renderToolboxButtonsEnd(props) {
return renderToolboxButtons(props, false);
}
/**
* Render all of the tabs, this takes in a list of toolbox button states. These are plain
* objects that have all of the relevant information needed to render the button.
* See Toolbox.prototype._createButtonState in devtools/client/framework/toolbox.js for
* documentation on this object.
*
* @param {Array} toolboxButtons - Array of objects that define the command buttons.
* @param {String} focusedButton - The id of the focused button.
* @param {Function} focusButton - Keep a record of the currently focused button.
* @param {boolean} isStart - Render either the starting buttons, or ending buttons.
*/
function renderToolboxButtons({toolboxButtons, focusedButton, focusButton}, isStart) {
const visibleButtons = toolboxButtons.filter(command => {
const {isVisible, isInStartContainer} = command;
return isVisible && (isStart ? isInStartContainer : !isInStartContainer);
});
if (visibleButtons.length === 0) {
return null;
}
return div({id: `toolbox-buttons-${isStart ? "start" : "end"}`},
...visibleButtons.map(command => {
const {id, description, onClick, isChecked, className: buttonClass} = command;
return button({
id,
title: description,
className: (
"command-button command-button-invertable devtools-button "
+ buttonClass + (isChecked ? " checked" : "")
),
onClick: (event) => {
onClick(event);
focusButton(id);
},
onFocus: () => focusButton(id),
tabIndex: id === focusedButton ? "0" : "-1"
});
})
);
}
/**
* The options button is a ToolboxTab just like in the renderTabs() function. However
* it is separate from the normal tabs, so deal with it separately here.
*
* @param {Object} optionsPanel - A single panel definition for the options panel.
* @param {String} currentToolId - The currently selected tool's id; e.g. "inspector".
* @param {Function} selectTool - Function to select a tool in the toolbox.
* @param {String} focusedButton - The id of the focused button.
* @param {Function} focusButton - Keep a record of the currently focused button.
*/
function renderOptions({optionsPanel, currentToolId, selectTool, focusedButton,
focusButton}) {
return div({id: "toolbox-option-container"}, ToolboxTab({
panelDefinition: optionsPanel,
currentToolId,
selectTool,
focusedButton,
focusButton,
}));
}
/**
* Render a separator.
*/
function renderSeparator() {
return div({
id: "toolbox-controls-separator",
className: "devtools-separator"
});
}
/**
* Render the dock buttons, and handle all the cases for what type of host the toolbox
* is attached to. The following props are expected.
*
* @property {String} focusedButton - The id of the focused button.
* @property {Function} closeToolbox - Completely close the toolbox.
* @property {Array} hostTypes - Array of host type objects, containing:
* @property {String} position - Position name
* @property {Function} switchHost - Function to switch the host.
* @property {Function} focusButton - Keep a record of the currently focused button.
* @property {Object} L10N - Localization interface.
* @property {Boolean} areDockButtonsEnabled - They are not enabled in certain situations
* like when they are in the WebIDE.
* @property {Boolean} canCloseToolbox - Are the tools in a context where they can be
* closed? This is not always the case, e.g. in the
* WebIDE.
*/
function renderDockButtons(props) {
const {
focusedButton,
closeToolbox,
hostTypes,
focusButton,
L10N,
areDockButtonsEnabled,
canCloseToolbox,
} = props;
let buttons = [];
if (areDockButtonsEnabled) {
hostTypes.forEach(hostType => {
const id = "toolbox-dock-" + hostType.position;
buttons.push(button({
id,
onFocus: () => focusButton(id),
className: "toolbox-dock-button devtools-button",
title: L10N.getStr(`toolboxDockButtons.${hostType.position}.tooltip`),
onClick: e => {
hostType.switchHost();
focusButton(id);
},
tabIndex: focusedButton === id ? "0" : "-1",
}));
});
}
const closeButtonId = "toolbox-close";
const closeButton = canCloseToolbox
? button({
id: closeButtonId,
onFocus: () => focusButton(closeButtonId),
className: "devtools-button",
title: L10N.getStr("toolbox.closebutton.tooltip"),
onClick: () => {
closeToolbox();
focusButton(closeButtonId);
},
tabIndex: focusedButton === "toolbox-close" ? "0" : "-1",
})
: null;
return div({id: "toolbox-controls"},
div({id: "toolbox-dock-buttons"}, ...buttons),
closeButton
);
}

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

@ -9,6 +9,10 @@ TEST_HARNESS_FILES.xpcshell.devtools.client.framework.test += [
'test/shared-redux-head.js',
]
DIRS += [
'components',
]
DevToolsModules(
'about-devtools-toolbox.js',
'attach-thread.js',

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

@ -152,11 +152,13 @@ var continueTests = Task.async(function* (toolbox, panel) {
ok(toolbox.getCurrentPanel(), "panel value is correct");
is(toolbox.currentToolId, toolId2, "toolbox _currentToolId is correct");
ok(!toolbox.doc.getElementById("toolbox-tab-" + toolId2).hasAttribute("icon-invertable"),
"The tool tab does not have the invertable attribute");
ok(!toolbox.doc.getElementById("toolbox-tab-" + toolId2)
.classList.contains("icon-invertable"),
"The tool tab does not have the invertable class");
ok(toolbox.doc.getElementById("toolbox-tab-inspector").hasAttribute("icon-invertable"),
"The builtin tool tabs do have the invertable attribute");
ok(toolbox.doc.getElementById("toolbox-tab-inspector")
.classList.contains("icon-invertable"),
"The builtin tool tabs do have the invertable class");
let toolDefinitions = gDevTools.getToolDefinitionMap();
ok(toolDefinitions.has(toolId2), "The tool is in gDevTools");

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

@ -41,7 +41,7 @@ function checkToolLoading() {
function selectAndCheckById(id) {
return toolbox.selectTool(id).then(function () {
let tab = toolbox.doc.getElementById("toolbox-tab-" + id);
is(tab.hasAttribute("selected"), true, "The " + id + " tab is selected");
is(tab.classList.contains("selected"), true, "The " + id + " tab is selected");
});
}

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

@ -97,8 +97,9 @@ function toolUnregistered(event, toolId)
function cleanup()
{
toolbox.destroy();
toolbox = null;
gBrowser.removeCurrentTab();
finish();
toolbox.destroy().then(() => {;
toolbox = null;
gBrowser.removeCurrentTab();
finish();
})
}

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

@ -8,74 +8,78 @@ var {Toolbox} = require("devtools/client/framework/toolbox");
var toolbox = null;
function test() {
const URL = "data:text/plain;charset=UTF-8,Nothing to see here, move along";
Task.spawn(function* () {
const URL = "data:text/plain;charset=UTF-8,Nothing to see here, move along";
const TOOL_ID_1 = "jsdebugger";
const TOOL_ID_2 = "webconsole";
const TOOL_ID_1 = "jsdebugger";
const TOOL_ID_2 = "webconsole";
yield addTab(URL);
addTab(URL).then(() => {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, TOOL_ID_1, Toolbox.HostType.BOTTOM)
.then(aToolbox => {
toolbox = aToolbox;
// select tool 2
toolbox.selectTool(TOOL_ID_2)
// and highlight the first one
.then(highlightTab.bind(null, TOOL_ID_1))
// to see if it has the proper class.
.then(checkHighlighted.bind(null, TOOL_ID_1))
// Now switch back to first tool
.then(() => toolbox.selectTool(TOOL_ID_1))
// to check again. But there is no easy way to test if
// it is showing orange or not.
.then(checkNoHighlightWhenSelected.bind(null, TOOL_ID_1))
// Switch to tool 2 again
.then(() => toolbox.selectTool(TOOL_ID_2))
// and check again.
.then(checkHighlighted.bind(null, TOOL_ID_1))
// Now unhighlight the tool
.then(unhighlightTab.bind(null, TOOL_ID_1))
// to see the classes gone.
.then(checkNoHighlight.bind(null, TOOL_ID_1))
// Now close the toolbox and exit.
.then(() => executeSoon(() => {
toolbox.destroy()
.then(() => {
toolbox = null;
gBrowser.removeCurrentTab();
finish();
});
}));
});
const target = TargetFactory.forTab(gBrowser.selectedTab);
toolbox = yield gDevTools.showToolbox(target, TOOL_ID_1, Toolbox.HostType.BOTTOM)
// select tool 2
yield toolbox.selectTool(TOOL_ID_2)
// and highlight the first one
yield highlightTab(TOOL_ID_1);
// to see if it has the proper class.
yield checkHighlighted(TOOL_ID_1);
// Now switch back to first tool
yield toolbox.selectTool(TOOL_ID_1);
// to check again. But there is no easy way to test if
// it is showing orange or not.
yield checkNoHighlightWhenSelected(TOOL_ID_1);
// Switch to tool 2 again
yield toolbox.selectTool(TOOL_ID_2);
// and check again.
yield checkHighlighted(TOOL_ID_1);
// Now unhighlight the tool
yield unhighlightTab(TOOL_ID_1);
// to see the classes gone.
yield checkNoHighlight(TOOL_ID_1);
// Now close the toolbox and exit.
executeSoon(() => {
toolbox.destroy().then(() => {
toolbox = null;
gBrowser.removeCurrentTab();
finish();
});
});
})
.catch(error => {
ok(false, "There was an error running the test.");
});
}
function highlightTab(toolId) {
info("Highlighting tool " + toolId + "'s tab.");
toolbox.highlightTool(toolId);
info(`Highlighting tool ${toolId}'s tab.`);
return toolbox.highlightTool(toolId);
}
function unhighlightTab(toolId) {
info("Unhighlighting tool " + toolId + "'s tab.");
toolbox.unhighlightTool(toolId);
info(`Unhighlighting tool ${toolId}'s tab.`);
return toolbox.unhighlightTool(toolId);
}
function checkHighlighted(toolId) {
let tab = toolbox.doc.getElementById("toolbox-tab-" + toolId);
ok(tab.hasAttribute("highlighted"), "The highlighted attribute is present");
ok(!tab.hasAttribute("selected") || tab.getAttribute("selected") != "true",
"The tab is not selected");
ok(tab.classList.contains("highlighted"),
`The highlighted class is present in ${toolId}.`);
ok(!tab.classList.contains("selected"),
`The tab is not selected in ${toolId}`);
}
function checkNoHighlightWhenSelected(toolId) {
let tab = toolbox.doc.getElementById("toolbox-tab-" + toolId);
ok(tab.hasAttribute("highlighted"), "The highlighted attribute is present");
ok(tab.hasAttribute("selected") && tab.getAttribute("selected") == "true",
"and the tab is selected, so the orange glow will not be present.");
ok(tab.classList.contains("highlighted"),
`The highlighted class is present in ${toolId}`);
ok(tab.classList.contains("selected"),
`And the tab is selected, so the orange glow will not be present. in ${toolId}`);
}
function checkNoHighlight(toolId) {
let tab = toolbox.doc.getElementById("toolbox-tab-" + toolId);
ok(!tab.hasAttribute("highlighted"),
"The highlighted attribute is not present");
ok(!tab.classList.contains("highlighted"),
`The highlighted class is not present in ${toolId}`);
}

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

@ -134,6 +134,6 @@ function* testPreviousHost() {
}
function checkToolboxLoaded(iframe) {
let tabs = iframe.contentDocument.getElementById("toolbox-tabs");
let tabs = iframe.contentDocument.querySelector(".toolbox-tabs");
ok(tabs, "toolbox UI has been loaded into iframe");
}

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

@ -293,22 +293,8 @@ function checkRegistered(toolId, deferred, event, data) {
if (data == toolId) {
ok(true, "Correct tool added back");
// checking tab on the toolbox
let radio = doc.getElementById("toolbox-tab-" + toolId);
ok(radio, "Tab added back for " + toolId);
if (radio.previousSibling) {
ok(+radio.getAttribute("ordinal") >=
+radio.previousSibling.getAttribute("ordinal"),
"Inserted tab's ordinal is greater than equal to its previous tab." +
"Expected " + radio.getAttribute("ordinal") + " >= " +
radio.previousSibling.getAttribute("ordinal"));
}
if (radio.nextSibling) {
ok(+radio.getAttribute("ordinal") <
+radio.nextSibling.getAttribute("ordinal"),
"Inserted tab's ordinal is less than its next tab. Expected " +
radio.getAttribute("ordinal") + " < " +
radio.nextSibling.getAttribute("ordinal"));
}
let button = doc.getElementById("toolbox-tab-" + toolId);
ok(button, "Tab added back for " + toolId);
} else {
ok(false, "Something went wrong, " + toolId + " was not registered");
}

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

@ -60,19 +60,16 @@ function testPreferenceAndUIStateIsConsistent() {
let checkNodes = [...panelWin.document.querySelectorAll(
"#enabled-toolbox-buttons-box input[type=checkbox]")];
let toolboxButtonNodes = [...doc.querySelectorAll(".command-button")];
toolboxButtonNodes.push(doc.getElementById("command-button-frames"));
let toggleableTools = toolbox.toolboxButtons;
// The noautohide button is only displayed in the browser toolbox
toggleableTools = toggleableTools.filter(
let toolbarButtons = toolbox.toolbarButtons.filter(
tool => tool.id != "command-button-noautohide");
for (let tool of toggleableTools) {
for (let tool of toolbarButtons) {
let isVisible = getBoolPref(tool.visibilityswitch);
let button = toolboxButtonNodes.filter(
toolboxButton => toolboxButton.id === tool.id)[0];
is(!button.hasAttribute("hidden"), isVisible,
let button = toolboxButtonNodes.find(toolboxButton => toolboxButton.id === tool.id);
is(!!button, isVisible,
"Button visibility matches pref for " + tool.id);
let check = checkNodes.filter(node => node.id === tool.id)[0];
@ -84,48 +81,52 @@ function testPreferenceAndUIStateIsConsistent() {
function testToggleToolboxButtons() {
let checkNodes = [...panelWin.document.querySelectorAll(
"#enabled-toolbox-buttons-box input[type=checkbox]")];
let toolboxButtonNodes = [...doc.querySelectorAll(".command-button")];
let toggleableTools = toolbox.toolboxButtons;
// The noautohide button is only displayed in the browser toolbox, and the element
// picker button is not toggleable.
toggleableTools = toggleableTools.filter(
tool => tool.id != "command-button-noautohide" && tool.id != "command-button-pick");
toolboxButtonNodes = toolboxButtonNodes.filter(
btn => btn.id != "command-button-noautohide" && btn.id != "command-button-pick");
let toolbarButtons = toolbox.toolbarButtons.filter(
tool => tool.id != "command-button-noautohide");
is(checkNodes.length, toggleableTools.length,
let visibleToolbarButtons = toolbox.toolbarButtons.filter(tool => tool.isVisible);
let toolbarButtonNodes = [...doc.querySelectorAll(".command-button")].filter(
btn => btn.id != "command-button-noautohide");
is(checkNodes.length, toolbarButtons.length,
"All of the buttons are toggleable.");
is(checkNodes.length, toolboxButtonNodes.length,
is(visibleToolbarButtons.length, toolbarButtonNodes.length,
"All of the DOM buttons are toggleable.");
for (let tool of toggleableTools) {
for (let tool of toolbarButtons) {
let id = tool.id;
let matchedCheckboxes = checkNodes.filter(node => node.id === id);
let matchedButtons = toolboxButtonNodes.filter(button => button.id === id);
let matchedButtons = toolbarButtonNodes.filter(button => button.id === id);
is(matchedCheckboxes.length, 1,
"There should be a single toggle checkbox for: " + id);
is(matchedButtons.length, 1,
"There should be a DOM button for: " + id);
is(matchedButtons[0], tool.button,
"DOM buttons should match for: " + id);
if (tool.isVisible) {
is(matchedButtons.length, 1,
"There should be a DOM button for the visible: " + id);
is(matchedButtons[0].getAttribute("title"), tool.description,
"The tooltip for button matches the tool definition.");
} else {
is(matchedButtons.length, 0,
"There should not be a DOM button for the invisible: " + id);
}
is(matchedCheckboxes[0].nextSibling.textContent, tool.label,
is(matchedCheckboxes[0].nextSibling.textContent, tool.description,
"The label for checkbox matches the tool definition.");
is(matchedButtons[0].getAttribute("title"), tool.label,
"The tooltip for button matches the tool definition.");
}
// Store modified pref names so that they can be cleared on error.
for (let tool of toggleableTools) {
for (let tool of toolbarButtons) {
let pref = tool.visibilityswitch;
modifiedPrefs.push(pref);
}
// Try checking each checkbox, making sure that it changes the preference
for (let node of checkNodes) {
let tool = toggleableTools.filter(
toggleableTool => toggleableTool.id === node.id)[0];
let tool = toolbarButtons.filter(
commandButton => commandButton.id === node.id)[0];
let isVisible = getBoolPref(tool.visibilityswitch);
testPreferenceAndUIStateIsConsistent();

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

@ -132,8 +132,9 @@ function toolboxToolUnregistered() {
}
function cleanup() {
toolbox.destroy();
toolbox = null;
gBrowser.removeCurrentTab();
finish();
toolbox.destroy().then(() => {;
toolbox = null;
gBrowser.removeCurrentTab();
finish();
});
}

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

@ -30,10 +30,12 @@ function cleanUp(toolbox) {
off(DebuggerClient, "connect", onDebuggerClientConnect);
toolbox.destroy().then(function () {
gBrowser.removeCurrentTab();
executeSoon(function () {
finish();
});
setTimeout(() => {
gBrowser.removeCurrentTab();
executeSoon(function () {
finish();
});
}, 1000);
});
}

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

@ -44,11 +44,11 @@ add_task(function* () {
// Open frame menu and wait till it's available on the screen.
// Also check 'open' attribute on the command button.
let btn = toolbox.doc.getElementById("command-button-frames");
ok(!btn.getAttribute("open"), "The open attribute must not be present");
ok(!btn.classList.contains("checked"), "The checked class must not be present");
let menu = toolbox.showFramesMenu({target: btn});
yield once(menu, "open");
is(btn.getAttribute("open"), "true", "The open attribute must be set");
ok(btn.classList.contains("checked"), "The checked class must be set");
// Verify that the frame list menu is populated
let frames = menu.items;

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

@ -122,7 +122,7 @@ exports.getHighlighterUtils = function (toolbox) {
}
isPicking = true;
toolbox.pickerButtonChecked = true;
toolbox.pickerButton.isChecked = true;
yield toolbox.selectTool("inspector");
toolbox.on("select", cancelPicker);
@ -156,7 +156,7 @@ exports.getHighlighterUtils = function (toolbox) {
}
isPicking = false;
toolbox.pickerButtonChecked = false;
toolbox.pickerButton.isChecked = false;
if (isRemoteHighlightable()) {
yield toolbox.highlighter.cancelPick();

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

@ -135,30 +135,36 @@ OptionsPanel.prototype = {
}
},
setupToolbarButtonsList: function () {
setupToolbarButtonsList: Task.async(function* () {
// Ensure the toolbox is open, and the buttons are all set up.
yield this.toolbox.isOpen;
let enabledToolbarButtonsBox = this.panelDoc.getElementById(
"enabled-toolbox-buttons-box");
let toggleableButtons = this.toolbox.toolboxButtons;
let setToolboxButtonsVisibility =
this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox);
let toolbarButtons = this.toolbox.toolbarButtons;
if (!toolbarButtons) {
console.warn("The command buttons weren't initiated yet.");
return;
}
let onCheckboxClick = (checkbox) => {
let toolDefinition = toggleableButtons.filter(
let commandButton = toolbarButtons.filter(
toggleableButton => toggleableButton.id === checkbox.id)[0];
Services.prefs.setBoolPref(
toolDefinition.visibilityswitch, checkbox.checked);
setToolboxButtonsVisibility();
commandButton.visibilityswitch, checkbox.checked);
this.toolbox.updateToolboxButtonsVisibility();
};
let createCommandCheckbox = tool => {
let createCommandCheckbox = button => {
let checkboxLabel = this.panelDoc.createElement("label");
let checkboxSpanLabel = this.panelDoc.createElement("span");
checkboxSpanLabel.textContent = tool.label;
checkboxSpanLabel.textContent = button.description;
let checkboxInput = this.panelDoc.createElement("input");
checkboxInput.setAttribute("type", "checkbox");
checkboxInput.setAttribute("id", tool.id);
if (InfallibleGetBoolPref(tool.visibilityswitch)) {
checkboxInput.setAttribute("id", button.id);
if (button.isVisible) {
checkboxInput.setAttribute("checked", true);
}
checkboxInput.addEventListener("change",
@ -169,14 +175,14 @@ OptionsPanel.prototype = {
return checkboxLabel;
};
for (let tool of toggleableButtons) {
if (!tool.isTargetSupported(this.toolbox.target)) {
for (let button of toolbarButtons) {
if (!button.isTargetSupported(this.toolbox.target)) {
continue;
}
enabledToolbarButtonsBox.appendChild(createCommandCheckbox(tool));
enabledToolbarButtonsBox.appendChild(createCommandCheckbox(button));
}
},
}),
setupToolsList: function () {
let defaultToolsBox = this.panelDoc.getElementById("default-tools-box");

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -47,28 +47,7 @@
<vbox id="toolbox-container" flex="1">
<div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-notificationbox"/>
<toolbar class="devtools-tabbar">
<hbox id="toolbox-picker-container" />
<hbox id="toolbox-tabs" flex="1" role="tablist" />
<hbox id="toolbox-buttons" pack="end">
<html:button id="command-button-frames"
class="command-button command-button-invertable devtools-button"
title="&toolboxFramesTooltip;"
hidden="true" />
<html:button id="command-button-noautohide"
class="command-button command-button-invertable devtools-button"
title="&toolboxNoAutoHideTooltip;"
hidden="true" />
</hbox>
<vbox id="toolbox-controls-separator" class="devtools-separator"/>
<hbox id="toolbox-option-container"/>
<hbox id="toolbox-controls">
<hbox id="toolbox-dock-buttons"/>
<html:button id="toolbox-close"
class="devtools-button"
title="&toolboxCloseButton.tooltip;"/>
</hbox>
</toolbar>
<div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-toolbar-mount" />
<vbox flex="1" class="theme-body">
<!-- Set large flex to allow the toolbox-panel-webconsole to have a
height set to a small value without flexing to fill up extra

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

@ -52,5 +52,5 @@ function* checkElementSelected(selector, inspector) {
function checkPickerMode(toolbox, isOn) {
let pickerButton = toolbox.doc.querySelector("#command-button-pick");
is(pickerButton.hasAttribute("checked"), isOn, "The picker mode is correct");
is(pickerButton.classList.contains("checked"), isOn, "The picker mode is correct");
}

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

@ -57,9 +57,7 @@ function* testToolboxInitialization(testActor, tab) {
yield testBreadcrumbs("span", inspector);
info("Destroying toolbox");
let destroyed = toolbox.once("destroyed");
toolbox.destroy();
yield destroyed;
yield toolbox.destroy();
ok("true", "'destroyed' notification received.");
ok(!gDevTools.getToolbox(target), "Toolbox destroyed.");

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

@ -10,19 +10,6 @@
<!ENTITY toggleToolboxF12.keycode "VK_F12">
<!ENTITY toggleToolboxF12.keytext "F12">
<!ENTITY toolboxCloseButton.tooltip "Close Developer Tools">
<!-- LOCALIZATION NOTE (toolboxFramesButton): This is the label for
- the iframes menu list that appears only when the document has some.
- It allows you to switch the context of the whole toolbox. -->
<!ENTITY toolboxFramesTooltip "Select an iframe as the currently targeted document">
<!-- LOCALIZATION NOTE (toolboxNoAutoHideButton): This is the label for
- the button to force the popups/panels to stay visible on blur.
- This is only visible in the browser toolbox as it is meant for
- addon developers and Firefox contributors. -->
<!ENTITY toolboxNoAutoHideTooltip "Disable popup auto hide">
<!-- LOCALIZATION NOTE (browserToolboxErrorMessage): This is the label
- shown next to error details when the Browser Toolbox is unable to open. -->
<!ENTITY browserToolboxErrorMessage "Error opening Browser Toolbox:">

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

@ -158,3 +158,18 @@ toolbox.minimize.key=CmdOrCtrl+Shift+U
# LOCALIZATION NOTE (toolbox.toggleHost.key)
# Key shortcut used to move the toolbox in bottom or side of the browser window
toolbox.toggleHost.key=CmdOrCtrl+Shift+D
# LOCALIZATION NOTE (toolbox.frames.tooltip): This is the label for
# the iframes menu list that appears only when the document has some.
# It allows you to switch the context of the whole toolbox.
toolbox.frames.tooltip=Select an iframe as the currently targeted document
# LOCALIZATION NOTE (toolbox.noautohide.tooltip): This is the label for
# the button to force the popups/panels to stay visible on blur.
# This is only visible in the browser toolbox as it is meant for
# addon developers and Firefox contributors.
toolbox.noautohide.tooltip=Disable popup auto hide
# LOCALIZATION NOTE (toolbox.closebutton.tooltip): This is the tooltip for
# the close button the developer tools toolbox.
toolbox.closebutton.tooltip=Close Developer Tools

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

@ -21,27 +21,27 @@ add_task(function* () {
let tab = toolbox.doc.getElementById("toolbox-tab-performance");
yield console.profile("rust");
yield waitUntil(() => tab.hasAttribute("highlighted"));
yield waitUntil(() => tab.classList.contains("highlighted"));
ok(tab.hasAttribute("highlighted"), "Performance tab is highlighted during recording " +
"from console.profile when unloaded.");
ok(tab.classList.contains("highlighted"), "Performance tab is highlighted during " +
"recording from console.profile when unloaded.");
yield console.profileEnd("rust");
yield waitUntil(() => !tab.hasAttribute("highlighted"));
yield waitUntil(() => !tab.classList.contains("highlighted"));
ok(!tab.hasAttribute("highlighted"),
ok(!tab.classList.contains("highlighted"),
"Performance tab is no longer highlighted when console.profile recording finishes.");
let { panel } = yield initPerformanceInTab({ tab: target.tab });
yield startRecording(panel);
ok(tab.hasAttribute("highlighted"),
ok(tab.classList.contains("highlighted"),
"Performance tab is highlighted during recording while in performance tool.");
yield stopRecording(panel);
ok(!tab.hasAttribute("highlighted"),
ok(!tab.classList.contains("highlighted"),
"Performance tab is no longer highlighted when recording finishes.");
yield teardownToolboxAndRemoveTab(panel);

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

@ -37,6 +37,7 @@ pref("devtools.toolbox.splitconsoleEnabled", false);
pref("devtools.toolbox.splitconsoleHeight", 100);
// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);
pref("devtools.command-button-frames.enabled", true);
pref("devtools.command-button-splitconsole.enabled", true);
pref("devtools.command-button-paintflashing.enabled", false);

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

@ -10,7 +10,6 @@ const defer = require("devtools/shared/defer");
const Services = require("Services");
const { TargetFactory } = require("devtools/client/framework/target");
const Telemetry = require("devtools/client/shared/telemetry");
const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
const {Task} = require("devtools/shared/task");
@ -67,71 +66,56 @@ var CommandUtils = {
},
/**
* A toolbarSpec is an array of strings each of which is a GCLI command.
* Create a list of props for React components that manage the state of the buttons.
*
* @param {Array} toolbarSpec - An array of strings each of which is a GCLI command.
* @param {Object} target
* @param {Object} document - Used to listen to unload event of the window.
* @param {Requisition} requisition
* @param {Function} createButtonState - A function that provides a common interface
* to create a button for the toolbox.
*
* @return {Array} List of ToolboxButton objects..
*
* Warning: this method uses the unload event of the window that owns the
* buttons that are of type checkbox. this means that we don't properly
* unregister event handlers until the window is destroyed.
*/
createButtons: function (toolbarSpec, target, document, requisition) {
createCommandButtons: function (toolbarSpec, target, document, requisition,
createButtonState) {
return util.promiseEach(toolbarSpec, typed => {
// Ask GCLI to parse the typed string (doesn't execute it)
return requisition.update(typed).then(() => {
let button = document.createElementNS(NS_XHTML, "button");
// Ignore invalid commands
let command = requisition.commandAssignment.value;
if (command == null) {
throw new Error("No command '" + typed + "'");
}
if (command.buttonId != null) {
button.id = command.buttonId;
if (command.buttonClass != null) {
button.className = command.buttonClass;
}
} else {
button.setAttribute("text-as-image", "true");
button.setAttribute("label", command.name);
if (!command.buttonId) {
throw new Error("Attempting to add a button to the toolbar, and the command " +
"did not have an id.");
}
// Create the ToolboxButton.
let button = createButtonState({
id: command.buttonId,
className: command.buttonClass,
description: command.tooltipText || command.description,
onClick: requisition.updateExec.bind(requisition, typed)
});
button.classList.add("devtools-button");
if (command.tooltipText != null) {
button.setAttribute("title", command.tooltipText);
} else if (command.description != null) {
button.setAttribute("title", command.description);
}
button.addEventListener("click",
requisition.updateExec.bind(requisition, typed));
button.addEventListener("keypress", (event) => {
if (ViewHelpers.isSpaceOrReturn(event)) {
event.preventDefault();
requisition.updateExec(typed);
}
}, false);
// Allow the command button to be toggleable
let onChange = null;
// Allow the command button to be toggleable.
if (command.state) {
button.setAttribute("autocheck", false);
/**
* The onChange event should be called with an event object that
* contains a target property which specifies which target the event
* applies to. For legacy reasons the event object can also contain
* a tab property.
*/
onChange = (eventName, ev) => {
const onChange = (eventName, ev) => {
if (ev.target == target || ev.tab == target.tab) {
let updateChecked = (checked) => {
if (checked) {
button.setAttribute("checked", true);
} else if (button.hasAttribute("checked")) {
button.removeAttribute("checked");
}
// This will emit a ToolboxButton update event.
button.isChecked = checked;
};
// isChecked would normally be synchronous. An annoying quirk
@ -150,14 +134,13 @@ var CommandUtils = {
command.state.onChange(target, onChange);
onChange("", { target: target });
document.defaultView.addEventListener("unload", function (event) {
if (command.state.offChange) {
command.state.offChange(target, onChange);
}
}, { once: true });
}
document.defaultView.addEventListener("unload", function (event) {
if (onChange && command.state.offChange) {
command.state.offChange(target, onChange);
}
button.remove();
button = null;
}, { once: true });
requisition.clear();

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

@ -13,6 +13,7 @@ const TOOL_DELAY = 200;
add_task(function* () {
yield addTab(TEST_URI);
let Telemetry = loadTelemetryAndRecordLogs();
yield pushPref("devtools.command-button-paintflashing.enabled", true);
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = yield gDevTools.showToolbox(target, "inspector");

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

@ -14,6 +14,8 @@ add_task(function* () {
yield addTab(TEST_URI);
let Telemetry = loadTelemetryAndRecordLogs();
yield pushPref("devtools.command-button-scratchpad.enabled", true);
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = yield gDevTools.showToolbox(target, "inspector");
info("inspector opened");

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

@ -94,6 +94,22 @@
.theme-firebug .devtools-sidebar-tabs tab {
}
.theme-firebug .devtools-tab span {
padding-inline-end: 0;
}
/* Tweak the margin and padding values differently for sidebar and the main tab bar */
.theme-firebug .devtools-tab,
.theme-firebug .devtools-tab.selected {
padding: 2px 4px 0 4px;
margin: 3px 1px -1px;
}
.theme-firebug .devtools-sidebar-tabs tab {
margin: 3px 0 -1px 0;
padding: 2px 0 0 0;
}
/* In order to hide bottom-border of side panel tabs we need
to make the parent element overflow visible, so child element
can move one pixel down to hide the bottom border of the parent. */
@ -107,7 +123,7 @@
border-bottom: 1px solid transparent;
}
.theme-firebug .devtools-tab[selected],
.theme-firebug .devtools-tab.selected,
.theme-firebug .devtools-sidebar-tabs tab[selected] {
background-color: rgb(247, 251, 254);
border: 1px solid rgb(170, 188, 207) !important;
@ -116,8 +132,8 @@
color: inherit;
}
.theme-firebug .devtools-tab spacer,
.theme-firebug .devtools-tab image {
.theme-firebug .devtools-tabbar .devtools-separator,
.theme-firebug .devtools-tab img {
display: none;
}
@ -135,7 +151,7 @@
margin: 0 4px !important;
}
.theme-firebug #panelSideBox .devtools-tab[selected],
.theme-firebug #panelSideBox .devtools-tab.selected,
.theme-firebug .devtools-sidebar-tabs tab[selected] {
background-color: white;
}
@ -155,13 +171,21 @@
.theme-firebug #toolbox-tab-options::before {
content: url(chrome://devtools/skin/images/firebug/tool-options.svg);
display: block;
margin: 4px 7px 0;
margin: 4px 4px 0;
}
.theme-firebug #toolbox-tab-options:not([selected]):hover::before {
filter: brightness(80%);
}
/* Element picker */
.theme-firebug #toolbox-buttons-start {
border: none;
}
.theme-firebug #command-button-pick {
top: 6px;
}
/* Toolbar */
.theme-firebug .theme-toolbar,
@ -233,3 +257,9 @@
.theme-firebug #element-picker {
min-height: 21px;
}
/* Make sure the toolbar buttons shrink nicely. */
#toolbox-buttons-end {
background-image: linear-gradient(rgba(253, 253, 253, 0.2), rgba(253, 253, 253, 0));
}

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

@ -187,7 +187,7 @@
/* Invert the colors of certain light theme images for displaying
* inside of the dark theme.
*/
.devtools-tab[icon-invertable] > image,
.devtools-tab.icon-invertable > img,
.devtools-toolbarbutton > image,
.devtools-button::before,
#breadcrumb-separator-normal,

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

@ -49,15 +49,32 @@
padding: 0;
background: var(--theme-tab-toolbar-background);
border-bottom-color: var(--theme-splitter-color);
display: flex;
}
#toolbox-tabs {
.toolbox-tabs {
margin: 0;
flex: 1;
}
.toolbox-tabs-wrapper {
position: relative;
display: flex;
flex: 1;
}
.toolbox-tabs {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
}
/* Set flex attribute to Toolbox buttons and Picker container so,
they don't overlapp with the tab bar */
#toolbox-buttons {
they don't overlap with the tab bar */
#toolbox-buttons-end {
display: flex;
}
@ -65,22 +82,38 @@
display: flex;
}
/* Toolbox tabs */
#toolbox-buttons-start {
border: solid 0 var(--theme-splitter-color);
border-inline-end-width: 1px;
}
/* Toolbox tabs */
.devtools-tab {
-moz-appearance: none;
-moz-binding: url("chrome://global/content/bindings/general.xml#control-item");
-moz-box-align: center;
min-width: 32px;
min-height: 24px;
max-width: 100px;
margin: 0;
padding: 0;
border-style: solid;
border-width: 0;
border-inline-start-width: 1px;
-moz-box-align: center;
-moz-box-flex: 1;
padding-inline-end: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background-color: transparent;
}
.devtools-tab-icon-only {
padding-inline-end: 2px;
}
.devtools-tab span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-inline-end: 13px;
position: relative;
top: 1px;
}
/* Save space on the tab-strip in Firebug theme */
@ -114,17 +147,17 @@
background-color: var(--toolbar-tab-hover-active);
}
.theme-dark .devtools-tab:not([selected])[highlighted] {
.theme-dark .devtools-tab:not(.selected).highlighted {
background-color: hsla(99, 100%, 14%, .3);
}
.theme-light .devtools-tab:not([selected])[highlighted] {
.theme-light .devtools-tab:not(.selected).highlighted {
background-color: rgba(44, 187, 15, .2);
}
/* Display execution pointer in the Debugger tab to indicate
that the debugger is paused. */
.theme-firebug #toolbox-tab-jsdebugger.devtools-tab:not([selected])[highlighted] {
.theme-firebug #toolbox-tab-jsdebugger.devtools-tab:not(.selected).highlighted {
background-color: rgba(89, 178, 234, .2);
background-image: url(chrome://devtools/skin/images/firebug/tool-debugger-paused.svg);
background-repeat: no-repeat;
@ -132,27 +165,29 @@
background-position: 3px 6px;
}
.devtools-tab > image {
.devtools-tab > img {
border: none;
margin: 0;
margin-inline-start: 4px;
margin-inline-start: 10px;
opacity: 0.6;
max-height: 16px;
width: 16px; /* Prevents collapse during theme switching */
vertical-align: text-top;
margin-inline-end: 6px;
}
/* Support invertable icon flags and make icon white when it's on a blue background */
.theme-light .devtools-tab[icon-invertable="light-theme"]:not([selected]) > image,
.devtools-tab[icon-invertable="dark-theme"][selected] > image {
.theme-light .devtools-tab.icon-invertable-light-theme:not(.selected) > img,
.devtools-tab.icon-invertable-dark-theme.selected > img {
filter: invert(1);
}
/* Don't apply any filter to non-invertable command button icons */
.command-button:not(.command-button-invertable),
/* [icon-invertable="light-theme"] icons are white, so do not invert them for the dark theme */
.theme-dark .devtools-tab[icon-invertable="light-theme"] > image,
/* icon-invertable-light-theme icons are white, so do not invert them for the dark theme */
.theme-dark .devtools-tab.icon-invertable-light-theme > img,
/* Since "highlighted" icons are green, we should omit the filter */
.devtools-tab[icon-invertable][highlighted]:not([selected]) > image {
.devtools-tab.icon-invertable.highlighted:not(.selected) > img {
filter: none;
}
@ -161,55 +196,50 @@
margin: 0 4px;
}
.devtools-tab:hover > image {
.devtools-tab:hover > img {
opacity: 0.8;
}
.devtools-tab:active > image,
.devtools-tab[selected] > image {
.devtools-tab:active > img,
.devtools-tab.selected > img {
opacity: 1;
}
.devtools-tabbar .devtools-tab[selected],
.devtools-tabbar .devtools-tab[selected]:hover:active {
.devtools-tabbar .devtools-tab.selected,
.devtools-tabbar .devtools-tab.selected:hover:active {
color: var(--theme-selection-color);
background-color: var(--theme-selection-background);
}
#toolbox-tabs .devtools-tab[selected],
#toolbox-tabs .devtools-tab[highlighted] {
.toolbox-tabs .devtools-tab.selected,
.toolbox-tabs .devtools-tab.highlighted {
border-width: 0;
padding-inline-start: 1px;
}
#toolbox-tabs .devtools-tab[selected]:last-child,
#toolbox-tabs .devtools-tab[highlighted]:last-child {
padding-inline-end: 1px;
}
#toolbox-tabs .devtools-tab[selected] + .devtools-tab,
#toolbox-tabs .devtools-tab[highlighted] + .devtools-tab {
.toolbox-tabs .devtools-tab.selected + .devtools-tab,
.toolbox-tabs .devtools-tab.highlighted + .devtools-tab {
border-inline-start-width: 0;
padding-inline-start: 1px;
}
#toolbox-tabs .devtools-tab:first-child[selected] {
.toolbox-tabs .devtools-tab:first-child {
border-inline-start-width: 0;
}
#toolbox-tabs .devtools-tab:last-child {
.toolbox-tabs .devtools-tab:last-child {
border-inline-end-width: 1px;
}
.devtools-tab:not([highlighted]) > .highlighted-icon,
.devtools-tab[selected] > .highlighted-icon,
.devtools-tab:not([selected])[highlighted] > .default-icon {
visibility: collapse;
.devtools-tab:not(.highlighted) > .highlighted-icon,
.devtools-tab.selected > .highlighted-icon,
.devtools-tab:not(.selected).highlighted > .default-icon {
display: none;
}
/* The options tab is special - it doesn't have the same parent
as the other tabs (toolbox-option-container vs toolbox-tabs) */
#toolbox-option-container .devtools-tab:not([selected]) {
#toolbox-option-container .devtools-tab:not(.selected) {
background-color: transparent;
}
#toolbox-option-container .devtools-tab {
@ -217,12 +247,16 @@
border-width: 0;
padding-inline-start: 1px;
}
#toolbox-tab-options > image {
margin: 0 8px;
#toolbox-option-container img {
margin-inline-end: 6px;
margin-inline-start: 6px;
}
/* Toolbox controls */
#toolbox-controls, #toolbox-dock-buttons {
display: flex;
}
#toolbox-controls > button,
#toolbox-dock-buttons > button {
-moz-appearance: none;
@ -263,7 +297,16 @@
background-image: url("chrome://devtools/skin/images/dock-bottom-maximize@2x.png");
}
#toolbox-buttons:empty + .devtools-separator,
/**
* Ensure that when the toolbar collapses in on itself when there is not enough room
* that it still looks reasonable.
*/
.devtools-tabbar > div {
background-color: var(--theme-tab-toolbar-background);
z-index: 0;
}
#toolbox-buttons-end:empty + .devtools-separator,
.devtools-separator[invisible] {
visibility: hidden;
}
@ -293,12 +336,12 @@
}
.command-button:hover:active,
.command-button[checked=true]:not(:hover) {
.command-button.checked:not(:hover) {
background-color: var(--toolbar-tab-hover-active)
}
.theme-light .command-button:hover:active,
.theme-light .command-button[checked=true]:not(:hover) {
.theme-light .command-button.checked:not(:hover) {
background-color: inherit;
}
@ -307,8 +350,8 @@
}
.command-button:hover:active::before,
.command-button[checked=true]::before,
.command-button[open=true]::before {
.command-button.checked::before,
.command-button.open::before {
opacity: 1;
}

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

@ -85,17 +85,15 @@ function runTest() {
webconsoleHeight: webconsoleHeight,
splitterVisibility: splitterVisibility,
openedConsolePanel: openedConsolePanel,
buttonSelected: cmdButton.hasAttribute("checked")
buttonSelected: cmdButton.classList.contains("checked")
};
}
function checkWebconsolePanelOpened() {
const checkWebconsolePanelOpened = Task.async(function* () {
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();
yield toolbox.toggleSplitConsole();
let currentUIState = getCurrentUIState();
@ -109,78 +107,54 @@ function runTest() {
"The console panel is not the current tool");
ok(currentUIState.buttonSelected, "The command button is selected");
openPanel("webconsole").then(() => {
currentUIState = getCurrentUIState();
yield openPanel("webconsole");
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");
ok(currentUIState.buttonSelected,
"The command button is still selected.");
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");
ok(currentUIState.buttonSelected,
"The command button is still selected.");
// Make sure splitting console does nothing while webconsole is opened
toolbox.toggleSplitConsole();
// Make sure splitting console does nothing while webconsole is opened
yield toolbox.toggleSplitConsole();
currentUIState = getCurrentUIState();
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");
ok(currentUIState.buttonSelected,
"The command button is still selected.");
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");
ok(currentUIState.buttonSelected,
"The command button is still selected.");
// Make sure that split state is saved after opening another panel
openPanel("inspector").then(() => {
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");
ok(currentUIState.buttonSelected,
"The command button is still selected.");
// Make sure that split state is saved after opening another panel
yield openPanel("inspector");
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");
ok(currentUIState.buttonSelected,
"The command button is still selected.");
toolbox.toggleSplitConsole();
deferred.resolve();
});
});
return deferred.promise;
}
yield toolbox.toggleSplitConsole();
});
function openPanel(toolId) {
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() {
const checkToolboxUI = Task.async(function* () {
let currentUIState = getCurrentUIState();
ok(!currentUIState.splitterVisibility, "Splitter is hidden by default");
@ -192,7 +166,7 @@ function runTest() {
"The console panel is not the current tool");
ok(!currentUIState.buttonSelected, "The command button is not selected.");
toolbox.toggleSplitConsole();
yield toolbox.toggleSplitConsole();
currentUIState = getCurrentUIState();
@ -209,7 +183,7 @@ function runTest() {
"The console panel is not the current tool");
ok(currentUIState.buttonSelected, "The command button is selected.");
toolbox.toggleSplitConsole();
yield toolbox.toggleSplitConsole();
currentUIState = getCurrentUIState();
@ -221,6 +195,23 @@ function runTest() {
ok(!currentUIState.openedConsolePanel,
"The console panel is not the current tool");
ok(!currentUIState.buttonSelected, "The command button is not selected.");
});
function openPanel(toolId) {
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) {
return openPanel(toolId).then(() => {
info("Checking toolbox for " + toolId);
return checkToolboxUI(toolbox.getCurrentPanel());
});
}
function testBottomHost() {

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

@ -99,7 +99,7 @@
function isCommandButtonChecked() {
return toolbox.doc.querySelector("#command-button-splitconsole")
.hasAttribute("checked");
.classList.contains("checked");
}
function toggleSplitConsoleWithEscape() {