Bug 1317102 - Part 4: Disable grid highlighter toggles in the rules when the max highlighter is reached. r=jdescottes
authorGabriel Luong <gabriel.luong@gmail.com>
Wed, 17 Oct 2018 16:51:40 -0400
changeset 500284 4c0be70aed863561a6c9d7ee84bf63b9acfb51eb
parent 500283 37647eaadbc29b3f744b225825414bca6bad5b1c
child 500288 0658fbb91e05b270f67252ec87e918d59d4aaf6d
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1317102
milestone64.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
Bug 1317102 - Part 4: Disable grid highlighter toggles in the rules when the max highlighter is reached. r=jdescottes
devtools/client/inspector/rules/test/browser.ini
devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js
devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js
devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js
devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js
devtools/client/inspector/rules/test/browser_rules_grid-toggle_05.js
devtools/client/inspector/rules/views/text-property-editor.js
devtools/client/inspector/shared/highlighters-overlay.js
devtools/client/themes/rules.css
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -176,16 +176,17 @@ skip-if = (os == "win" && debug) # bug 9
 [browser_rules_grid-highlighter-on-navigate.js]
 [browser_rules_grid-highlighter-on-reload.js]
 [browser_rules_grid-highlighter-restored-after-reload.js]
 [browser_rules_grid-toggle_01.js]
 [browser_rules_grid-toggle_01b.js]
 [browser_rules_grid-toggle_02.js]
 [browser_rules_grid-toggle_03.js]
 [browser_rules_grid-toggle_04.js]
