Bug 1456680 - Part 3: Show an active state for the grid markup badge if its grid container is highlighted. r=pbro draft
authorGabriel Luong <gabriel.luong@gmail.com>
Sun, 29 Apr 2018 13:04:57 -0400
changeset 789663 dc623b8bf6de369c65a268aafab81c2b9d0d8420
parent 789662 2ef31d77bdd5d11b8a6ad9186850725e4ae405b6
child 789664 e5a9352bf132e639bf9fe6cd87b4c75acf3d4b7c
push id108295
push userbmo:gl@mozilla.com
push dateMon, 30 Apr 2018 01:53:22 +0000
reviewerspbro
bugs1456680
milestone61.0a1
Bug 1456680 - Part 3: Show an active state for the grid markup badge if its grid container is highlighted. r=pbro highlighted. MozReview-Commit-ID: DPW4dqlDY0j
devtools/client/inspector/markup/markup.js
devtools/client/inspector/markup/test/browser_markup_display_node_01.js
devtools/client/inspector/markup/test/browser_markup_display_node_02.js
devtools/client/inspector/markup/test/browser_markup_events-overflow.js
devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
devtools/client/inspector/markup/test/helper_events_test_runner.js
devtools/client/inspector/markup/views/element-editor.js
devtools/client/inspector/shared/highlighters-overlay.js
devtools/client/themes/markup.css
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -60,22 +60,28 @@ const ATTR_COLLAPSE_LENGTH_PREF = "devto
  *         The inspector we're watching.
  * @param  {iframe} frame
  *         An iframe in which the caller has kindly loaded markup.xhtml.
  */
 function MarkupView(inspector, frame, controllerWindow) {
   EventEmitter.decorate(this);
 
   this.inspector = inspector;
+  this.highlighters = this.inspector.highlighters;
   this.walker = this.inspector.walker;
   this._frame = frame;
   this.win = this._frame.contentWindow;
   this.doc = this._frame.contentDocument;
   this._elt = this.doc.querySelector("#root");
 
+  // Stores a reference to the MarkupElementContainer of a highlighted grid.
+  // This is used to track when a MarkupElementContainer needs to be updated to
+  // inactivate its grid diplay markup badge on a grid highlighter change.
+  this.highlightedGridContainer = null;
+
   this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize",
                                                DEFAULT_MAX_CHILDREN);
 
   this.collapseAttributes = Services.prefs.getBoolPref(ATTR_COLLAPSE_ENABLED_PREF);
   this.collapseAttributeLength = Services.prefs.getIntPref(ATTR_COLLAPSE_LENGTH_PREF);
 
   // Creating the popup to be used to show CSS suggestions.
   // The popup will be attached to the toolbox document.
