Bug 1521188 - Indicate grid/flex container/item in infobar highlighter. r=pbro
authorThomas <tgerard79@yahoo.fr>
Mon, 10 Jun 2019 17:09:56 +0000
changeset 478136 c99100c3a71f8aa8c031d2d4e03ce2d28c9355fc
parent 478135 7e1265c0234559fd7cf77dec3c98bf5b5862545a
child 478137 4a538b1e4768e482e50843faa775fdc9689cffb2
push id36136
push usernerli@mozilla.com
push dateTue, 11 Jun 2019 03:18:15 +0000
treeherdermozilla-central@bb62c9157f04 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1521188
milestone69.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 1521188 - Indicate grid/flex container/item in infobar highlighter. r=pbro Indicate in the infobar highlighter if the element is of kind grid or flex, and if it is a container or an item. Differential Revision: https://phabricator.services.mozilla.com/D29964
devtools/client/inspector/test/browser.ini
devtools/client/inspector/test/browser_inspector_infobar_05.js
devtools/client/inspector/test/doc_inspector_infobar_04.html
devtools/server/actors/highlighters.css
devtools/server/actors/highlighters/box-model.js
devtools/server/actors/inspector/utils.js
devtools/shared/locales/en-US/highlighters.properties
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
   doc_inspector_highlighter.html
   doc_inspector_highlighter_rect.html
   doc_inspector_highlighter_rect_iframe.html
   doc_inspector_highlighter_scroll.html
   doc_inspector_highlighter_xbl.xul
   doc_inspector_infobar_01.html
   doc_inspector_infobar_02.html
   doc_inspector_infobar_03.html
+  doc_inspector_infobar_04.html
   doc_inspector_infobar_textnode.html
   doc_inspector_long-divs.html
   doc_inspector_menu.html
   doc_inspector_outerhtml.html
   doc_inspector_reload_xul.xul
   doc_inspector_remove-iframe-during-load.html
   doc_inspector_search.html
   doc_inspector_search-reserved.html
@@ -135,16 +136,17 @@ skip-if = (os == 'win' && !debug) # Bug 
 [browser_inspector_highlighter-selector_02.js]
 [browser_inspector_highlighter-xbl.js]
 [browser_inspector_highlighter-zoom.js]
 [browser_inspector_iframe-navigation.js]
 [browser_inspector_infobar_01.js]
 [browser_inspector_infobar_02.js]
 [browser_inspector_infobar_03.js]
 [browser_inspector_infobar_04.js]
