Bug 1342305 - Refactor grid inspector into its own grids folder. r?jdescottes draft
authorGabriel Luong <gabriel.luong@gmail.com>
Fri, 24 Feb 2017 14:14:04 -0500
changeset 490697 6ac94548ebe787c0405ba72656bd0b7619d48301
parent 490696 0a15dfecc9fa2b375e5de3ad6f195516b1de2d2f
child 547354 cdc8e440fcd5c0f49f6dd02b21ae52a9b46cdb8b
push id47202
push userbmo:gl@mozilla.com
push dateWed, 01 Mar 2017 07:18:12 +0000
reviewersjdescottes
bugs1342305
milestone54.0a1
Bug 1342305 - Refactor grid inspector into its own grids folder. r?jdescottes MozReview-Commit-ID: I9iaPwFZlrb MozReview-Commit-ID: Lfw6nYguLGW
devtools/client/inspector/grids/actions/grids.js
devtools/client/inspector/grids/actions/highlighter-settings.js
devtools/client/inspector/grids/actions/index.js
devtools/client/inspector/grids/actions/moz.build
devtools/client/inspector/grids/components/Grid.js
devtools/client/inspector/grids/components/GridDisplaySettings.js
devtools/client/inspector/grids/components/GridItem.js
devtools/client/inspector/grids/components/GridList.js
devtools/client/inspector/grids/components/GridOutline.js
devtools/client/inspector/grids/components/moz.build
devtools/client/inspector/grids/grid-inspector.js
devtools/client/inspector/grids/moz.build
devtools/client/inspector/grids/reducers/grids.js
devtools/client/inspector/grids/reducers/highlighter-settings.js
devtools/client/inspector/grids/reducers/moz.build
devtools/client/inspector/grids/types.js
devtools/client/inspector/grids/utils/l10n.js
devtools/client/inspector/grids/utils/moz.build
devtools/client/inspector/inspector.js
devtools/client/inspector/layout/actions/grids.js
devtools/client/inspector/layout/actions/highlighter-settings.js
devtools/client/inspector/layout/actions/index.js
devtools/client/inspector/layout/actions/moz.build
devtools/client/inspector/layout/components/App.js
devtools/client/inspector/layout/components/Grid.js
devtools/client/inspector/layout/components/GridDisplaySettings.js
devtools/client/inspector/layout/components/GridItem.js
devtools/client/inspector/layout/components/GridList.js
devtools/client/inspector/layout/components/GridOutline.js
devtools/client/inspector/layout/components/moz.build
devtools/client/inspector/layout/layout.js
devtools/client/inspector/layout/moz.build
devtools/client/inspector/layout/reducers/grids.js
devtools/client/inspector/layout/reducers/highlighter-settings.js
devtools/client/inspector/layout/reducers/moz.build
devtools/client/inspector/layout/types.js
devtools/client/inspector/layout/utils/l10n.js
devtools/client/inspector/layout/utils/moz.build
devtools/client/inspector/moz.build
devtools/client/inspector/reducers.js
devtools/client/shared/browser-loader.js
rename from devtools/client/inspector/layout/actions/grids.js
rename to devtools/client/inspector/grids/actions/grids.js
rename from devtools/client/inspector/layout/actions/highlighter-settings.js
rename to devtools/client/inspector/grids/actions/highlighter-settings.js
rename from devtools/client/inspector/layout/actions/index.js
rename to devtools/client/inspector/grids/actions/index.js
rename from devtools/client/inspector/layout/actions/moz.build
rename to devtools/client/inspector/grids/actions/moz.build
rename from devtools/client/inspector/layout/components/Grid.js
rename to devtools/client/inspector/grids/components/Grid.js
rename from devtools/client/inspector/layout/components/GridDisplaySettings.js
rename to devtools/client/inspector/grids/components/GridDisplaySettings.js
rename from devtools/client/inspector/layout/components/GridItem.js
rename to devtools/client/inspector/grids/components/GridItem.js
rename from devtools/client/inspector/layout/components/GridList.js
rename to devtools/client/inspector/grids/components/GridList.js
rename from devtools/client/inspector/layout/components/GridOutline.js
rename to devtools/client/inspector/grids/components/GridOutline.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/components/moz.build
@@ -0,0 +1,13 @@
+# -*- 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(
+    'Grid.js',
+    'GridDisplaySettings.js',
+    'GridItem.js',
+    'GridList.js',
+    'GridOutline.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -0,0 +1,404 @@
+/* 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 Services = require("Services");
+const { Task } = require("devtools/shared/task");
+
+const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
+
+const {
+  updateGridColor,
+  updateGridHighlighted,
+  updateGrids,
+} = require("./actions/grids");
+const {
+  updateShowGridLineNumbers,
+  updateShowInfiniteLines,
+} = require("./actions/highlighter-settings");
+
+const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
+const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
+
+// Default grid colors.
+const GRID_COLORS = [
+  "#05E4EE",
+  "#BB9DFF",
+  "#FFB53B",
+  "#71F362",
+  "#FF90FF",
+  "#FF90FF",
+  "#1B80FF",
+  "#FF2647"
+];
+
+function GridInspector(inspector, window) {
+  this.document = window.document;
+  this.highlighters = inspector.highlighters;
+  this.inspector = inspector;
+  this.store = inspector.store;
+  this.walker = this.inspector.walker;
+
+  this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
+  this.setSelectedNode = this.setSelectedNode.bind(this);
+  this.updateGridPanel = this.updateGridPanel.bind(this);
+
+  this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
+  this.onHighlighterChange = this.onHighlighterChange.bind(this);
+  this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
+  this.onShowBoxModelHighlighterForNode =
+    this.onShowBoxModelHighlighterForNode.bind(this);
+  this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
+  this.onSidebarSelect = this.onSidebarSelect.bind(this);
+  this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
+  this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
+  this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
+
+  this.init();
+}
+
+GridInspector.prototype = {
+
+  /**
+   * Initializes the grid inspector by fetching the LayoutFront from the walker, loading
+   * the highlighter settings and initalizing the SwatchColorPicker instance.
+   */
+  init: Task.async(function* () {
+    if (!this.inspector) {
+      return;
+    }
+
+    this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
+
+    this.loadHighlighterSettings();
+
+    // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
+    this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
+      this.inspector.toolbox.doc,
+      this.inspector,
+      {
+        supportsCssColor4ColorFunction: () => false
+      }
+    );
+
+    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
+    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.sidebar.on("select", this.onSidebarSelect);
+
+    this.onSidebarSelect();
+  }),
+
+  /**
+   * Destruction function called when the inspector is destroyed. Removes event listeners
+   * and cleans up references.
+   */
+  destroy() {
+    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
+    this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.sidebar.off("select", this.onSidebarSelect);
+    this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
+
+    this.swatchColorPickerTooltip.destroy();
+
+    this.document = null;
+    this.highlighters = null;
+    this.inspector = null;
+    this.layoutInspector = null;
+    this.store = null;
+    this.swatchColorPickerTooltip = null;
+    this.walker = null;
+  },
+
+  getComponentProps() {
+    return {
+      getSwatchColorPickerTooltip: this.getSwatchColorPickerTooltip,
+      setSelectedNode: this.setSelectedNode,
+      onSetGridOverlayColor: this.onSetGridOverlayColor,
+      onShowBoxModelHighlighterForNode: this.onShowBoxModelHighlighterForNode,
+      onShowGridAreaHighlight: this.onShowGridAreaHighlight,
+      onToggleGridHighlighter: this.onToggleGridHighlighter,
+      onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
+      onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
+    };
+  },
+
+  /**
+   * Returns the color set for the grid highlighter associated with the provided
+   * nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront for which we need the color.
+   */
+  getGridColorForNodeFront(nodeFront) {
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.nodeFront === nodeFront) {
+        return grid.color;
+      }
+    }
+
+    return null;
+  },
+
+  /**
+   * Create a highlighter settings object for the provided nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront for which we need highlighter settings.
+   */
+  getGridHighlighterSettings(nodeFront) {
+    let { highlighterSettings } = this.store.getState();
+
+    // Get the grid color for the provided nodeFront.
+    let color = this.getGridColorForNodeFront(nodeFront);
+
+    // Merge the grid color to the generic highlighter settings.
+    return Object.assign({}, highlighterSettings, {
+      color
+    });
+  },
+
+  /**
+   * Retrieve the shared SwatchColorPicker instance.
+   */
+  getSwatchColorPickerTooltip() {
+    return this.swatchColorPickerTooltip;
+  },
+
+  /**
+   * Returns true if the layout panel is visible, and false otherwise.
+   */
+  isPanelVisible() {
+    return this.inspector.toolbox.currentToolId === "inspector" &&
+           this.inspector.sidebar &&
+           this.inspector.sidebar.getCurrentTabID() === "layoutview";
+  },
+
+  /**
+   * Load the grid highligher display settings into the store from the stored preferences.
+   */
+  loadHighlighterSettings() {
+    let { dispatch } = this.store;
+
+    let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
+    let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
+
+    dispatch(updateShowGridLineNumbers(showGridLineNumbers));
+    dispatch(updateShowInfiniteLines(showInfinteLines));
+  },
+
+  /**
+   * Set the inspector selection.
+   *
+   * @param {NodeFront} nodeFront
+   *        The NodeFront corresponding to the new selection.
+   */
+  setSelectedNode(nodeFront) {
+    this.inspector.selection.setNodeFront(nodeFront, "layout-panel");
+  },
+
+  /**
+   * Updates the grid panel by dispatching the new grid data. This is called when the
+   * layout view becomes visible or the view needs to be updated with new grid data.
+   *
+   * @param  {Array|null} gridFronts
+   *         Optional array of all GridFront in the current page.
+   */
+  updateGridPanel: Task.async(function* (gridFronts) {
+    // Stop refreshing if the inspector or store is already destroyed.
+    if (!this.inspector || !this.store) {
+      return;
+    }
+
+    // Get all the GridFront from the server if no gridFronts were provided.
+    if (!gridFronts) {
+      gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
+    }
+
+    let grids = [];
+    for (let i = 0; i < gridFronts.length; i++) {
+      let grid = gridFronts[i];
+      let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
+
+      let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
+      let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor;
+
+      grids.push({
+        id: i,
+        color,
+        gridFragments: grid.gridFragments,
+        highlighted: nodeFront == this.highlighters.gridHighlighterShown,
+        nodeFront,
+      });
+    }
+
+    this.store.dispatch(updateGrids(grids));
+  }),
+
+  /**
+   * Handler for "grid-layout-changed" events emitted from the LayoutActor.
+   *
+   * @param  {Array} grids
+   *         Array of all GridFront in the current page.
+   */
+  onGridLayoutChange(grids) {
+    if (this.isPanelVisible()) {
+      this.updateGridPanel(grids);
+    }
+  },
+
+  /**
+   * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
+   * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
+   *
+   * @param  {Event} event
+   *         Event that was triggered.
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront of the grid container element for which the grid highlighter
+   *         is shown for.
+   */
+  onHighlighterChange(event, nodeFront) {
+    let highlighted = event === "grid-highlighter-shown";
+    this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
+  },
+
+  /**
+   * Handler for a change in the grid overlay color picker for a grid container.
+   *
+   * @param  {NodeFront} node
+   *         The NodeFront of the grid container element for which the grid color is
+   *         being updated.
+   * @param  {String} color
+   *         A hex string representing the color to use.
+   */
+  onSetGridOverlayColor(node, color) {
+    this.store.dispatch(updateGridColor(node, color));
+    let { grids } = this.store.getState();
+
+    // If the grid for which the color was updated currently has a highlighter, update
+    // the color.
+    for (let grid of grids) {
+      if (grid.nodeFront === node && grid.highlighted) {
+        let highlighterSettings = this.getGridHighlighterSettings(node);
+        this.highlighters.showGridHighlighter(node, highlighterSettings);
+      }
+    }
+  },
+
+  /**
+   * Shows the box-model highlighter on the element corresponding to the provided
+   * NodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The node to highlight.
+   * @param  {Object} options
+   *         Options passed to the highlighter actor.
+   */
+  onShowBoxModelHighlighterForNode(nodeFront, options) {
+    let toolbox = this.inspector.toolbox;
+    toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
+  },
+
+  /**
+   * Highlights the grid area in the CSS Grid Highlighter for the given grid.
+   *
+   * @param  {NodeFront} node
+   *         The NodeFront of the grid container element for which the grid
+   *         highlighter is highlighted for.
+   * @param  {String} gridAreaName
+   *         The name of the grid area for which the grid highlighter
+   *         is highlighted for.
+   * @param  {String} color
+   *         The color of the grid area for which the grid highlighter
+   *         is highlighted for.
+   */
+  onShowGridAreaHighlight(node, gridAreaName, color) {
+    let { highlighterSettings } = this.store.getState();
+
+    highlighterSettings.showGridArea = gridAreaName;
+    highlighterSettings.color = color;
+
+    this.highlighters.showGridHighlighter(node, highlighterSettings);
+  },
+
+  /**
+   * Handler for the inspector sidebar select event. Starts listening for
+   * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
+   * listening for grid layout changes. Finally, refresh the layout view if
+   * it is visible.
+   */
+  onSidebarSelect() {
+    if (!this.isPanelVisible()) {
+      this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
+      return;
+    }
+
+    this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
+    this.updateGridPanel();
+  },
+
+  /**
+   * Handler for a change in the input checkboxes in the GridList component.
+   * Toggles on/off the grid highlighter for the provided grid container element.
+   *
+   * @param  {NodeFront} node
+   *         The NodeFront of the grid container element for which the grid
+   *         highlighter is toggled on/off for.
+   */
+  onToggleGridHighlighter(node) {
+    let highlighterSettings = this.getGridHighlighterSettings(node);
+    this.highlighters.toggleGridHighlighter(node, highlighterSettings);
+  },
+
+  /**
+   * Handler for a change in the show grid line numbers checkbox in the
+   * GridDisplaySettings component. Toggles on/off the option to show the grid line
+   * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
+   * grids currently highlighted.
+   *
+   * @param  {Boolean} enabled
+   *         Whether or not the grid highlighter should show the grid line numbers.
+   */
+  onToggleShowGridLineNumbers(enabled) {
+    this.store.dispatch(updateShowGridLineNumbers(enabled));
+    Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
+
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.highlighted) {
+        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
+        this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
+      }
+    }
+  },
+
+  /**
+   * Handler for a change in the extend grid lines infinitely checkbox in the
+   * GridDisplaySettings component. Toggles on/off the option to extend the grid
+   * lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
+   * for grids currently highlighted.
+   *
+   * @param  {Boolean} enabled
+   *         Whether or not the grid highlighter should extend grid lines infinitely.
+   */
+  onToggleShowInfiniteLines(enabled) {
+    this.store.dispatch(updateShowInfiniteLines(enabled));
+    Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
+
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.highlighted) {
+        let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
+        this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
+      }
+    }
+  },
+
+};
+
+module.exports = GridInspector;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/moz.build
@@ -0,0 +1,17 @@
+# -*- 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 += [
+    'actions',
+    'components',
+    'reducers',
+    'utils',
+]
+
+DevToolsModules(
+    'grid-inspector.js',
+    'types.js',
+)
rename from devtools/client/inspector/layout/reducers/grids.js
rename to devtools/client/inspector/grids/reducers/grids.js
rename from devtools/client/inspector/layout/reducers/highlighter-settings.js
rename to devtools/client/inspector/grids/reducers/highlighter-settings.js
rename from devtools/client/inspector/layout/reducers/moz.build
rename to devtools/client/inspector/grids/reducers/moz.build
rename from devtools/client/inspector/layout/types.js
rename to devtools/client/inspector/grids/types.js
rename from devtools/client/inspector/layout/utils/l10n.js
rename to devtools/client/inspector/grids/utils/l10n.js
rename from devtools/client/inspector/layout/utils/moz.build
rename to devtools/client/inspector/grids/utils/moz.build
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -20,16 +20,17 @@ const nodeConstants = require("devtools/
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
 const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs");
 const BoxModel = require("devtools/client/inspector/boxmodel/box-model");
 const {FontInspector} = require("devtools/client/inspector/fonts/fonts");
+const GridInspector = require("devtools/client/inspector/grids/grid-inspector");
 const {InspectorSearch} = require("devtools/client/inspector/inspector-search");
 const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
 const MarkupView = require("devtools/client/inspector/markup/markup");
 const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
 const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
@@ -572,16 +573,18 @@ Inspector.prototype = {
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.boxmodel = new BoxModel(this, this.panelWin);
 
     const {ComputedViewTool} =
       this.browserRequire("devtools/client/inspector/computed/computed");
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
     if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
+      this.gridInspector = new GridInspector(this, this.panelWin);
+
       const LayoutView = this.browserRequire("devtools/client/inspector/layout/layout");
       this.layoutview = new LayoutView(this, this.panelWin);
     }
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -5,36 +5,39 @@
 "use strict";
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
-const Accordion = createFactory(require("./Accordion"));
-const Grid = createFactory(require("./Grid"));
+const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
+const Grid = createFactory(require("devtools/client/inspector/grids/components/Grid"));
 
-const BoxModel = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModel"));
+const BoxModelTypes = require("devtools/client/inspector/boxmodel/types");
+const GridTypes = require("devtools/client/inspector/grids/types");
 
-const Types = require("../types");
-const { getStr } = require("../utils/l10n");
+const Accordion = createFactory(require("./Accordion"));
 
 const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
+const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
+const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);
+
 const App = createClass({
 
   displayName: "App",
 
   propTypes: {
-    boxModel: PropTypes.shape(Types.boxModel).isRequired,
+    boxModel: PropTypes.shape(BoxModelTypes.boxModel).isRequired,
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
-    grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
-    highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
+    grids: PropTypes.arrayOf(PropTypes.shape(GridTypes.grid)).isRequired,
+    highlighterSettings: PropTypes.shape(GridTypes.highlighterSettings).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     showBoxModelProperties: PropTypes.bool.isRequired,
     showGridOutline: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
@@ -53,17 +56,17 @@ const App = createClass({
         items: [
           {
             header: BOXMODEL_L10N.getStr("boxmodel.title"),
             component: BoxModel,
             componentProps: this.props,
             opened: true,
           },
           {
-            header: getStr("layout.header"),
+            header: LAYOUT_L10N.getStr("layout.header"),
             component: Grid,
             componentProps: this.props,
             opened: true,
           },
         ]
       })
     );
   },
--- a/devtools/client/inspector/layout/components/moz.build
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -3,14 +3,9 @@
 # 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(
     'Accordion.css',
     'Accordion.js',
     'App.js',
-    'Grid.js',
-    'GridDisplaySettings.js',
-    'GridItem.js',
-    'GridList.js',
-    'GridOutline.js',
 )
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -1,254 +1,83 @@
 /* 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 Services = require("Services");
-const { Task } = require("devtools/shared/task");
 
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
-const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
-
-const {
-  updateGridColor,
-  updateGridHighlighted,
-  updateGrids,
-} = require("./actions/grids");
-const {
-  updateShowGridLineNumbers,
-  updateShowInfiniteLines,
-} = require("./actions/highlighter-settings");
-
 const App = createFactory(require("./components/App"));
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
-const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
 const SHOW_GRID_OUTLINE_PREF = "devtools.gridinspector.showGridOutline";
-const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
-
-// Default grid colors.
-const GRID_COLORS = [
-  "#05E4EE",
-  "#BB9DFF",
-  "#FFB53B",
-  "#71F362",
-  "#FF90FF",
-  "#FF90FF",
-  "#1B80FF",
-  "#FF2647"
-];
 
 function LayoutView(inspector, window) {
   this.document = window.document;
-  this.highlighters = inspector.highlighters;
   this.inspector = inspector;
   this.store = inspector.store;
-  this.walker = this.inspector.walker;
-
-  this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
-  this.onHighlighterChange = this.onHighlighterChange.bind(this);
-  this.onSidebarSelect = this.onSidebarSelect.bind(this);
 
   this.init();
 }
 
 LayoutView.prototype = {
 
-  /**
-   * Initializes the layout view by fetching the LayoutFront from the walker, creating
-   * the redux store and adding the view into the inspector sidebar.
-   */
-  init: Task.async(function* () {
+  init() {
     if (!this.inspector) {
       return;
     }
 
     let {
       onHideBoxModelHighlighter,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
     } = this.inspector.boxmodel.getComponentProps();
 
-    this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
-
-    this.loadHighlighterSettings();
-
-    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
-    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
-    this.inspector.sidebar.on("select", this.onSidebarSelect);
-
-    // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
-    this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
-      this.inspector.toolbox.doc,
-      this.inspector,
-      {
-        supportsCssColor4ColorFunction: () => false
-      }
-    );
+    let {
+      getSwatchColorPickerTooltip,
+      setSelectedNode,
+      onSetGridOverlayColor,
+      onShowBoxModelHighlighterForNode,
+      onShowGridAreaHighlight,
+      onToggleGridHighlighter,
+      onToggleShowGridLineNumbers,
+      onToggleShowInfiniteLines,
+    } = this.inspector.gridInspector.getComponentProps();
 
     let app = App({
-      /**
-       * Retrieve the shared SwatchColorPicker instance.
-       */
-      getSwatchColorPickerTooltip: () => {
-        return this.swatchColorPickerTooltip;
-      },
-
-      /**
-       * Set the inspector selection.
-       *
-       * @param {NodeFront} nodeFront
-       *        The NodeFront corresponding to the new selection.
-       */
-      setSelectedNode: (nodeFront) => {
-        this.inspector.selection.setNodeFront(nodeFront, "layout-panel");
-      },
-
+      getSwatchColorPickerTooltip,
+      setSelectedNode,
       /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
       showBoxModelProperties: true,
 
       /**
        * Shows the grid outline if user preferences are set to true, otherwise, hidden by
        * default.
        */
       showGridOutline: Services.prefs.getBoolPref(SHOW_GRID_OUTLINE_PREF),
 
       onHideBoxModelHighlighter,
-
-      /**
-       * Handler for a change in the grid overlay color picker for a grid container.
-       *
-       * @param  {NodeFront} node
-       *         The NodeFront of the grid container element for which the grid color is
-       *         being updated.
-       * @param  {String} color
-       *         A hex string representing the color to use.
-       */
-      onSetGridOverlayColor: (node, color) => {
-        this.store.dispatch(updateGridColor(node, color));
-        let { grids } = this.store.getState();
-
-        // If the grid for which the color was updated currently has a highlighter, update
-        // the color.
-        for (let grid of grids) {
-          if (grid.nodeFront === node && grid.highlighted) {
-            let highlighterSettings = this.getGridHighlighterSettings(node);
-            this.highlighters.showGridHighlighter(node, highlighterSettings);
-          }
-        }
-      },
-
+      onSetGridOverlayColor,
       onShowBoxModelEditor,
       onShowBoxModelHighlighter,
-
-     /**
-      * Shows the box-model highlighter on the element corresponding to the provided
-      * NodeFront.
-      *
-      * @param  {NodeFront} nodeFront
-      *         The node to highlight.
-      * @param  {Object} options
-      *         Options passed to the highlighter actor.
-      */
-      onShowBoxModelHighlighterForNode: (nodeFront, options) => {
-        let toolbox = this.inspector.toolbox;
-        toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
-      },
-
-      /**
-       * Highlights the grid area in the CSS Grid Highlighter for the given grid.
-       *
-       * @param  {NodeFront} node
-       *         The NodeFront of the grid container element for which the grid
-       *         highlighter is highlighted for.
-       * @param  {String} gridAreaName
-       *         The name of the grid area for which the grid highlighter
-       *         is highlighted for.
-       * @param  {String} color
-       *         The color of the grid area for which the grid highlighter
-       *         is highlighted for.
-       */
-      onShowGridAreaHighlight: (node, gridAreaName, color) => {
-        let { highlighterSettings } = this.store.getState();
-
-        highlighterSettings.showGridArea = gridAreaName;
-        highlighterSettings.color = color;
-
-        this.highlighters.showGridHighlighter(node, highlighterSettings);
-      },
-
-      /**
-       * Handler for a change in the input checkboxes in the GridList component.
-       * Toggles on/off the grid highlighter for the provided grid container element.
-       *
-       * @param  {NodeFront} node
-       *         The NodeFront of the grid container element for which the grid
-       *         highlighter is toggled on/off for.
-       */
-      onToggleGridHighlighter: node => {
-        let highlighterSettings = this.getGridHighlighterSettings(node);
-        this.highlighters.toggleGridHighlighter(node, highlighterSettings);
-      },
-
-      /**
-       * Handler for a change in the show grid line numbers checkbox in the
-       * GridDisplaySettings component. Toggles on/off the option to show the grid line
-       * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
-       * grids currently highlighted.
-       *
-       * @param  {Boolean} enabled
-       *         Whether or not the grid highlighter should show the grid line numbers.
-       */
-      onToggleShowGridLineNumbers: enabled => {
-        this.store.dispatch(updateShowGridLineNumbers(enabled));
-        Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
-
-        let { grids } = this.store.getState();
-
-        for (let grid of grids) {
-          if (grid.highlighted) {
-            let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
-            this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
-          }
-        }
-      },
-
-      /**
-       * Handler for a change in the extend grid lines infinitely checkbox in the
-       * GridDisplaySettings component. Toggles on/off the option to extend the grid
-       * lines infinitely in the grid highlighter. Refreshes the shown grid highlighter
-       * for grids currently highlighted.
-       *
-       * @param  {Boolean} enabled
-       *         Whether or not the grid highlighter should extend grid lines infinitely.
-       */
-      onToggleShowInfiniteLines: enabled => {
-        this.store.dispatch(updateShowInfiniteLines(enabled));
-        Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
-
-        let { grids } = this.store.getState();
-
-        for (let grid of grids) {
-          if (grid.highlighted) {
-            let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
-            this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
-          }
-        }
-      }
+      onShowBoxModelHighlighterForNode,
+      onShowGridAreaHighlight,
+      onToggleGridHighlighter,
+      onToggleShowGridLineNumbers,
+      onToggleShowInfiniteLines,
     });
 
     let provider = createElement(Provider, {
       store: this.store,
       id: "layoutview",
       title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
       key: "layoutview",
     }, app);
@@ -256,170 +85,22 @@ LayoutView.prototype = {
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     this.inspector.addSidebarTab(
       "layoutview",
       INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
       provider,
       defaultTab == "layoutview"
     );
-  }),
-
-  /**
-   * Destruction function called when the inspector is destroyed. Removes event listeners
-   * and cleans up references.
-   */
-  destroy() {
-    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
-    this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
-    this.inspector.sidebar.off("select", this.onSidebarSelect);
-    this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
-
-    this.document = null;
-    this.inspector = null;
-    this.layoutInspector = null;
-    this.store = null;
-    this.walker = null;
-  },
-
-  /**
-   * Returns the color set for the grid highlighter associated with the provided
-   * nodeFront.
-   *
-   * @param  {NodeFront} nodeFront
-   *         The NodeFront for which we need the color.
-   */
-  getGridColorForNodeFront(nodeFront) {
-    let { grids } = this.store.getState();
-
-    for (let grid of grids) {
-      if (grid.nodeFront === nodeFront) {
-        return grid.color;
-      }
-    }
-
-    return null;
-  },
-
-  /**
-   * Create a highlighter settings object for the provided nodeFront.
-   *
-   * @param  {NodeFront} nodeFront
-   *         The NodeFront for which we need highlighter settings.
-   */
-  getGridHighlighterSettings(nodeFront) {
-    let { highlighterSettings } = this.store.getState();
-
-    // Get the grid color for the provided nodeFront.
-    let color = this.getGridColorForNodeFront(nodeFront);
-
-    // Merge the grid color to the generic highlighter settings.
-    return Object.assign({}, highlighterSettings, {
-      color
-    });
-  },
-
-  /**
-   * Returns true if the layout panel is visible, and false otherwise.
-   */
-  isPanelVisible() {
-    return this.inspector.toolbox.currentToolId === "inspector" &&
-           this.inspector.sidebar &&
-           this.inspector.sidebar.getCurrentTabID() === "layoutview";
-  },
-
-  /**
-   * Load the grid highligher display settings into the store from the stored preferences.
-   */
-  loadHighlighterSettings() {
-    let { dispatch } = this.store;
-
-    let showGridLineNumbers = Services.prefs.getBoolPref(SHOW_GRID_LINE_NUMBERS);
-    let showInfinteLines = Services.prefs.getBoolPref(SHOW_INFINITE_LINES_PREF);
-
-    dispatch(updateShowGridLineNumbers(showGridLineNumbers));
-    dispatch(updateShowInfiniteLines(showInfinteLines));
   },
 
   /**
-   * Updates the grid panel by dispatching the new grid data. This is called when the
-   * layout view becomes visible or the view needs to be updated with new grid data.
-   *
-   * @param {Array|null} gridFronts
-   *        Optional array of all GridFront in the current page.
+   * Destruction function called when the inspector is destroyed. Cleans up references.
    */
-  updateGridPanel: Task.async(function* (gridFronts) {
-    // Stop refreshing if the inspector or store is already destroyed.
-    if (!this.inspector || !this.store) {
-      return;
-    }
-
-    // Get all the GridFront from the server if no gridFronts were provided.
-    if (!gridFronts) {
-      gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
-    }
-
-    let grids = [];
-    for (let i = 0; i < gridFronts.length; i++) {
-      let grid = gridFronts[i];
-      let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
-
-      let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
-      let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor;
-
-      grids.push({
-        id: i,
-        color,
-        gridFragments: grid.gridFragments,
-        highlighted: nodeFront == this.highlighters.gridHighlighterShown,
-        nodeFront,
-      });
-    }
-
-    this.store.dispatch(updateGrids(grids));
-  }),
-
-  /**
-   * Handler for "grid-layout-changed" events emitted from the LayoutActor.
-   *
-   * @param  {Array} grids
-   *         Array of all GridFront in the current page.
-   */
-  onGridLayoutChange(grids) {
-    if (this.isPanelVisible()) {
-      this.updateGridPanel(grids);
-    }
-  },
-
-  /**
-   * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
-   * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
-   *
-   * @param  {Event} event
-   *         Event that was triggered.
-   * @param  {NodeFront} nodeFront
-   *         The NodeFront of the grid container element for which the grid highlighter
-   *         is shown for.
-   */
-  onHighlighterChange(event, nodeFront) {
-    let highlighted = event === "grid-highlighter-shown";
-    this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
-  },
-
-  /**
-   * Handler for the inspector sidebar select event. Starts listening for
-   * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
-   * listening for grid layout changes. Finally, refresh the layout view if
-   * it is visible.
-   */
-  onSidebarSelect() {
-    if (!this.isPanelVisible()) {
-      this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
-      return;
-    }
-
-    this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
-    this.updateGridPanel();
+  destroy() {
+    this.document = null;
+    this.inspector = null;
+    this.store = null;
   },
 
 };
 
 module.exports = LayoutView;
--- a/devtools/client/inspector/layout/moz.build
+++ b/devtools/client/inspector/layout/moz.build
@@ -1,17 +1,13 @@
 # -*- 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 += [
-    'actions',
     'components',
-    'reducers',
-    'utils',
 ]
 
 DevToolsModules(
     'layout.js',
-    'types.js',
 )
