Bug 1297100 - Display CSS Grid layouts when highlighting elements in the page r=pbro draft
authorGabriel Luong <gabriel.luong@gmail.com>
Tue, 24 Jul 2018 16:27:09 -0400
changeset 822132 e46e1d129f90b35a0f55721fe5958c6f82e23bff
parent 822131 dc036d48bba75840d54bd31b57f6d79ec67b4c71
push id117296
push userbmo:gl@mozilla.com
push dateTue, 24 Jul 2018 20:28:07 +0000
reviewerspbro
bugs1297100
milestone63.0a1
Bug 1297100 - Display CSS Grid layouts when highlighting elements in the page r=pbro MozReview-Commit-ID: 3i9XkYjWivo
devtools/server/actors/highlighters.css
devtools/server/actors/highlighters/box-model.js
devtools/server/actors/highlighters/css-grid.js
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -59,16 +59,22 @@
 }
 
 :-moz-native-anonymous .highlighter-container.box-model {
   /* Make the box-model container have a z-index other than auto so it always sits above
      other highlighters. */
   z-index: 1;
 }
 
+:-moz-native-anonymous .highlighter-container.css-grid {
+  /* Make the css grid container have a z-index greater than the box-model so it always
+     sits above other highlighters. */
+  z-index: 2;
+}
+
 :-moz-native-anonymous .highlighter-container [hidden] {
   display: none;
 }
 
 :-moz-native-anonymous .highlighter-container [dragging] {
   cursor: grabbing;
 }
 
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -16,16 +16,18 @@ const {
 } = require("./utils/markup");
 const {
   setIgnoreLayoutChanges,
   getCurrentZoom,
  } = require("devtools/shared/layout/utils");
 const { getNodeDisplayName } = require("devtools/server/actors/inspector/utils");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 
+loader.lazyRequireGetter(this, "CssGridHighlighter", "devtools/server/actors/highlighters/css-grid", true);
+
 // 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;
 // FIXME: add ":visited" and ":link" after bug 713106 is fixed
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
@@ -269,19 +271,33 @@ class BoxModelHighlighter extends AutoRe
     this.highlighterEnv.off("will-navigate", this.onWillNavigate);
 
     const { pageListenerTarget } = this.highlighterEnv;
     if (pageListenerTarget) {
       pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
     }
 
     this.markup.destroy();
+
+    if (this._cssGridHighlighter) {
+      this._cssGridHighlighter.destroy();
+      this._cssGridHighlighter = null;
+    }
+
     AutoRefreshHighlighter.prototype.destroy.call(this);
   }
 
+  get cssGridHighlighter() {
+    if (!this._cssGridHighlighter) {
+      this._cssGridHighlighter = new CssGridHighlighter(this.highlighterEnv);
+    }
+
+    return this._cssGridHighlighter;
+  }
+
   getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
   }
 
   /**
    * Override the AutoRefreshHighlighter's _isNodeValid method to also return true for
    * text nodes since these can also be highlighted.
    * @param {DOMNode} node
@@ -345,39 +361,74 @@ class BoxModelHighlighter extends AutoRe
       }
       this._showBoxModel();
       shown = true;
     } else {
       // Nothing to highlight (0px rectangle like a <script> tag for instance)
       this._hide();
     }
 
+    this._updateGridHighlighter();
+
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
 
     return shown;
   }
 
+  /**
+   * Update the grid highlighter on the current highlighted node. Show the CSS grid
+   * highlighter if the current node is a grid container or grid item.
+   */
+  _updateGridHighlighter() {
+    this._hideCssGridHighlighter();
+
+    let gridNode;
+    if (this.currentNode &&
+        this.currentNode.getGridFragments &&
+        this.currentNode.getGridFragments().length) {
+      gridNode = this.currentNode;
+    } else if (this.currentNode.parentNode &&
+               this.currentNode.parentNode.getGridFragments &&
+               this.currentNode.parentNode.getGridFragments().length) {
+      gridNode = this.currentNode.parentNode;
+    }
+
+    if (gridNode) {
+      this.cssGridHighlighter.show(gridNode, { showBoldGridBorders: true });
+    }
+  }
+
   _scrollUpdate() {
     this._moveInfobar();
   }
 
   /**
    * Hide the highlighter, the outline and the infobar.
    */
   _hide() {
     setIgnoreLayoutChanges(true);
 
     this._untrackMutations();
     this._hideBoxModel();
     this._hideInfobar();
+    this._hideCssGridHighlighter();
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
   }
 
   /**
+   * Hide the CSS Grid highlighter.
+   */
+  _hideCssGridHighlighter() {
+    if (this._cssGridHighlighter) {
+      this.cssGridHighlighter.hide();
+    }
+  }
+
+  /**
    * Hide the infobar
    */
   _hideInfobar() {
     this.getElement("infobar-container").setAttribute("hidden", "true");
   }
 
   /**
    * Show the infobar
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -136,16 +136,20 @@ const gCachedGridPattern = new Map();
  *
  * Available Options:
  * - color(colorValue)
  *     @param  {String} colorValue
  *     The color that should be used to draw the highlighter for this grid.
  * - showAllGridAreas(isShown)
  *     @param  {Boolean} isShown
  *     Shows all the grid area highlights for the current grid if isShown is true.
+ * - showBoldGridBorders(isShown)
+ *     Shows a bold grid border for the first and last lines of the grid. This option
+ *     is used in the box-model highlighter to show a bold grid border when overlapping
+ *     with the box-model highlight.
  * - showGridArea(areaName)
  *     @param  {String} areaName
  *     Shows the grid area highlight for the given area name.
  * - showGridAreasOverlay(isShown)
  *     @param  {Boolean} isShown
  *     Displays an overlay of all the grid areas for the current grid container if
  *     isShown is true.
  * - showGridCell({ gridFragmentIndex: Number, rowNumber: Number, columnNumber: Number })
@@ -226,17 +230,17 @@ class CssGridHighlighter extends AutoRef
     // on a page that has scrolled already.
     updateCanvasPosition(this._canvasPosition, this._scroll, this.win,
       this._winDimensions);
   }
 
   _buildMarkup() {
     const container = createNode(this.win, {
       attributes: {
-        "class": "highlighter-container"
+        "class": "highlighter-container css-grid"
       }
     });
 
     const root = createNode(this.win, {
       parent: container,
       attributes: {
         "id": "root",
         "class": "root"
@@ -1407,18 +1411,20 @@ class CssGridHighlighter extends AutoRef
    * @param  {Number} startPos
    *         The start position of the cross side of the grid line.
    * @param  {Number} endPos
    *         The end position of the cross side of the grid line.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
    * @param  {String} lineType
    *         The grid line type - "edge", "explicit", or "implicit".
+   * @param  {Boolean} isFirstOrLastLine
+   *         Whether or not this is the first or last line for a row or column grid line.
    */