+[browser_rules_grid-toggle_05.js]
 [browser_rules_gridline-names-autocomplete.js]
 [browser_rules_guessIndentation.js]
 [browser_rules_highlight-used-fonts.js]
 [browser_rules_inherited-properties_01.js]
 [browser_rules_inherited-properties_02.js]
 [browser_rules_inherited-properties_03.js]
 [browser_rules_inherited-properties_04.js]
 [browser_rules_inline-source-map.js]
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js
@@ -24,35 +24,37 @@ add_task(async function() {
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Toggling OFF the CSS grid highlighter from the rule-view.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   gridToggle.click();
   await onHighlighterHidden;
 
   info("Checking the CSS grid highlighter is not shown and toggle button is not active " +
     "in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js
@@ -24,35 +24,37 @@ add_task(async function() {
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Toggling OFF the CSS grid highlighter from the rule-view.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   gridToggle.click();
   await onHighlighterHidden;
 
   info("Checking the CSS grid highlighter is not shown and toggle button is not active " +
     "in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js
@@ -29,17 +29,19 @@ add_task(async function() {
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
   const overriddenContainer = getRuleViewProperty(view, "div, ul", "display").valueSpan;
   const overriddenGridToggle = overriddenContainer.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle && overriddenGridToggle, "Grid highlighter toggles are visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!overriddenGridToggle.hasAttribute("disabled"),
+    "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active") &&
     !overriddenGridToggle.classList.contains("active"),
     "Grid highlighter toggle buttons are not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the overridden rule in the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   overriddenGridToggle.click();
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js
@@ -30,42 +30,43 @@ add_task(async function() {
 
   info("Selecting the first grid container.");
   await selectNode("#grid1", inspector);
   let container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   let gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the state of the CSS grid toggle for the first grid container in the " +
     "rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter for the first grid container from the " +
     "rule-view.");
   let onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Selecting the second grid container.");
   await selectNode("#grid2", inspector);
   const firstGridHighterShown = highlighters.gridHighlighters.keys().next().value;
   container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the state of the CSS grid toggle for the second grid container in the " +
     "rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is still shown.");
 
   info("Toggling ON the CSS grid highlighter for the second grid container from the " +
     "rule-view.");
   onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
@@ -80,12 +81,12 @@ add_task(async function() {
 
   info("Selecting the first grid container.");
   await selectNode("#grid1", inspector);
   container = getRuleViewProperty(view, ".grid", "display").valueSpan;
   gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the state of the CSS grid toggle for the first grid container in the " +
     "rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js
@@ -24,35 +24,37 @@ add_task(async function() {
   const {inspector, view} = await openRuleView();
   const highlighters = view.highlighters;
 
   await selectNode("#grid", inspector);
   const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
   const gridToggle = container.querySelector(".ruleview-grid");
 
   info("Checking the initial state of the CSS grid toggle in the rule-view.");
-  ok(gridToggle, "Grid highlighter toggle is visible.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 
   info("Toggling ON the CSS grid highlighter from the rule-view.");
   const onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS grid highlighter is created and toggle button is active in " +
     "the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(gridToggle.classList.contains("active"),
     "Grid highlighter toggle is active.");
   is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
 
   info("Toggling OFF the CSS grid highlighter from the rule-view.");
   const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   gridToggle.click();
   await onHighlighterHidden;
 
   info("Checking the CSS grid highlighter is not shown and toggle button is not active " +
     "in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
   ok(!gridToggle.classList.contains("active"),
     "Grid highlighter toggle button is not active.");
   ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_05.js
@@ -0,0 +1,102 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the grid toggle is hidden when the maximum number of grid highlighters
+// have been reached.
+
+const TEST_URI = `
+  <style type='text/css'>
+    .grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid1" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid2" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+  <div id="grid3" class="grid">
+    <div class="cell1">cell1</div>
+    <div class="cell2">cell2</div>
+  </div>
+`;
+
+add_task(async function() {
+  await pushPref("devtools.gridinspector.maxHighlighters", 2);
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector, gridInspector } = await openLayoutView();
+  const ruleView = selectRuleView(inspector);
+  const { document: doc } = gridInspector;
+  const { highlighters } = inspector;
+
+  await selectNode("#grid1", inspector);
+  const gridList = doc.getElementById("grid-list");
+  const checkbox2 = gridList.children[1].querySelector("input");
+  const checkbox3 = gridList.children[2].querySelector("input");
+  const container = getRuleViewProperty(ruleView, ".grid", "display").valueSpan;
+  const gridToggle = container.querySelector(".ruleview-grid");
+
+  info("Checking the initial state of the CSS grid toggle in the rule-view.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid2.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  checkbox2.click();
+  await onHighlighterShown;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled and not active.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid3.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  checkbox3.click();
+  await onHighlighterShown;
+
+  info("Checking the CSS grid toggle for #grid1 is disabled.");
+  ok(gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is disabled.");
+  is(highlighters.gridHighlighters.size, 2, "CSS grid highlighters are shown.");
+
+  info("Toggling OFF the CSS grid highlighter for #grid3.");
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  checkbox3.click();
+  await onHighlighterHidden;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled and not active.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter for #grid1 from the rule-view.");
+  onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  gridToggle.click();
+  await onHighlighterShown;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active.");
+  is(highlighters.gridHighlighters.size, 2, "CSS grid highlighters are shown.");
+
+  info("Toggling OFF the CSS grid highlighter for #grid1 from the rule-view.");
+  onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  gridToggle.click();
+  await onHighlighterHidden;
+
+  info("Checking the CSS grid toggle for #grid1 is not disabled and not active.");
+  ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled.");
+  ok(!gridToggle.classList.contains("active"),
+    "Grid highlighter toggle button is not active.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+});
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -71,16 +71,17 @@ function TextPropertyEditor(ruleEditor, 
   this.ruleEditor = ruleEditor;
   this.ruleView = this.ruleEditor.ruleView;
   this.cssProperties = this.ruleView.cssProperties;
   this.doc = this.ruleEditor.doc;
   this.popup = this.ruleView.popup;
   this.prop = property;
   this.prop.editor = this;
   this.browserWindow = this.doc.defaultView.top;
+
   this._populatedComputed = false;
   this._hasPendingClick = false;
   this._clickedElementOptions = null;
 
   this.toolbox = this.ruleView.inspector.toolbox;
   this.telemetry = this.toolbox.telemetry;
 
   this.getGridlineNames = this.getGridlineNames.bind(this);
@@ -516,32 +517,32 @@ TextPropertyEditor.prototype = {
     if (this.ruleEditor.isEditable) {
       for (const angleSpan of this.angleSwatchSpans) {
         angleSpan.on("unit-change", this._onSwatchCommit);
         const title = l10n("rule.angleSwatch.tooltip");
         angleSpan.setAttribute("title", title);
       }
     }
 
+    const nodeFront = this.ruleView.inspector.selection.nodeFront;
+
     const flexToggle = this.valueSpan.querySelector(".ruleview-flex");
     if (flexToggle) {
       flexToggle.setAttribute("title", l10n("rule.flexToggle.tooltip"));
-      if (this.ruleView.highlighters.flexboxHighlighterShown ===
-          this.ruleView.inspector.selection.nodeFront) {
-        flexToggle.classList.add("active");
-      }
+      flexToggle.classList.toggle("active",
+        this.ruleView.highlighters.flexboxHighlighterShown === nodeFront);
     }
 
     const gridToggle = this.valueSpan.querySelector(".ruleview-grid");
     if (gridToggle) {
       gridToggle.setAttribute("title", l10n("rule.gridToggle.tooltip"));
-      if (this.ruleView.highlighters.gridHighlighters.has(
-            this.ruleView.inspector.selection.nodeFront)) {
-        gridToggle.classList.add("active");
-      }
+      gridToggle.classList.toggle("active",
+        this.ruleView.highlighters.gridHighlighters.has(nodeFront));
+      gridToggle.toggleAttribute("disabled",
+        !this.ruleView.highlighters.canGridHighlighterToggle(nodeFront));
     }
 
     const shapeToggle = this.valueSpan.querySelector(".ruleview-shapeswatch");
     if (shapeToggle) {
       const mode = "css" + name.split("-").map(s => {
         return s[0].toUpperCase() + s.slice(1);
       }).join("");
       shapeToggle.setAttribute("data-mode", mode);
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -23,16 +23,18 @@ class HighlightersOverlay {
    * @param  {Inspector} inspector
    *         Inspector toolbox panel.
    */
   constructor(inspector) {
     this.inspector = inspector;
     this.highlighterUtils = this.inspector.toolbox.highlighterUtils;
     this.store = this.inspector.store;
     this.telemetry = inspector.telemetry;
+    this.maxGridHighlighters =
+      Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters");
 
     // Collection of instantiated highlighter actors like FlexboxHighlighter,
     // ShapesHighlighter and GeometryEditorHighlighter.
     this.highlighters = {};
     // Map of grid container NodeFront to their instantiated grid highlighter actors.
     this.gridHighlighters = new Map();
     // Array of reusable grid highlighters that have been instantiated and are not
     // associated with any NodeFront.
@@ -427,29 +429,26 @@ class HighlightersOverlay {
    * @param  {Object} options
    *         Object used for passing options to the grid highlighter.
    * @param. {String|null} trigger
    *         String name matching "grid" or "rule" to indicate where the
    *         grid highlighter was toggled on from. "grid" represents the grid view
    *         "rule" represents the rule view.
    */
   async showGridHighlighter(node, options, trigger) {
-    const maxHighlighters =
-      Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters");
-
     // When the grid highlighter has the given node, it is probably called with new
     // highlighting options, so skip any extra grid highlighter handling.
     if (!this.gridHighlighters.has(node)) {
-      if (maxHighlighters === 1) {
+      if (this.maxGridHighlighters === 1) {
         // Only one grid highlighter can be shown at a time. Hides any instantiated
         // grid highlighters.
         for (const nodeFront of this.gridHighlighters.keys()) {
           await this.hideGridHighlighter(nodeFront);
         }
-      } else if (this.gridHighlighters.size === maxHighlighters) {
+      } else if (this.gridHighlighters.size === this.maxGridHighlighters) {
         // The maximum number of grid highlighters shown have been reached. Don't show
         // any additional grid highlighters.
         return;
       }
     }
 
     const highlighter = await this._getGridHighlighter(node);
     if (!highlighter) {
@@ -491,27 +490,27 @@ class HighlightersOverlay {
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element to unhighlight.
    */
   async hideGridHighlighter(node) {
     if (!this.gridHighlighters.has(node)) {
       return;
     }
 
-    this._toggleRuleViewIcon(node, false, ".ruleview-grid");
-
     // Hide the highlighter and put it in the pool of extra grid highlighters
     // so that it can be reused.
     const highlighter = this.gridHighlighters.get(node);
     await highlighter.hide();
     this.extraGridHighlighterPool.push(highlighter);
 
     this.state.grids.delete(node);
     this.gridHighlighters.delete(node);
 
+    this._toggleRuleViewIcon(node, false, ".ruleview-grid");
+
     // Emit the NodeFront of the grid container element that the grid highlighter was
     // hidden for.
     this.emit("grid-highlighter-hidden", node);
   }
 
   /**
    * Show the box model highlighter for the given node.
    *
@@ -785,22 +784,32 @@ class HighlightersOverlay {
    * @param  {NodeFront} node
    *         The NodeFront of the element with a shape to highlight.
    * @param  {Boolean} active
    *         Whether or not the shape icon should be active.
    * @param  {String} selector
    *         The selector of the rule view icon to toggle.
    */
   _toggleRuleViewIcon(node, active, selector) {
-    if (this.inspector.selection.nodeFront != node) {
+    const ruleViewEl = this.inspector.getPanel("ruleview").view.element;
+
+    if (this.inspector.selection.nodeFront !== node) {
+      if (selector === ".ruleview-grid") {
+        for (const icon of ruleViewEl.querySelectorAll(selector)) {
+          if (this.canGridHighlighterToggle(this.inspector.selection.nodeFront)) {
+            icon.removeAttribute("disabled");
+          } else {
+            icon.setAttribute("disabled", true);
+          }
+        }
+      }
+
       return;
     }
 
-    const ruleViewEl = this.inspector.getPanel("ruleview").view.element;
-
     for (const icon of ruleViewEl.querySelectorAll(selector)) {
       icon.classList.toggle("active", active);
     }
   }
 
   /**
    * Toggle the class "active" on the given shape point in the rule view if the current
    * inspector selection is highlighted by the shapes highlighter.
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -435,16 +435,21 @@
   border-radius: 0;
 }
 
 .ruleview-grid {
   background: url("chrome://devtools/skin/images/grid.svg");
   border-radius: 0;
 }
 
+.ruleview-grid[disabled] {
+  cursor: default;
+  opacity: 0.5;
+}
+
 .ruleview-shape-point.active,
 .ruleview-shapeswatch.active + .ruleview-shape > .ruleview-shape-point:hover {
   background-color: var(--rule-highlight-background-color);
 }
 
 .ruleview-colorswatch::before {
   content: '';
   background-color: #eee;