+[browser_inspector_infobar_05.js]
 [browser_inspector_infobar_textnode.js]
 [browser_inspector_initialization.js]
 skip-if = (e10s && debug) # Bug 1250058 - Docshell leak on debug e10s
 [browser_inspector_inspect-object-element.js]
 [browser_inspector_inspect_node_contextmenu.js]
 [browser_inspector_invalidate.js]
 [browser_inspector_keyboard-shortcuts-copy-outerhtml.js]
 tags = clipboard
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_infobar_05.js
@@ -0,0 +1,109 @@
+/* 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";
+
+// Bug 1521188 - Indicate grid/flex container/item in infobar
+// Check the text content of the highlighter nodeinfo bar.
+const STRINGS_URI = "devtools/shared/locales/highlighters.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+
+const TEST_URI = URL_ROOT + "doc_inspector_infobar_04.html";
+
+const CLASS_GRID_TYPE = "box-model-infobar-grid-type";
+const CLASS_FLEX_TYPE = "box-model-infobar-flex-type";
+
+const FLEX_CONTAINER_TEXT = L10N.getStr("flexType.container");
+const FLEX_ITEM_TEXT = L10N.getStr("flexType.item");
+const FLEX_DUAL_TEXT = L10N.getStr("flexType.dual");
+const GRID_CONTAINER_TEXT = L10N.getStr("gridType.container");
+const GRID_ITEM_TEXT = L10N.getStr("gridType.item");
+const GRID_DUAL_TEXT = L10N.getStr("gridType.dual");
+
+const TEST_DATA = [
+  {
+    selector: "#flex-container",
+    flexText: FLEX_CONTAINER_TEXT,
+    gridText: "",
+  },
+  {
+    selector: "#flex-item",
+    flexText: FLEX_ITEM_TEXT,
+    gridText: "",
+  },
+  {
+    selector: "#flex-container-item",
+    flexText: FLEX_DUAL_TEXT,
+    gridText: "",
+  },
+  {
+    selector: "#grid-container",
+    flexText: "",
+    gridText: GRID_CONTAINER_TEXT,
+  },
+  {
+    selector: "#grid-item",
+    flexText: "",
+    gridText: GRID_ITEM_TEXT,
+  },
+  {
+    selector: "#grid-container-item",
+    flexText: "",
+    gridText: GRID_DUAL_TEXT,
+  },
+  {
+    selector: "#flex-item-grid-container",
+    flexText: FLEX_ITEM_TEXT,
+    gridText: GRID_CONTAINER_TEXT,
+  },
+];
+
+const TEST_TEXT_DATA = [
+  {
+    selector: "#flex-text-container",
+    flexText: FLEX_ITEM_TEXT,
+    gridText: "",
+  },
+  {
+    selector: "#grid-text-container",
+    flexText: "",
+    gridText: GRID_ITEM_TEXT,
+  },
+];
+
+add_task(async function() {
+  const { inspector, testActor } = await openInspectorForURL(TEST_URI);
+
+  for (const currentTest of TEST_DATA) {
+    info("Testing " + currentTest.selector);
+    await testTextContent(currentTest, inspector, testActor);
+  }
+
+  for (const currentTest of TEST_TEXT_DATA) {
+    info("Testing " + currentTest.selector);
+    await testTextNodeTextContent(currentTest, inspector, testActor);
+  }
+});
+
+async function testTextContent({ selector, gridText, flexText }, inspector, testActor) {
+  await selectAndHighlightNode(selector, inspector);
+
+  const gridType = await testActor.getHighlighterNodeTextContent(
+    CLASS_GRID_TYPE
+  );
+  const flexType = await testActor.getHighlighterNodeTextContent(
+    CLASS_FLEX_TYPE
+  );
+
+  is(gridType, gridText, "node " + selector + ": grid type matches.");
+  is(flexType, flexText, "node " + selector + ": flex type matches.");
+}
+
+async function testTextNodeTextContent(test, inspector, testActor) {
+  const { walker } = inspector;
+  const div = await walker.querySelector(walker.rootNode, test.selector);
+  const { nodes } = await walker.children(div);
+  test.selector = nodes[0];
+  await testTextContent(test, inspector, testActor);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/doc_inspector_infobar_04.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+
+    <style>
+      .flex {
+        display: flex;
+      }
+
+      .grid {
+        display: grid;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="flex-container" class="flex">
+      <div id="flex-item"></div>
+      <div id="flex-container-item" class="flex"></div>
+    </div>
+
+    <div id="grid-container" class="grid">
+      <div id="grid-item"></div>
+      <div id="grid-container-item" class="grid"></div>
+    </div>
+
+    <div id="flex-container-with-grid" class="flex">
+      <div id="flex-item-grid-container" class="grid"></div>
+    </div>
+
+    <div id="flex-text-container" class="flex">
+      flex item (node text)
+    </div>
+
+    <div id="grid-text-container" class="grid">
+      grid item (node text). The text content for this text node needs to be long enough
+      so that the inspector does not inline it. Indeed we want to be able to select and
+      highlight it independently in the inspector so we can test the grid item infobar.
+    </div>
+  </body>
+</html>
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -205,23 +205,38 @@
 
 :-moz-native-anonymous .box-model-infobar-classes,
 :-moz-native-anonymous .box-model-infobar-pseudo-classes {
   color: hsl(200, 74%, 57%);
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
-:-moz-native-anonymous [class$=infobar-dimensions] {
-  color: var(--highlighter-infobar-color);
+:-moz-native-anonymous [class$=infobar-dimensions],
+:-moz-native-anonymous [class$=infobar-grid-type],
+:-moz-native-anonymous [class$=infobar-flex-type] {
   border-inline-start: 1px solid #5a6169;
   margin-inline-start: 6px;
   padding-inline-start: 6px;
 }
 
+:-moz-native-anonymous [class$=infobar-grid-type]:empty,
+:-moz-native-anonymous [class$=infobar-flex-type]:empty {
+  display: none;
+}
+
+:-moz-native-anonymous [class$=infobar-dimensions] {
+  color: var(--highlighter-infobar-color);
+}
+
+:-moz-native-anonymous [class$=infobar-grid-type],
+:-moz-native-anonymous [class$=infobar-flex-type] {
+  color: var(--grey-40);
+}
+
 /* CSS Grid Highlighter */
 
 :-moz-native-anonymous .css-grid-canvas {
   position: absolute;
   pointer-events: none;
   top: 0;
   left: 0;
   image-rendering: -moz-crisp-edges;
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -14,18 +14,21 @@ const {
   isNodeValid,
   moveInfobar,
 } = require("./utils/markup");
 const { PSEUDO_CLASSES } = require("devtools/shared/css/constants");
 const {
   getCurrentZoom,
   setIgnoreLayoutChanges,
  } = require("devtools/shared/layout/utils");
-const { getNodeDisplayName } = require("devtools/server/actors/inspector/utils");
+const { getNodeDisplayName, getNodeGridFlexType } = require("devtools/server/actors/inspector/utils");
 const nodeConstants = require("devtools/shared/dom-node-constants");
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const STRINGS_URI = "devtools/shared/locales/highlighters.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
 
 // Note that the order of items in this array is important because it is used
 // for drawing the BoxModelHighlighter's path elements correctly.
 const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
 const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
 // Width of boxmodelhighlighter guides
 const GUIDE_STROKE_WIDTH = 1;
 
@@ -76,16 +79,18 @@ const GUIDE_STROKE_WIDTH = 1;
  *     <div class="box-model-infobar-container">
  *       <div class="box-model-infobar-arrow highlighter-infobar-arrow-top" />
  *       <div class="box-model-infobar">
  *         <div class="box-model-infobar-text" align="center">
  *           <span class="box-model-infobar-tagname">Node name</span>
  *           <span class="box-model-infobar-id">Node id</span>
  *           <span class="box-model-infobar-classes">.someClass</span>
  *           <span class="box-model-infobar-pseudo-classes">:hover</span>
+ *           <span class="box-model-infobar-grid-type">Grid Type</span>
+ *           <span class="box-model-infobar-flex-type">Flex Type</span>
  *         </div>
  *       </div>
  *       <div class="box-model-infobar-arrow box-model-infobar-arrow-bottom"/>
  *     </div>
  *   </div>
  * </div>
  */
 class BoxModelHighlighter extends AutoRefreshHighlighter {
@@ -251,16 +256,36 @@ class BoxModelHighlighter extends AutoRe
       parent: texthbox,
       attributes: {
         "class": "infobar-dimensions",
         "id": "infobar-dimensions",
       },
       prefix: this.ID_CLASS_PREFIX,
     });
 
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        "class": "infobar-grid-type",
+        "id": "infobar-grid-type",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
+    createNode(this.win, {
+      nodeType: "span",
+      parent: texthbox,
+      attributes: {
+        "class": "infobar-flex-type",
+        "id": "infobar-flex-type",
+      },
+      prefix: this.ID_CLASS_PREFIX,
+    });
+
     return highlighterContainer;
   }
 
   /**
    * Destroy the nodes. Remove listeners.
    */
   destroy() {
     this.highlighterEnv.off("will-navigate", this.onWillNavigate);
@@ -706,25 +731,44 @@ class BoxModelHighlighter extends AutoRe
       return;
     }
 
     const { width, height } = quad.bounds;
     const dim = parseFloat((width / zoom).toPrecision(6)) +
               " \u00D7 " +
               parseFloat((height / zoom).toPrecision(6));
 
