☠☠ backed out by 91be73741295 ☠ ☠ | |
author | Gabriel Luong <gabriel.luong@gmail.com> |
Wed, 07 Feb 2018 15:57:46 -0500 | |
changeset 402865 | a4ae1745e9d41f3da39ac23075430c657c569ccf |
parent 402864 | 5b5d0df3b6eed562b1489e4f910735b7a77b56f7 |
child 402866 | fb9756553e9c3871bfdc265ebf0e9c0b89c58687 |
push id | 33405 |
push user | shindli@mozilla.com |
push date | Thu, 08 Feb 2018 10:04:47 +0000 |
treeherder | mozilla-central@0ac953fcddf1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | pbro |
bugs | 1432599 |
milestone | 60.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/flexbox/actions/flexbox.js @@ -0,0 +1,47 @@ +/* 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 { + CLEAR_FLEXBOX, + UPDATE_FLEXBOX, + UPDATE_FLEXBOX_HIGHLIGHTED, +} = require("./index"); + +module.exports = { + + /** + * Clears the flexbox state by resetting it back to the initial flexbox state. + */ + clearFlexbox() { + return { + type: CLEAR_FLEXBOX, + }; + }, + + /** + * Updates the flexbox state with the newly selected flexbox. + */ + updateFlexbox(flexbox) { + return { + type: UPDATE_FLEXBOX, + flexbox, + }; + }, + + /** + * Updates the flexbox highlighted state. + * + * @param {Boolean} highlighted + * Whether or not the flexbox highlighter is highlighting the flexbox. + */ + updateFlexboxHighlighted(highlighted) { + return { + type: UPDATE_FLEXBOX_HIGHLIGHTED, + highlighted, + }; + }, + +};
--- a/devtools/client/inspector/flexbox/actions/index.js +++ b/devtools/client/inspector/flexbox/actions/index.js @@ -3,12 +3,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const { createEnum } = require("devtools/client/shared/enum"); createEnum([ - // Update the entire flexboxes state with the new list of flexboxes. - "UPDATE_FLEXBOXES", + // Clears the flexbox state by resetting it back to the initial flexbox state. + "CLEAR_FLEXBOX", + + // Updates the flexbox state with the newly selected flexbox. + "UPDATE_FLEXBOX", + + // Updates the flexbox highlighted state. + "UPDATE_FLEXBOX_HIGHLIGHTED", ], module.exports);
--- a/devtools/client/inspector/flexbox/actions/moz.build +++ b/devtools/client/inspector/flexbox/actions/moz.build @@ -1,9 +1,10 @@ # -*- 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( + 'flexbox.js', 'index.js', )
--- a/devtools/client/inspector/flexbox/components/Flexbox.js +++ b/devtools/client/inspector/flexbox/components/Flexbox.js @@ -1,26 +1,65 @@ /* 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: dom, - PureComponent, -} = require("devtools/client/shared/vendor/react"); +const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const dom = require("devtools/client/shared/vendor/react-dom-factories"); +const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { getStr } = require("devtools/client/inspector/layout/utils/l10n"); + +const FlexboxItem = createFactory(require("./FlexboxItem")); + +const Types = require("../types"); class Flexbox extends PureComponent { static get propTypes() { - return {}; + return { + flexbox: PropTypes.shape(Types.flexbox).isRequired, + setSelectedNode: PropTypes.func.isRequired, + onHideBoxModelHighlighter: PropTypes.func.isRequired, + onShowBoxModelHighlighterForNode: PropTypes.func.isRequired, + onToggleFlexboxHighlighter: PropTypes.func.isRequired, + }; } render() { - return dom.div( - { - id: "layout-flexbox-container", - } - ); + const { + flexbox, + setSelectedNode, + onHideBoxModelHighlighter, + onShowBoxModelHighlighterForNode, + onToggleFlexboxHighlighter, + } = this.props; + + return flexbox.actorID ? + dom.div({ id: "layout-flexbox-container" }, + dom.div({ className: "flexbox-content" }, + dom.div({ className: "flexbox-container" }, + dom.span({}, getStr("flexbox.overlayFlexbox")), + dom.ul( + { + id: "flexbox-list", + className: "devtools-monospace", + }, + FlexboxItem({ + key: flexbox.id, + flexbox, + setSelectedNode, + onHideBoxModelHighlighter, + onShowBoxModelHighlighterForNode, + onToggleFlexboxHighlighter, + }) + ) + ) + ) + ) + : + dom.div({ className: "devtools-sidepanel-no-result" }, + getStr("flexbox.noFlexboxeOnThisPage") + ); } } module.exports = Flexbox;
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/flexbox/components/FlexboxItem.js @@ -0,0 +1,99 @@ +/* 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 { PureComponent } = require("devtools/client/shared/vendor/react"); +const dom = require("devtools/client/shared/vendor/react-dom-factories"); +const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils"); + +// Reps +const { REPS, MODE } = require("devtools/client/shared/components/reps/reps"); +const { Rep } = REPS; +const ElementNode = REPS.ElementNode; + +const Types = require("../types"); + +class FlexboxItem extends PureComponent { + static get propTypes() { + return { + flexbox: PropTypes.shape(Types.flexbox).isRequired, + setSelectedNode: PropTypes.func.isRequired, + onHideBoxModelHighlighter: PropTypes.func.isRequired, + onShowBoxModelHighlighterForNode: PropTypes.func.isRequired, + onToggleFlexboxHighlighter: PropTypes.func.isRequired, + }; + } + + constructor(props) { + super(props); + this.onFlexboxCheckboxClick = this.onFlexboxCheckboxClick.bind(this); + this.onFlexboxInspectIconClick = this.onFlexboxInspectIconClick.bind(this); + } + + onFlexboxCheckboxClick(e) { + // If the click was on the svg icon to select the node in the inspector, bail out. + const originalTarget = e.nativeEvent && e.nativeEvent.explicitOriginalTarget; + if (originalTarget && originalTarget.namespaceURI === "http://www.w3.org/2000/svg") { + // We should be able to cancel the click event propagation after the following reps + // issue is implemented : https://github.com/devtools-html/reps/issues/95 . + e.preventDefault(); + return; + } + + const { + flexbox, + onToggleFlexboxHighlighter, + } = this.props; + + onToggleFlexboxHighlighter(flexbox.nodeFront); + } + + onFlexboxInspectIconClick(nodeFront) { + const { setSelectedNode } = this.props; + setSelectedNode(nodeFront, "layout-panel").catch(e => console.error(e)); + nodeFront.scrollIntoView().catch(e => console.error(e)); + } + + render() { + const { + flexbox, + onHideBoxModelHighlighter, + onShowBoxModelHighlighterForNode, + } = this.props; + const { + actorID, + highlighted, + nodeFront, + } = flexbox; + + return dom.li( + {}, + dom.label( + {}, + dom.input( + { + type: "checkbox", + value: actorID, + checked: highlighted, + onChange: this.onFlexboxCheckboxClick, + } + ), + Rep( + { + defaultRep: ElementNode, + mode: MODE.TINY, + object: translateNodeFrontToGrip(nodeFront), + onDOMNodeMouseOut: () => onHideBoxModelHighlighter(), + onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront), + onInspectIconClick: () => this.onFlexboxInspectIconClick(nodeFront), + } + ) + ) + ); + } +} + +module.exports = FlexboxItem;
--- a/devtools/client/inspector/flexbox/components/moz.build +++ b/devtools/client/inspector/flexbox/components/moz.build @@ -1,9 +1,10 @@ # -*- 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( 'Flexbox.js', + 'FlexboxItem.js', )
--- a/devtools/client/inspector/flexbox/flexbox.js +++ b/devtools/client/inspector/flexbox/flexbox.js @@ -1,21 +1,251 @@ /* 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 { throttle } = require("devtools/client/inspector/shared/utils"); + +const { + clearFlexbox, + updateFlexbox, + updateFlexboxHighlighted, +} = require("./actions/flexbox"); + class FlexboxInspector { constructor(inspector, window) { this.document = window.document; + this.highlighters = inspector.highlighters; this.inspector = inspector; this.store = inspector.store; + this.walker = inspector.walker; + + this.onHighlighterChange = this.onHighlighterChange.bind(this); + this.onReflow = throttle(this.onReflow, 500, this); + this.onSidebarSelect = this.onSidebarSelect.bind(this); + this.onToggleFlexboxHighlighter = this.onToggleFlexboxHighlighter.bind(this); + this.onUpdatePanel = this.onUpdatePanel.bind(this); + + this.init(); + } + + async init() { + if (!this.inspector) { + return; + } + + this.hasGetCurrentFlexbox = await this.inspector.target.actorHasMethod("layout", + "getCurrentFlexbox"); + this.layoutInspector = await this.walker.getLayoutInspector(); + + this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterChange); + this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterChange); + this.inspector.sidebar.on("select", this.onSidebarSelect); + + this.onSidebarSelect(); } destroy() { + this.highlighters.off("flexbox-highlighter-hidden", this.onHighlighterChange); + this.highlighters.off("flexbox-highlighter-shown", this.onHighlighterChange); + this.inspector.sidebar.off("select", this.onSidebarSelect); + + this.inspector.reflowTracker.untrackReflows(this, this.onReflow); + this.document = null; + this.highlighters = null; this.inspector = null; + this.hasGetCurrentFlexbox = null; + this.layoutInspector = null; this.store = null; + this.walker = null; + } + + getComponentProps() { + return { + onToggleFlexboxHighlighter: this.onToggleFlexboxHighlighter, + }; + } + + /** + * Returns true if the layout panel is visible, and false otherwise. + */ + isPanelVisible() { + return this.inspector && this.inspector.toolbox && this.inspector.sidebar && + this.inspector.toolbox.currentToolId === "inspector" && + this.inspector.sidebar.getCurrentTabID() === "layoutview"; + } + + /** + * Handler for "flexbox-highlighter-shown" and "flexbox-highlighter-hidden" events + * emitted from the HighlightersOverlay. Updates the flex container highlighted state + * only if the provided NodeFront is the current selected flex container. + * + * @param {Event} event + * Event that was triggered. + * @param {NodeFront} nodeFront + * The NodeFront of the flex container element for which the flexbox + * highlighter is shown for. + */ + onHighlighterChange(event, nodeFront) { + const { flexbox } = this.store.getState(); + const highlighted = event === "flexbox-highlighter-shown"; + + if (flexbox.nodeFront === nodeFront && flexbox.highlighted !== highlighted) { + this.store.dispatch(updateFlexboxHighlighted(highlighted)); + } + } + + /** + * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows, + * updates the flexbox panel because the shape of the flexbox on the page may have + * changed. + * + * TODO: In the future, we will want to compare the flex item fragment data returned + * for rendering the flexbox outline. + */ + async onReflow() { + if (!this.isPanelVisible()) { + return; + } + + const { flexbox } = this.store.getState(); + + let flexboxFront; + try { + if (!this.hasGetCurrentFlexbox) { + return; + } + + flexboxFront = await this.layoutInspector.getCurrentFlexbox( + this.inspector.selection.nodeFront); + } catch (e) { + // This call might fail if called asynchrously after the toolbox is finished + // closing. + return; + } + + // Clear the flexbox panel if there is no flex container for the current node + // selection. + if (!flexboxFront) { + this.store.dispatch(clearFlexbox()); + return; + } + + // Do nothing because the same flex container is still selected. + if (flexbox.actorID == flexboxFront.actorID) { + return; + } + + // Update the flexbox panel with the new flexbox front contents. + this.update(flexboxFront); + } + + /** + * Handler for the inspector sidebar "select" event. Updates the flexbox panel if it + * is visible. + */ + onSidebarSelect() { + if (!this.isPanelVisible()) { + this.inspector.reflowTracker.untrackReflows(this, this.onReflow); + this.inspector.off("new-root", this.onUpdatePanel); + this.inspector.selection.off("new-node-front", this.onUpdatePanel); + return; + } + + this.inspector.reflowTracker.trackReflows(this, this.onReflow); + this.inspector.on("new-root", this.onUpdatePanel); + this.inspector.selection.on("new-node-front", this.onUpdatePanel); + + this.update(); + } + + /** + * Handler for a change in the input checkboxes in the FlexboxItem component. + * Toggles on/off the flexbox highlighter for the provided flex container element. + * + * @param {NodeFront} node + * The NodeFront of the flexb container element for which the flexbox + * highlighter is toggled on/off for. + */ + onToggleFlexboxHighlighter(node) { + this.highlighters.toggleFlexboxHighlighter(node); + this.store.dispatch(updateFlexboxHighlighted(node !== + this.highlighters.flexboxHighlighterShow)); + } + + /** + * Handler for "new-root" event fired by the inspector and "new-node-front" event fired + * by the inspector selection. Updates the flexbox panel if it is visible. + */ + onUpdatePanel() { + if (!this.isPanelVisible()) { + return; + } + + this.update(); + } + + /** + * Updates the flexbox panel by dispatching the new flexbox data. This is called when + * the layout view becomes visible or a new node is selected and needs to be update + * with new flexbox data. + * + * @param {FlexboxFront|Null} flexboxFront + * THe FlexboxFront of the flex container for the current node selection. + */ + async update(flexboxFront) { + // Stop refreshing if the inspector or store is already destroyed or no node is + // selected. + if (!this.inspector || !this.store || !this.inspector.selection.nodeFront) { + return; + } + + // Fetch the current flexbox if no flexbox front was passed into this update. + if (!flexboxFront) { + try { + if (!this.hasGetCurrentFlexbox) { + return; + } + + flexboxFront = await this.layoutInspector.getCurrentFlexbox( + this.inspector.selection.nodeFront); + } catch (e) { + // This call might fail if called asynchrously after the toolbox is finished + // closing. + return; + } + } + + // Clear the flexbox panel if there is no flex container for the current node + // selection. + if (!flexboxFront) { + this.store.dispatch(clearFlexbox()); + return; + } + + let nodeFront = flexboxFront.containerNodeFront; + + // If the FlexboxFront doesn't yet have access to the NodeFront for its container, + // then get it from the walker. This happens when the walker hasn't seen this + // particular DOM Node in the tree yet or when we are connected to an older server. + if (!nodeFront) { + try { + nodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID, + ["containerEl"]); + } catch (e) { + // This call might fail if called asynchrously after the toolbox is finished + // closing. + return; + } + } + + this.store.dispatch(updateFlexbox({ + actorID: flexboxFront.actorID, + highlighted: nodeFront == this.highlighters.flexboxHighlighterShown, + nodeFront, + })); } } module.exports = FlexboxInspector;
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/flexbox/reducers/flexbox.js @@ -0,0 +1,46 @@ +/* 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 { + CLEAR_FLEXBOX, + UPDATE_FLEXBOX, + UPDATE_FLEXBOX_HIGHLIGHTED, +} = require("../actions/index"); + +const INITIAL_FLEXBOX = { + // The actor ID of the flex container. + actorID: null, + // Whether or not the flexbox highlighter is highlighting the flex container. + highlighted: false, + // The NodeFront of the flex container. + nodeFront: null, +}; + +let reducers = { + + [CLEAR_FLEXBOX](flexbox, _) { + return INITIAL_FLEXBOX; + }, + + [UPDATE_FLEXBOX](_, { flexbox }) { + return flexbox; + }, + + [UPDATE_FLEXBOX_HIGHLIGHTED](flexbox, { highlighted }) { + return Object.assign({}, flexbox, { + highlighted, + }); + }, + +}; + +module.exports = function (flexbox = INITIAL_FLEXBOX, action) { + let reducer = reducers[action.type]; + if (!reducer) { + return flexbox; + } + return reducer(flexbox, action); +};
deleted file mode 100644 --- a/devtools/client/inspector/flexbox/reducers/flexboxes.js +++ /dev/null @@ -1,19 +0,0 @@ -/* 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 INITIAL_FLEXBOXES = []; - -let reducers = { - -}; - -module.exports = function (flexboxes = INITIAL_FLEXBOXES, action) { - let reducer = reducers[action.type]; - if (!reducer) { - return flexboxes; - } - return reducer(flexboxes, action); -};
--- a/devtools/client/inspector/flexbox/reducers/index.js +++ b/devtools/client/inspector/flexbox/reducers/index.js @@ -1,7 +1,7 @@ /* 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"; -exports.flexboxes = require("./flexboxes"); +exports.flexbox = require("./flexbox");
--- a/devtools/client/inspector/flexbox/reducers/moz.build +++ b/devtools/client/inspector/flexbox/reducers/moz.build @@ -1,10 +1,10 @@ # -*- 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( - 'flexboxes.js', + 'flexbox.js', 'index.js', )
--- a/devtools/client/inspector/flexbox/types.js +++ b/devtools/client/inspector/flexbox/types.js @@ -2,14 +2,19 @@ * 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types"); exports.flexbox = { - // The id of the flexbox container. - id: PropTypes.number, + + // The actor ID of the flex container. + actorID: PropTypes.number, - // The node front of the flexbox container. + // Whether or not the flexbox highlighter is highlighting the flex container. + highlighted: PropTypes.bool, + + // The NodeFront of the flex container. nodeFront: PropTypes.object, + };
--- a/devtools/client/inspector/grids/components/Grid.js +++ b/devtools/client/inspector/grids/components/Grid.js @@ -2,23 +2,23 @@ * 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 { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { getStr } = require("devtools/client/inspector/layout/utils/l10n"); const GridDisplaySettings = createFactory(require("./GridDisplaySettings")); const GridList = createFactory(require("./GridList")); const GridOutline = createFactory(require("./GridOutline")); const Types = require("../types"); -const { getStr } = require("../utils/l10n"); class Grid extends PureComponent { static get propTypes() { return { getSwatchColorPickerTooltip: PropTypes.func.isRequired, grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired, highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired, setSelectedNode: PropTypes.func.isRequired,
--- a/devtools/client/inspector/grids/components/GridDisplaySettings.js +++ b/devtools/client/inspector/grids/components/GridDisplaySettings.js @@ -2,19 +2,19 @@ * 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 { PureComponent } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { getStr } = require("devtools/client/inspector/layout/utils/l10n"); const Types = require("../types"); -const { getStr } = require("../utils/l10n"); class GridDisplaySettings extends PureComponent { static get propTypes() { return { highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired, onToggleShowGridAreas: PropTypes.func.isRequired, onToggleShowGridLineNumbers: PropTypes.func.isRequired, onToggleShowInfiniteLines: PropTypes.func.isRequired,
--- a/devtools/client/inspector/grids/components/GridList.js +++ b/devtools/client/inspector/grids/components/GridList.js @@ -2,21 +2,21 @@ * 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 { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { getStr } = require("devtools/client/inspector/layout/utils/l10n"); const GridItem = createFactory(require("./GridItem")); const Types = require("../types"); -const { getStr } = require("../utils/l10n"); class GridList extends PureComponent { static get propTypes() { return { getSwatchColorPickerTooltip: PropTypes.func.isRequired, grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired, setSelectedNode: PropTypes.func.isRequired, onHideBoxModelHighlighter: PropTypes.func.isRequired,
--- a/devtools/client/inspector/grids/components/GridOutline.js +++ b/devtools/client/inspector/grids/components/GridOutline.js @@ -3,19 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const Services = require("Services"); const { PureComponent } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { getStr } = require("devtools/client/inspector/layout/utils/l10n"); const Types = require("../types"); -const { getStr } = require("../utils/l10n"); // The delay prior to executing the grid cell highlighting. const GRID_HIGHLIGHTING_DEBOUNCE = 50; // Prefs for the max number of rows/cols a grid container can have for // the outline to display. const GRID_OUTLINE_MAX_ROWS_PREF = Services.prefs.getIntPref("devtools.gridinspector.gridOutlineMaxRows");
deleted file mode 100644 --- a/devtools/client/inspector/grids/utils/l10n.js +++ /dev/null @@ -1,15 +0,0 @@ -/* 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 { LocalizationHelper } = require("devtools/shared/l10n"); -const L10N = new LocalizationHelper("devtools/client/locales/layout.properties"); - -module.exports = { - getStr: (...args) => L10N.getStr(...args), - getFormatStr: (...args) => L10N.getFormatStr(...args), - getFormatStrWithNumbers: (...args) => L10N.getFormatStrWithNumbers(...args), - numberWithDecimals: (...args) => L10N.numberWithDecimals(...args), -};
--- a/devtools/client/inspector/grids/utils/moz.build +++ b/devtools/client/inspector/grids/utils/moz.build @@ -1,10 +1,9 @@ # -*- 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( - 'l10n.js', 'utils.js', )
--- a/devtools/client/inspector/layout/layout.js +++ b/devtools/client/inspector/layout/layout.js @@ -37,16 +37,22 @@ class LayoutView { let { onHideBoxModelHighlighter, onShowBoxModelEditor, onShowBoxModelHighlighter, onToggleGeometryEditor, } = this.inspector.getPanel("boxmodel").getComponentProps(); + this.flexboxInspector = new FlexboxInspector(this.inspector, + this.inspector.panelWin); + let { + onToggleFlexboxHighlighter, + } = this.flexboxInspector.getComponentProps(); + this.gridInspector = new GridInspector(this.inspector, this.inspector.panelWin); let { getSwatchColorPickerTooltip, onSetGridOverlayColor, onShowGridAreaHighlight, onShowGridCellHighlight, onShowGridLineNamesHighlight, onToggleGridHighlighter, @@ -66,16 +72,17 @@ class LayoutView { onHideBoxModelHighlighter, onSetGridOverlayColor, onShowBoxModelEditor, onShowBoxModelHighlighter, onShowBoxModelHighlighterForNode, onShowGridAreaHighlight, onShowGridCellHighlight, onShowGridLineNamesHighlight, + onToggleFlexboxHighlighter, onToggleGeometryEditor, onToggleGridHighlighter, onToggleShowGridAreas, onToggleShowGridLineNumbers, onToggleShowInfiniteLines, }); let provider = createElement(Provider, { @@ -88,16 +95,17 @@ class LayoutView { // Expose the provider to let inspector.js use it in setupSidebar. this.provider = provider; } /** * Destruction function called when the inspector is destroyed. Cleans up references. */ destroy() { + this.flexboxInspector.destroy(); this.gridInspector.destroy(); this.document = null; this.inspector = null; this.store = null; } }
--- a/devtools/client/inspector/layout/moz.build +++ b/devtools/client/inspector/layout/moz.build @@ -1,13 +1,14 @@ # -*- 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/. DIRS += [ 'components', + 'utils', ] DevToolsModules( 'layout.js', )
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/utils/l10n.js @@ -0,0 +1,15 @@ +/* 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 { LocalizationHelper } = require("devtools/shared/l10n"); +const L10N = new LocalizationHelper("devtools/client/locales/layout.properties"); + +module.exports = { + getStr: (...args) => L10N.getStr(...args), + getFormatStr: (...args) => L10N.getFormatStr(...args), + getFormatStrWithNumbers: (...args) => L10N.getFormatStrWithNumbers(...args), + numberWithDecimals: (...args) => L10N.numberWithDecimals(...args), +};
new file mode 100644 --- /dev/null +++ b/devtools/client/inspector/layout/utils/moz.build @@ -0,0 +1,9 @@ +# -*- 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( + 'l10n.js', +)
--- a/devtools/client/inspector/reducers.js +++ b/devtools/client/inspector/reducers.js @@ -7,13 +7,13 @@ // This file exposes the Redux reducers of the box model, grid and grid highlighter // settings. exports.animations = require("devtools/client/inspector/animation/reducers/animations"); exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model"); exports.changes = require("devtools/client/inspector/changes/reducers/changes"); exports.events = require("devtools/client/inspector/events/reducers/events"); exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar"); -exports.flexboxes = require("devtools/client/inspector/flexbox/reducers/flexboxes"); +exports.flexbox = require("devtools/client/inspector/flexbox/reducers/flexbox"); exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options"); exports.fonts = require("devtools/client/inspector/fonts/reducers/fonts"); exports.grids = require("devtools/client/inspector/grids/reducers/grids"); exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");
--- a/devtools/client/locales/en-US/layout.properties +++ b/devtools/client/locales/en-US/layout.properties @@ -3,16 +3,24 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # LOCALIZATION NOTE This file contains the Layout Inspector strings. # The Layout Inspector is a panel accessible in the Inspector sidebar. # LOCALIZATION NOTE (flexbox.header): The accordion header for the Flexbox pane. flexbox.header=Flexbox +# LOCALIZATION NOTE (flexbox.noFlexboxeOnThisPage): In the case where there are no CSS +# flex containers to display. +flexbox.noFlexboxeOnThisPage=Select a Flex container or item to continue. + +# LOCALIZATION NOTE (flexbox.overlayFlexbox): Header for the list of flex container +# elements if only one item can be selected. +flexbox.overlayFlexbox=Overlay Flexbox + # LOCALIZATION NOTE (layout.cannotShowGridOutline, layout.cannotSHowGridOutline.title): # In the case where the grid outline cannot be effectively displayed. layout.cannotShowGridOutline=Cannot show outline for this grid layout.cannotShowGridOutline.title=The selected grid’s outline cannot effectively fit inside the layout panel for it to be usable. # LOCALIZATION NOTE (layout.displayAreaNames): Label of the display area names setting # option in the CSS Grid pane. layout.displayAreaNames=Display area names
--- a/devtools/client/themes/layout.css +++ b/devtools/client/themes/layout.css @@ -8,76 +8,84 @@ overflow: auto; min-width: 200px; } /** * Common styles for shared components */ +.flexbox-container, .grid-container { display: flex; flex-direction: column; flex: 1 auto; align-items: center; min-width: 140px; } +.grid-container:first-child { + margin-bottom: 10px; +} + +.flexbox-container > span, .grid-container > span { font-weight: 600; margin-bottom: 5px; pointer-events: none; } +.flexbox-container > ul, .grid-container > ul { list-style: none; margin: 0; padding: 0; } +.flexbox-container li, .grid-container li { display: flex; align-items: center; padding: 4px 0; } +.flexbox-container input .grid-container input { margin: 0 5px; } +.flexbox-container label, .grid-container label { display: flex; align-items: center; } /** * Grid Container */ +#layout-flexbox-container, #layout-grid-container { display: flex; flex-direction: column; margin: 5px; } /** * Grid Content */ +.flexbox-content, .grid-content { display: flex; flex-wrap: wrap; flex: 1; margin: 5px 0; } -.grid-container:first-child { - margin-bottom: 10px; -} - /** * Grid Outline */ .grid-outline-container { margin: 5px 0; }
--- a/devtools/server/actors/layout.js +++ b/devtools/server/actors/layout.js @@ -1,14 +1,15 @@ /* 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 { Cu } = require("chrome"); const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); const { flexboxSpec, gridSpec, layoutSpec } = require("devtools/shared/specs/layout"); const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants"); const { getStringifiableFragments } = require("devtools/server/actors/utils/css-grid-utils"); loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants"); loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true); @@ -25,17 +26,17 @@ loader.lazyRequireGetter(this, "CssLogic * The |Grid| actor provides the grid fragment information to inspect the grid container. */ const FlexboxActor = ActorClassWithSpec(flexboxSpec, { /** * @param {LayoutActor} layoutActor * The LayoutActor instance. * @param {DOMNode} containerEl - * The flexbox container element. + * The flex container element. */ initialize(layoutActor, containerEl) { Actor.prototype.initialize.call(this, layoutActor.conn); this.containerEl = containerEl; this.walker = layoutActor.walker; }, @@ -131,87 +132,83 @@ const LayoutActor = ActorClassWithSpec(l destroy() { Actor.prototype.destroy.call(this); this.tabActor = null; this.walker = null; }, /** - * Returns an array of FlexboxActor objects for all the flexbox containers found by - * iterating below the given rootNode. + * Returns the flex container found by iterating on the given selected node. The current + * node can be a flex container or flex item. If it is a flex item, returns the parent + * flex container. Otherwise, return null if the current or parent node is not a flex + * container. * - * @param {Node|NodeActor} rootNode - * The root node to start iterating at. - * @return {Array} An array of FlexboxActor objects. + * @param {Node|NodeActor} node + * The node to start iterating at. + * @return {FlexboxActor|Null} The FlexboxActor of the flex container of the give node. + * Otherwise, returns null. */ - getFlexbox(rootNode) { - let flexboxes = []; - - if (!rootNode) { - return flexboxes; + getCurrentFlexbox(node) { + if (isNodeDead(node)) { + return null; } - let treeWalker = this.walker.getDocumentWalker(rootNode, + // Given node can either be a Node or a NodeActor. + if (node.rawNode) { + node = node.rawNode; + } + + let treeWalker = this.walker.getDocumentWalker(node, nodeFilterConstants.SHOW_ELEMENT); + let currentNode = treeWalker.currentNode; + let displayType = this.walker.getNode(currentNode).displayType; - while (treeWalker.nextNode()) { - let currentNode = treeWalker.currentNode; - let computedStyle = CssLogic.getComputedStyle(currentNode); + if (!displayType) { + return null; + } - if (!computedStyle) { - continue; + // Check if the current node is a flex container. + if (displayType == "inline-flex" || displayType == "flex") { + return new FlexboxActor(this, treeWalker.currentNode); + } + + // Otherwise, check if this is a flex item or the parent node is a flex container. + while ((currentNode = treeWalker.parentNode())) { + if (!currentNode) { + break; } - if (computedStyle.display == "inline-flex" || computedStyle.display == "flex") { - let flexboxActor = new FlexboxActor(this, currentNode); - flexboxes.push(flexboxActor); + displayType = this.walker.getNode(currentNode).displayType; + + switch (displayType) { + case "inline-flex": + case "flex": + return new FlexboxActor(this, currentNode); + case "contents": + // Continue walking up the tree since the parent node is a content element. + continue; } + + break; } - return flexboxes; - }, - - /** - * Returns an array of FlexboxActor objects for all existing flexbox containers found by - * iterating below the given rootNode and optionally including nested frames. - * - * @param {NodeActor} rootNode - * @param {Boolean} traverseFrames - * Whether or not we should iterate through nested frames. - * @return {Array} An array of FlexboxActor objects. - */ - getAllFlexbox(rootNode, traverseFrames) { - let flexboxes = []; - - if (!rootNode) { - return flexboxes; - } - - if (!traverseFrames) { - return this.getFlexbox(rootNode.rawNode); - } - - for (let {document} of this.tabActor.windows) { - flexboxes = [...flexboxes, ...this.getFlexbox(document.documentElement)]; - } - - return flexboxes; + return null; }, /** * Returns an array of GridActor objects for all the grid elements contained in the * given root node. * * @param {Node|NodeActor} node * The root node for grid elements * @return {Array} An array of GridActor objects. */ getGrids(node) { - if (!node) { + if (isNodeDead(node)) { return []; } // Root node can either be a Node or a NodeActor. if (node.rawNode) { node = node.rawNode; } @@ -227,11 +224,15 @@ const LayoutActor = ActorClassWithSpec(l for (let frame of frames) { gridActors = gridActors.concat(this.getGrids(frame.contentDocument)); } return gridActors; }, }); +function isNodeDead(node) { + return !node || !node.rawNode || Cu.isDeadWrapper(node.rawNode); +} + exports.FlexboxActor = FlexboxActor; exports.GridActor = GridActor; exports.LayoutActor = LayoutActor;
--- a/devtools/shared/specs/index.js +++ b/devtools/shared/specs/index.js @@ -107,17 +107,17 @@ const Types = exports.__TypesForTests = front: "devtools/shared/fronts/highlighters", }, { types: ["domnodelist", "domwalker", "inspector"], spec: "devtools/shared/specs/inspector", front: "devtools/shared/fronts/inspector", }, { - types: ["grid", "layout"], + types: ["flexbox", "grid", "layout"], spec: "devtools/shared/specs/layout", front: "devtools/shared/fronts/layout", }, { types: ["memory"], spec: "devtools/shared/specs/memory", front: "devtools/shared/fronts/memory", },
--- a/devtools/shared/specs/layout.js +++ b/devtools/shared/specs/layout.js @@ -17,23 +17,22 @@ const gridSpec = generateActorSpec({ methods: {}, }); const layoutSpec = generateActorSpec({ typeName: "layout", methods: { - getAllFlexbox: { + getCurrentFlexbox: { request: { - rootNode: Arg(0, "domnode"), - traverseFrames: Arg(1, "nullable:boolean") + node: Arg(0, "domnode"), }, response: { - flexboxes: RetVal("array:flexbox") + flexbox: RetVal("nullable:flexbox") } }, getGrids: { request: { rootNode: Arg(0, "domnode") }, response: {