@@ -96,30 +102,34 @@ function MarkupView(inspector, frame, co
   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
   this._isImagePreviewTarget = this._isImagePreviewTarget.bind(this);
   this._mutationObserver = this._mutationObserver.bind(this);
   this._onBlur = this._onBlur.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onCollapseAttributesPrefChange = this._onCollapseAttributesPrefChange.bind(this);
   this._onDisplayChange = this._onDisplayChange.bind(this);
   this._onFocus = this._onFocus.bind(this);
+  this._onGridHighlighterHidden = this._onGridHighlighterHidden.bind(this);
+  this._onGridHighlighterShown = this._onGridHighlighterShown.bind(this);
   this._onMouseClick = this._onMouseClick.bind(this);
   this._onMouseMove = this._onMouseMove.bind(this);
   this._onMouseOut = this._onMouseOut.bind(this);
   this._onMouseUp = this._onMouseUp.bind(this);
   this._onNewSelection = this._onNewSelection.bind(this);
   this._onToolboxPickerCanceled = this._onToolboxPickerCanceled.bind(this);
   this._onToolboxPickerHover = this._onToolboxPickerHover.bind(this);
 
   // Listening to various events.
   this._elt.addEventListener("blur", this._onBlur, true);
   this._elt.addEventListener("click", this._onMouseClick);
   this._elt.addEventListener("mousemove", this._onMouseMove);
   this._elt.addEventListener("mouseout", this._onMouseOut);
   this._frame.addEventListener("focus", this._onFocus);
+  this.highlighters.on("grid-highlighter-hidden", this._onGridHighlighterHidden);
+  this.highlighters.on("grid-highlighter-shown", this._onGridHighlighterShown);
   this.inspector.selection.on("new-node-front", this._onNewSelection);
   this.walker.on("display-change", this._onDisplayChange);
   this.walker.on("mutations", this._mutationObserver);
   this.win.addEventListener("copy", this._onCopy);
   this.win.addEventListener("mouseup", this._onMouseUp);
   this.toolbox.on("picker-canceled", this._onToolboxPickerCanceled);
   this.toolbox.on("picker-node-hovered", this._onToolboxPickerHover);
 
@@ -1874,16 +1884,18 @@ MarkupView.prototype = {
     this.popup.destroy();
     this.popup = null;
 
     this._elt.removeEventListener("blur", this._onBlur, true);
     this._elt.removeEventListener("click", this._onMouseClick);
     this._elt.removeEventListener("mousemove", this._onMouseMove);
     this._elt.removeEventListener("mouseout", this._onMouseOut);
     this._frame.removeEventListener("focus", this._onFocus);
+    this.highlighters.off("grid-highlighter-hidden", this._onGridHighlighterHidden);
+    this.highlighters.off("grid-highlighter-shown", this._onGridHighlighterShown);
     this.inspector.selection.off("new-node-front", this._onNewSelection);
     this.toolbox.off("picker-node-hovered", this._onToolboxPickerHover);
     this.walker.off("display-change", this._onDisplayChange);
     this.walker.off("mutations", this._mutationObserver);
     this.win.removeEventListener("copy", this._onCopy);
     this.win.removeEventListener("mouseup", this._onMouseUp);
 
     this._prefObserver.off(ATTR_COLLAPSE_ENABLED_PREF,
@@ -1901,16 +1913,18 @@ MarkupView.prototype = {
 
     this.eventDetailsTooltip.destroy();
     this.eventDetailsTooltip = null;
 
     this.imagePreviewTooltip.destroy();
     this.imagePreviewTooltip = null;
 
     this.doc = null;
+    this.highlighters = null;
+    this.highlightedGridContainer = null;
     this.win = null;
 
     this._lastDropTarget = null;
     this._lastDragTarget = null;
 
     return this._destroyer;
   },
 
@@ -1996,16 +2010,56 @@ MarkupView.prototype = {
       nextSibling = null;
     }
 
     if (parent.nodeType !== nodeConstants.ELEMENT_NODE) {
       return null;
     }
 
     return {parent, nextSibling};
+  },
+
+  /**
+   * Handler for "grid-highlighter-hidden" event emitted from the HighlightersOverlay.
+   * Updates the markup view to show an inactive grid display badge for the given
+   * nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront of the grid container element for which the grid highlighter
+   *         is hidden for.
+   */
+  _onGridHighlighterHidden(nodeFront) {
+    const container = this.getContainer(nodeFront);
+
+    if (container) {
+      container.update();
+    }
+
+    this.highlightedGridContainer = null;
+  },
+
+  /**
+   * Handler for "grid-highlighter-shown" event emitted from the HighlightersOverlay.
+   * Updates the markup view to show an active grid display badge for the given nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront of the grid container element for which the grid highlighter
+   *         is shown for.
+   */
+  _onGridHighlighterShown(nodeFront) {
+    const container = this.getContainer(nodeFront);
+
+    // Update the last highlighted grid element container to inactivate its badge.
+    if (this.highlightedGridContainer) {
+      this.highlightedGridContainer.update();
+    }
+
+    if (container) {
+      container.update();
+    }
   }
 };
 
 /**
  * Map a number from one range to another.
  */
 function map(value, oldMin, oldMax, newMin, newMax) {
   let ratio = oldMax - oldMin;
--- a/devtools/client/inspector/markup/test/browser_markup_display_node_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_display_node_01.js
@@ -26,33 +26,33 @@ const TEST_URI = `
 
 add_task(async function() {
   let {inspector} = await openInspectorForURL("data:text/html;charset=utf-8," +
     encodeURIComponent(TEST_URI));
 
   info("Check the display node is shown and the value of #grid.");
   await selectNode("#grid", inspector);
   let gridContainer = await getContainerForSelector("#grid", inspector);
-  let gridDisplayNode = gridContainer.elt.querySelector(".markupview-display-badge");
+  let gridDisplayNode = gridContainer.elt.querySelector(".markup-badge[data-display]");
   is(gridDisplayNode.textContent, "grid", "Got the correct display type for #grid.");
   is(gridDisplayNode.style.display, "inline-block", "#grid display node is shown.");
 
   info("Check the display node is shown and the value of #flex.");
   await selectNode("#flex", inspector);
   let flexContainer = await getContainerForSelector("#flex", inspector);
-  let flexDisplayNode = flexContainer.elt.querySelector(".markupview-display-badge");
+  let flexDisplayNode = flexContainer.elt.querySelector(".markup-badge[data-display]");
   is(flexDisplayNode.textContent, "flex", "Got the correct display type for #flex");
   is(flexDisplayNode.style.display, "inline-block", "#flex display node is shown.");
 
   info("Check the display node is shown and the value of #block.");
   await selectNode("#block", inspector);
   let blockContainer = await getContainerForSelector("#block", inspector);
-  let blockDisplayNode = blockContainer.elt.querySelector(".markupview-display-badge");
+  let blockDisplayNode = blockContainer.elt.querySelector(".markup-badge[data-display]");
   is(blockDisplayNode.textContent, "block", "Got the correct display type for #block");
   is(blockDisplayNode.style.display, "none", "#block display node is hidden.");
 
   info("Check the display node is shown and the value of span.");
   await selectNode("span", inspector);
   let spanContainer = await getContainerForSelector("span", inspector);
-  let spanDisplayNode = spanContainer.elt.querySelector(".markupview-display-badge");
+  let spanDisplayNode = spanContainer.elt.querySelector(".markup-badge[data-display]");
   is(spanDisplayNode.textContent, "inline", "Got the correct display type for #span");
   is(spanDisplayNode.style.display, "none", "span display node is hidden.");
 });
--- a/devtools/client/inspector/markup/test/browser_markup_display_node_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_display_node_02.js
@@ -92,17 +92,17 @@ add_task(async function() {
     await runTestData(inspector, testActor, data);
   }
 });
 
 async function runTestData(inspector, testActor,
                       {selector, before, changeStyle, after}) {
   await selectNode(selector, inspector);
   let container = await getContainerForSelector(selector, inspector);
-  let displayNode = container.elt.querySelector(".markupview-display-badge");
+  let displayNode = container.elt.querySelector(".markup-badge[data-display]");
 
   is(displayNode.textContent, before.textContent,
     `Got the correct before display type for ${selector}: ${displayNode.textContent}`);
   is(displayNode.style.display, before.display,
     `Got the correct before display style for ${selector}: ${displayNode.style.display}`);
 
   info("Listening for the display-change event");
   let onDisplayChanged = inspector.markup.walker.once("display-change");
--- a/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
@@ -29,17 +29,17 @@ const TEST_DATA = [
     alignTop: false,
   },
 ];
 
 add_task(async function() {
   let { inspector } = await openInspectorForURL(TEST_URL);
 
   let markupContainer = await getContainerForSelector("#events", inspector);
-  let evHolder = markupContainer.elt.querySelector(".markupview-event-badge");
+  let evHolder = markupContainer.elt.querySelector(".markup-badge[data-event]");
   let tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Clicking to open event tooltip.");
   EventUtils.synthesizeMouseAtCenter(evHolder, {},
     inspector.markup.doc.defaultView);
   await tooltip.once("shown");
   info("EventTooltip visible.");
 
--- a/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
@@ -25,17 +25,17 @@ add_task(async function() {
   await toolbox.switchHost("bottom");
   await runTests(inspector);
 
   await toolbox.destroy();
 });
 
 async function runTests(inspector) {
   let markupContainer = await getContainerForSelector("#events", inspector);
-  let evHolder = markupContainer.elt.querySelector(".markupview-event-badge");
+  let evHolder = markupContainer.elt.querySelector(".markup-badge[data-event]");
   let tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Clicking to open event tooltip.");
 
   let onInspectorUpdated = inspector.once("inspector-updated");
   let onTooltipShown = tooltip.once("shown");
   EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
 
--- a/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
@@ -22,20 +22,20 @@ const TEST_URL = `
 
 add_task(async function() {
   let {inspector, toolbox} = await openInspectorForURL(
     "data:text/html;charset=utf-8," + encodeURI(TEST_URL));
 
   await inspector.markup.expandAll();
 
   let container1 = await getContainerForSelector("#d1", inspector);
-  let evHolder1 = container1.elt.querySelector(".markupview-event-badge");
+  let evHolder1 = container1.elt.querySelector(".markup-badge[data-event]");
 
   let container2 = await getContainerForSelector("#d2", inspector);
-  let evHolder2 = container2.elt.querySelector(".markupview-event-badge");
+  let evHolder2 = container2.elt.querySelector(".markup-badge[data-event]");
 
   let tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Click the event icon for the first element");
   let onShown = tooltip.once("shown");
   EventUtils.synthesizeMouseAtCenter(evHolder1, {},
     inspector.markup.doc.defaultView);
   await onShown;
--- a/devtools/client/inspector/markup/test/helper_events_test_runner.js
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -50,17 +50,17 @@ async function runEventPopupTests(url, t
 async function checkEventsForNode(test, inspector, testActor) {
   let {selector, expected, beforeTest, isSourceMapped} = test;
   let container = await getContainerForSelector(selector, inspector);
 
   if (typeof beforeTest === "function") {
     await beforeTest(inspector, testActor);
   }
 
-  let evHolder = container.elt.querySelector(".markupview-event-badge");
+  let evHolder = container.elt.querySelector(".markup-badge[data-event]");
 
   if (expected.length === 0) {
     // if no event is expected, simply check that the event bubble is hidden
     is(evHolder.style.display, "none", "event bubble should be hidden");
     return;
   }
 
   let tooltip = inspector.markup.eventDetailsTooltip;
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -53,16 +53,17 @@ const DISPLAY_TYPES = {
  *         The container owning this editor.
  * @param  {Element} node
  *         The node being edited.
  */
 function ElementEditor(container, node) {
   this.container = container;
   this.node = node;
   this.markup = this.container.markup;
+  this.highlighters = this.markup.highlighters;
   this.doc = this.markup.doc;
   this._cssProperties = getCssProperties(this.markup.toolbox);
 
   this.attrElements = new Map();
   this.animationTimers = {};
 
   this.elt = null;
   this.tag = null;
@@ -166,24 +167,24 @@ ElementEditor.prototype = {
 
     this.closeTag = this.doc.createElement("span");
     this.closeTag.classList.add("tag", "theme-fg-color3");
     close.appendChild(this.closeTag);
 
     close.appendChild(this.doc.createTextNode(">"));
 
     this.eventNode = this.doc.createElement("div");
-    this.eventNode.classList.add("markupview-event-badge");
+    this.eventNode.classList.add("markup-badge");
     this.eventNode.dataset.event = "true";
     this.eventNode.textContent = "event";
     this.eventNode.title = INSPECTOR_L10N.getStr("markupView.event.tooltiptext");
     this.elt.appendChild(this.eventNode);
 
     this.displayNode = this.doc.createElement("div");
-    this.displayNode.classList.add("markupview-display-badge");
+    this.displayNode.classList.add("markup-badge");
     this.elt.appendChild(this.displayNode);
   },
 
   set selected(value) {
     if (this.textEditor) {
       this.textEditor.selected = value;
     }
   },
@@ -271,20 +272,29 @@ ElementEditor.prototype = {
     }
 
     // Update the event bubble display
     this.eventNode.style.display = this.node.hasEventListeners ? "inline-block" : "none";
 
     // Update the display type node
     let showDisplayNode = this.node.displayType in DISPLAY_TYPES;
     this.displayNode.textContent = this.node.displayType;
-    this.displayNode.dataset.display = showDisplayNode ? this.node.displayType : "";
+    this.displayNode.dataset.display = this.node.displayType;
     this.displayNode.style.display = showDisplayNode ? "inline-block" : "none";
     this.displayNode.title = showDisplayNode ? DISPLAY_TYPES[this.node.displayType] : "";
 
+    if (this.highlighters.gridHighlighterShown === this.node) {
+      this.displayNode.classList.add("active");
+
+      // Store the MarkupElementContainer of the highlighted grid.
+      this.markup.highlightedGridContainer = this.container;
+    } else {
+      this.displayNode.classList.remove("active");
+    }
+
     this.updateTextEditor();
   },
 
   /**
    * Update the inline text editor in case of a single text child node.
    */
   updateTextEditor: function() {
     let node = this.node.inlineTextChild;
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -402,19 +402,19 @@ class HighlightersOverlay {
     }
 
     this._toggleRuleViewIcon(node, false, ".ruleview-grid");
 
     await this.highlighters.CssGridHighlighter.hide();
 
     // Emit the NodeFront of the grid container element that the grid highlighter was
     // hidden for.
-    this.emit("grid-highlighter-hidden", this.gridHighlighterShown,
-      this.state.grid.options);
+    const nodeFront = this.gridHighlighterShown;
     this.gridHighlighterShown = null;
+    this.emit("grid-highlighter-hidden", nodeFront, this.state.grid.options);
 
     // Erase grid highlighter state.
     this.state.grid = {};
   }
 
   /**
    * Show the box model highlighter for the given node.
    *
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -1,25 +1,27 @@
 /* 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/. */
 
 :root {
+  --markup-badge-active-background-color: var(--blue-50);
   --markup-badge-background-color: var(--grey-20);
   --markup-badge-border-color: #CACAD1;
   --markup-badge-color: var(--grey-90);
   --markup-badge-hover-background-color: #DFDFE8;
   --markup-hidden-attr-name-color: #BA89B8;
   --markup-hidden-attr-value-color: #5C6D87;
   --markup-hidden-punctuation-color: #909090;
   --markup-hidden-tag-color: #97A4B3;
   --markup-outline: var(--theme-splitter-color);
 }
 
 .theme-dark:root {
+  --markup-badge-active-background-color: var(--blue-60);
   --markup-badge-background-color: var(--grey-70);
   --markup-badge-border-color: var(--grey-50);
   --markup-badge-color: var(--grey-30);
   --markup-badge-hover-background-color: var(--grey-80);
   --markup-hidden-attr-name-color: #B07EB3;
   --markup-hidden-attr-value-color: #9893A3;
   --markup-hidden-punctuation-color: #909090;
   --markup-hidden-tag-color: #AFB5BF;
@@ -389,33 +391,41 @@ ul.children + .tag-line::before {
   color: var(--theme-selection-color);
 }
 
 /* Applicable to the DOCTYPE */
 .doctype {
   font-style: italic;
 }
 
-/* Display and Event Badges */
-.markupview-display-badge,
-.markupview-event-badge {
+/* Markup Badges */
+.markup-badge {
   display: none;
   font-size: 9px;
   font-weight: normal;
   line-height: 11px;
   vertical-align: 1px;
   border: 1px solid var(--markup-badge-border-color);
   border-radius: 3px;
   padding: 0px 2px;
   margin-inline-start: 5px;
   -moz-user-select: none;
   background-color: var(--markup-badge-background-color);
   color: var(--markup-badge-color);
 }
 
-.markupview-event-badge {
+.markup-badge.active {
+  background-color: var(--markup-badge-active-background-color);
+  border-color: var(--theme-selection-color);
+  color: var(--theme-selection-color);
+}
+
+.markup-badge[data-display="grid"],
+.markup-badge[data-event] {
   cursor: pointer;
 }
 
-.markupview-event-badge:focus,
-.markupview-event-badge:hover {
+.markup-badge[data-display="grid"]:focus,
+.markup-badge[data-display="grid"]:hover,
+.markup-badge[data-event]:focus,
+.markup-badge[data-event]:hover {
   background-color: var(--markup-badge-hover-background-color);
 }