-  renderLine(linePos, startPos, endPos, dimensionType, lineType) {
+  renderLine(linePos, startPos, endPos, dimensionType, lineType, isFirstOrLastLine) {
     const { devicePixelRatio } = this.win;
     const lineWidth = getDisplayPixelRatio(this.win);
     const offset = (lineWidth / 2) % 1;
     const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
     const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
 
     linePos = Math.round(linePos);
     startPos = Math.round(startPos);
@@ -1442,19 +1448,20 @@ class CssGridHighlighter extends AutoRef
     } else {
       drawLine(this.ctx, startPos, linePos, endPos, linePos, lineOptions);
     }
 
     this.ctx.strokeStyle = this.color;
     this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha;
 
     if (GRID_LINES_PROPERTIES[lineType].lineWidth) {
-      this.ctx.lineWidth = GRID_LINES_PROPERTIES[lineType].lineWidth * devicePixelRatio;
+      this.ctx.lineWidth = GRID_LINES_PROPERTIES[lineType].lineWidth * devicePixelRatio *
+        (isFirstOrLastLine ? 2 : 1);
     } else {
-      this.ctx.lineWidth = lineWidth;
+      this.ctx.lineWidth = lineWidth * (isFirstOrLastLine ? 2 : 1);
     }
 
     this.ctx.stroke();
     this.ctx.restore();
   }
 
   /**
    * Render the grid lines given the grid dimension information of the
@@ -1471,25 +1478,30 @@ class CssGridHighlighter extends AutoRef
    *         of the grid dimension.
    * @param  {Number} endPos
    *         The end position of the cross side ("left" for ROWS and "top" for COLUMNS)
    *         of the grid dimension.
    */
   renderLines(gridDimension, dimensionType, startPos, endPos) {
     const { lines, tracks } = gridDimension;
     const lastEdgeLineIndex = this.getLastEdgeLineIndex(tracks);
+    const showBoldGridBorders = this.options.showBoldGridBorders;
 
     for (let i = 0; i < lines.length; i++) {
       const line = lines[i];
       const linePos = line.start;
 
       if (i == 0 || i == lastEdgeLineIndex) {
-        this.renderLine(linePos, startPos, endPos, dimensionType, "edge");
+        const isFirstOrLastLine = showBoldGridBorders &&
+          (i == 0 || i == lines.length - 1);
+        this.renderLine(linePos, startPos, endPos, dimensionType, "edge",
+          isFirstOrLastLine);
       } else {
-        this.renderLine(linePos, startPos, endPos, dimensionType, tracks[i - 1].type);
+        this.renderLine(linePos, startPos, endPos, dimensionType, tracks[i - 1].type,
+          showBoldGridBorders && i == lines.length - 1);
       }
 
       // Render a second line to illustrate the gutter for non-zero breadth.
       if (line.breadth > 0) {
         this.renderGridGap(linePos, startPos, endPos, line.breadth, dimensionType);
         this.renderLine(linePos + line.breadth, startPos, endPos, dimensionType,
           tracks[i].type);
       }