--- a/devtools/client/inspector/moz.build
+++ b/devtools/client/inspector/moz.build
@@ -2,16 +2,17 @@
 # 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 += [
     'boxmodel',
     'components',
     'computed',
     'fonts',
+    'grids',
     'layout',
     'markup',
     'rules',
     'shared'
 ]
 
 DevToolsModules(
     'breadcrumbs.js',
--- a/devtools/client/inspector/reducers.js
+++ b/devtools/client/inspector/reducers.js
@@ -3,10 +3,10 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // This file exposes the Redux reducers of the box model, grid and grid highlighter
 // settings.
 
 exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
-exports.grids = require("devtools/client/inspector/layout/reducers/grids");
-exports.highlighterSettings = require("devtools/client/inspector/layout/reducers/highlighter-settings");
+exports.grids = require("devtools/client/inspector/grids/reducers/grids");
+exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -9,16 +9,17 @@ const { devtools } = Cu.import("resource
 const { joinURI } = devtools.require("devtools/shared/path");
 const { assert } = devtools.require("devtools/shared/DevToolsUtils");
 const Services = devtools.require("Services");
 const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm");
 
 const BROWSER_BASED_DIRS = [
   "resource://devtools/client/inspector/boxmodel",
   "resource://devtools/client/inspector/computed",
+  "resource://devtools/client/inspector/grids",
   "resource://devtools/client/inspector/layout",
   "resource://devtools/client/jsonview",
   "resource://devtools/client/shared/vendor",
   "resource://devtools/client/shared/redux",
 ];
 
 const COMMON_LIBRARY_DIRS = [
   "resource://devtools/client/shared/vendor",