+    const { grid: gridType, flex: flexType } = getNodeGridFlexType(node);
+    const gridLayoutTextType = this._getLayoutTextType("gridType", gridType);
+    const flexLayoutTextType = this._getLayoutTextType("flexType", flexType);
+
     this.getElement("infobar-tagname").setTextContent(displayName);
     this.getElement("infobar-id").setTextContent(id);
     this.getElement("infobar-classes").setTextContent(classList);
     this.getElement("infobar-pseudo-classes").setTextContent(pseudos);
     this.getElement("infobar-dimensions").setTextContent(dim);
+    this.getElement("infobar-grid-type").setTextContent(gridLayoutTextType);
+    this.getElement("infobar-flex-type").setTextContent(flexLayoutTextType);
 
     this._moveInfobar();
   }
 
+  _getLayoutTextType(layoutTypeKey, { isContainer, isItem }) {
+    if (!isContainer && !isItem) {
+      return "";
+    }
+    if (isContainer && !isItem) {
+      return L10N.getStr(`${layoutTypeKey}.container`);
+    }
+    if (!isContainer && isItem) {
+      return L10N.getStr(`${layoutTypeKey}.item`);
+    }
+    return L10N.getStr(`${layoutTypeKey}.dual`);
+  }
+
   _getPseudoClasses(node) {
     if (node.nodeType !== nodeConstants.ELEMENT_NODE) {
       // hasPseudoClassLock can only be used on Elements.
       return [];
     }
 
     return PSEUDO_CLASSES.filter(pseudo => hasPseudoClassLock(node, pseudo));
   }
--- a/devtools/server/actors/inspector/utils.js
+++ b/devtools/server/actors/inspector/utils.js
@@ -34,16 +34,46 @@ const getNodeDisplayName = function(rawN
   if (rawNode.nodeName && !rawNode.localName) {
     // The localName & prefix APIs have been moved from the Node interface to the Element
     // interface. Use Node.nodeName as a fallback.
     return rawNode.nodeName;
   }
   return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
 };
 
+/**
+ * Returns flex and grid information about a DOM node.
+ * In particular is it a grid flex/container and/or item?
+ *
+ * @param  {DOMNode} node
+ *         The node for which then information is required
+ * @return {Object}
+ *         An object like { grid: { isContainer, isItem }, flex: { isContainer, isItem } }
+ */
+function getNodeGridFlexType(node) {
+  return {
+    grid: getNodeGridType(node),
+    flex: getNodeFlexType(node),
+  };
+}
+
+function getNodeFlexType(node) {
+  return {
+    isContainer: node.getAsFlexContainer && !!node.getAsFlexContainer(),
+    isItem: !!node.parentFlexElement,
+  };
+}
+
+function getNodeGridType(node) {
+  return {
+    isContainer: node.getGridFragments && node.getGridFragments().length,
+    isItem: !!findGridParentContainerForNode(node),
+  };
+}
+
 function nodeDocument(node) {
   if (Cu.isDeadWrapper(node)) {
     return null;
   }
   return node.ownerDocument ||
          (node.nodeType == Node.DOCUMENT_NODE ? node : null);
 }
 
@@ -347,14 +377,15 @@ function findGridParentContainerForNode(
 }
 
 module.exports = {
   allAnonymousContentTreeWalkerFilter,
   findGridParentContainerForNode,
   getClosestBackgroundColor,
   getClosestBackgroundImage,
   getNodeDisplayName,
+  getNodeGridFlexType,
   imageToImageData,
   isNodeDead,
   nodeDocument,
   scrollbarTreeWalkerFilter,
   standardTreeWalkerFilter,
 };
--- a/devtools/shared/locales/en-US/highlighters.properties
+++ b/devtools/shared/locales/en-US/highlighters.properties
@@ -4,8 +4,32 @@
 
 # LOCALIZATION NOTE This file contains strings used in highlighters.
 # Highlighters are visualizations that DevTools draws on top of content to aid
 # in understanding content sizing, etc.
 
 # LOCALIZATION NOTE (grid.rowColumnPositions): The row and column position of a grid
 # cell shown in the grid cell infobar when hovering over the CSS grid outline.
 grid.rowColumnPositions=Row %S / Column %S
+
+# LOCALIZATION NOTE (gridType.container): the layout type of an element shown in
+# the infobar when hovering over a DOM element and it is a grid container.
+gridType.container=Grid Container
+
+# LOCALIZATION NOTE (gridType.item): the layout type of an element shown in
+# the infobar when hovering over a DOM element and it is a grid item.
+gridType.item=Grid Item
+
+# LOCALIZATION NOTE (gridType.dual): the layout type of an element shown in
+# the infobar when hovering over a DOM element and it is both a grid container and a grid item.
+gridType.dual=Grid Container/Item
+
+# LOCALIZATION NOTE (flexType.container): the layout type of an element shown in
+# the infobar when hovering over a DOM element and it is a flex container.
+flexType.container=Flex Container
+
+# LOCALIZATION NOTE (flexType.item): the layout type of an element shown in
+# the infobar when hovering over a DOM element and it is a flex item.
+flexType.item=Flex Item
+
+# LOCALIZATION NOTE (flexType.dual): the layout type of an element shown in
+# the infobar when hovering over a DOM element and it is both a flex container and a flex item.
+flexType.dual=Flex Container/Item