Bug 1249558 - Part 1: Display the grid area highlight in the css grid overlay r=pbro
authorGabriel Luong <gabriel.luong@gmail.com>
Wed, 07 Sep 2016 15:41:48 -0400
changeset 313120 443263a8af4effc5c55b806d9fc87263061bb085
parent 313119 fb737d495a7912d8030f7ffc5e277ed496e1a7a4
child 313121 f46da98e6eddfc6df2b5e0aa565cc47fd752c0a7
push id30672
push usercbook@mozilla.com
push dateThu, 08 Sep 2016 10:00:36 +0000
treeherdermozilla-central@331524df5cab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1249558
milestone51.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 1249558 - Part 1: Display the grid area highlight in the css grid overlay r=pbro
devtools/server/actors/highlighters.css
devtools/server/actors/highlighters/css-grid.js
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -50,17 +50,17 @@
 :-moz-native-anonymous .highlighter-container [hidden] {
   display: none;
 }
 
 :-moz-native-anonymous .highlighter-container [dragging] {
   cursor: grabbing;
 }
 
-/* Box model highlighter */
+/* Box Model Highlighter */
 
 :-moz-native-anonymous .box-model-regions {
   opacity: 0.6;
 }
 
 /* Box model regions can be faded (see the onlyRegionArea option in
    highlighters.js) in order to only display certain regions. */
 :-moz-native-anonymous .box-model-regions [faded] {
@@ -197,17 +197,36 @@
 
 :-moz-native-anonymous .box-model-nodeinfobar-dimensions {
   color: hsl(210, 30%, 85%);
   border-inline-start: 1px solid #5a6169;
   margin-inline-start: 6px;
   padding-inline-start: 6px;
 }
 
-/* Css transform highlighter */
+/* CSS Grid Highlighter */
+
+:-moz-native-anonymous .css-grid-canvas {
+  position: absolute;
+  pointer-events: none;
+  top: 0;
+  left: 0;
+  image-rendering: -moz-crisp-edges;
+}
+
+:-moz-native-anonymous .css-grid-regions {
+  opacity: 0.6;
+}
+
+:-moz-native-anonymous .css-grid-areas {
+  fill: #CEC0ED;
+  stroke: none;
+}
+
+/* CSS Transform Highlighter */
 
 :-moz-native-anonymous .css-transform-transformed {
   fill: var(--highlighter-content-color);
   opacity: 0.8;
 }
 
 :-moz-native-anonymous .css-transform-untransformed {
   fill: #66cc52;
@@ -217,25 +236,25 @@
 :-moz-native-anonymous .css-transform-transformed,
 :-moz-native-anonymous .css-transform-untransformed,
 :-moz-native-anonymous .css-transform-line {
   stroke: var(--highlighter-guide-color);
   stroke-dasharray: 5 3;
   stroke-width: 2;
 }
 
-/* Rect highlighter */
+/* Rect Highlighter */
 
 :-moz-native-anonymous .highlighted-rect {
   position: absolute;
   background: var(--highlighter-content-color);
   opacity: 0.8;
 }
 
-/* Element geometry highlighter */
+/* Element Geometry Highlighter */
 
 :-moz-native-anonymous .geometry-editor-root {
   /* The geometry editor can be interacted with, so it needs to react to
      pointer events */
   pointer-events: auto;
   -moz-user-select: none;
 }
 
@@ -295,17 +314,17 @@
 :-moz-native-anonymous .geometry-editor-label-text {
   fill: var(--highlighter-bubble-text-color);
   font: message-box;
   font-size: 10px;
   text-anchor: middle;
   dominant-baseline: middle;
 }
 
-/* Rulers highlighter */
+/* Rulers Highlighter */
 
 :-moz-native-anonymous .rulers-highlighter-elements {
   shape-rendering: crispEdges;
   pointer-events: none;
   position: fixed;
   top: 0;
   left: 0;
 }
@@ -339,17 +358,17 @@
   text-anchor: start;
 }
 
 :-moz-native-anonymous .rulers-highlighter-vertical-labels > text {
   transform: rotate(-90deg);
   text-anchor: end;
 }
 
-/* Measuring Tool highlighter */
+/* Measuring Tool Highlighter */
 
 :-moz-native-anonymous .measuring-tool-highlighter-root {
   position: absolute;
   top: 0;
   left: 0;
   pointer-events: auto;
   cursor: crosshair;
 }
@@ -399,27 +418,17 @@
 :-moz-native-anonymous .measuring-tool-highlighter-guide-right,
 :-moz-native-anonymous .measuring-tool-highlighter-guide-bottom,
 :-moz-native-anonymous .measuring-tool-highlighter-guide-left {
   stroke: var(--highlighter-guide-color);
   stroke-dasharray: 5 3;
   shape-rendering: crispEdges;
 }
 
-/* CSS Grid highlighter */
-
-:-moz-native-anonymous .css-grid-canvas {
-  position: absolute;
-  pointer-events: none;
-  top: 0;
-  left: 0;
-  image-rendering: -moz-crisp-edges;
-}
-
-/* Eye dropper */
+/* Eye Dropper */
 
 :-moz-native-anonymous .eye-dropper-root {
   --magnifier-width: 96px;
   --magnifier-height: 96px;
   /* Width accounts for all color formats (hsl being the longest) */
   --label-width: 160px;
   --label-height: 23px;
   --color: #e0e0e0;
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -1,17 +1,21 @@
 /* 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 { extend } = require("sdk/core/heritage");
 const { AutoRefreshHighlighter } = require("./auto-refresh");
-const { CanvasFrameAnonymousContentHelper, createNode } = require("./utils/markup");
+const {
+  CanvasFrameAnonymousContentHelper,
+  createNode,
+  createSVGNode
+} = require("./utils/markup");
 const {
   getCurrentZoom,
   setIgnoreLayoutChanges
 } = require("devtools/shared/layout/utils");
 const Services = require("Services");
 
 const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
 const ROWS = "rows";
@@ -37,20 +41,37 @@ const GRID_LINES_PROPERTIES = {
  *
  * Usage example:
  * let h = new CssGridHighlighter(env);
  * h.show(node, options);
  * h.hide();
  * h.destroy();
  *
  * Available Options:
- * - showGridLineNumbers {Boolean}
- *   Displays the grid line numbers
- * - showInfiniteLines {Boolean}
- *   Displays an infinite line to represent the grid lines
+ * - showGridArea(areaName)
+ *     @param  {String} areaName
+ *     Shows the grid area highlight for the given area name.
+ * - showAllGridAreas
+ *     Shows all the grid area highlights for the current grid.
+ * - showGridLineNumbers(isShown)
+ *     @param  {Boolean}
+ *     Displays the grid line numbers on the grid lines if isShown is true.
+ * - showInfiniteLines(isShown)
+ *     @param  {Boolean} isShown
+ *     Displays an infinite line to represent the grid lines if isShown is true.
+ *
+ * Structure:
+ * <div class="highlighter-container">
+ *   <canvas id="css-grid-canvas" class="css-grid-canvas">
+ *   <svg class="css-grid-elements" hidden="true">
+ *     <g class="css-grid-regions">
+ *       <path class="css-grid-areas" points="..." />
+ *     </g>
+ *   </svg>
+ * </div>
  */
 function CssGridHighlighter(highlighterEnv) {
   AutoRefreshHighlighter.call(this, highlighterEnv);
 
   this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
     this._buildMarkup.bind(this));
 }
 
@@ -75,16 +96,48 @@ CssGridHighlighter.prototype = extend(Au
       attributes: {
         "id": "canvas",
         "class": "canvas",
         "hidden": "true"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
+    // Build the SVG element
+    let svg = createSVGNode(this.win, {
+      nodeType: "svg",
+      parent: container,
+      attributes: {
+        "id": "elements",
+        "width": "100%",
+        "height": "100%",
+        "hidden": "true"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    let regions = createSVGNode(this.win, {
+      nodeType: "g",
+      parent: svg,
+      attributes: {
+        "class": "regions"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "path",
+      parent: regions,
+      attributes: {
+        "class": "areas",
+        "id": "areas"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
     return container;
   },
 
   destroy() {
     AutoRefreshHighlighter.prototype.destroy.call(this);
     this.markup.destroy();
   },
 
@@ -105,16 +158,43 @@ CssGridHighlighter.prototype = extend(Au
       this.hide();
       return false;
     }
 
     return this._update();
   },
 
   /**
+   * Shows the grid area highlight for the given area name.
+   *
+   * @param  {String} areaName
+   *         Grid area name.
+   */
+  showGridArea(areaName) {
+    this.renderGridArea(areaName);
+    this._showGridArea();
+  },
+
+  /**
+   * Shows all the grid area highlights for the current grid.
+   */
+  showAllGridAreas() {
+    this.renderGridArea();
+    this._showGridArea();
+  },
+
+  /**
+   * Clear the grid area highlights.
+   */
+  clearGridAreas() {
+    let box = this.getElement("areas");
+    box.setAttribute("d", "");
+  },
+
+  /**
    * Checks if the current node has a CSS Grid layout.
    *
    * @return  {Boolean} true if the current node has a CSS grid layout, false otherwise.
    */
   isGrid() {
     return this.currentNode.getGridFragments().length > 0;
   },
 
@@ -132,31 +212,39 @@ CssGridHighlighter.prototype = extend(Au
     let newGridData = stringifyGridFragments(this.gridData);
 
     return hasMoved || oldGridData !== newGridData;
   },
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
-   * Should be called whenever node's geometry or grid changes
+   * Should be called whenever node's geometry or grid changes.
    */
   _update() {
     setIgnoreLayoutChanges(true);
 
-    // Clear the canvas.
+    // Clear the canvas the grid area highlights.
     this.clearCanvas();
+    this.clearGridAreas();
 
-    // And start drawing the fragments.
+    // Start drawing the grid fragments.
     for (let i = 0; i < this.gridData.length; i++) {
       let fragment = this.gridData[i];
       let quad = this.currentQuads.content[i];
       this.renderFragment(fragment, quad);
     }
 
+    // Display the grid area highlights if needed.
+    if (this.options.showAllGridAreas) {
+      this.showAllGridAreas();
+    } else if (this.options.showGridArea) {
+      this.showGridArea(this.options.showGridArea);
+    }
+
     this._showGrid();
 
     setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
     return true;
   },
 
   clearCanvas() {
     let ratio = parseFloat((this.win.devicePixelRatio || 1).toFixed(2));
@@ -327,29 +415,86 @@ CssGridHighlighter.prototype = extend(Au
     if (dimensionType === COLUMNS) {
       this.ctx.fillText(lineNumber, linePos, startPos);
     } else {
       let textWidth = this.ctx.measureText(lineNumber).width;
       this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
     }
   },
 
+  /**
+   * Render the grid area highlight for the given area name or for all the grid areas.
+   *
+   * @param  {String} areaName
+   *         Name of the grid area to be highlighted. If no area name is provided, all
+   *         the grid areas should be highlighted.
+   */
+  renderGridArea(areaName) {
+    let paths = [];
+    let currentZoom = getCurrentZoom(this.win);
+
+    for (let i = 0; i < this.gridData.length; i++) {
+      let fragment = this.gridData[i];
+      let {bounds} = this.currentQuads.content[i];
+
+      for (let area of fragment.areas) {
+        if (areaName && areaName != area.name) {
+          continue;
+        }
+
+        let rowStart = fragment.rows.lines[area.rowStart - 1];
+        let rowEnd = fragment.rows.lines[area.rowEnd - 1];
+        let columnStart = fragment.cols.lines[area.columnStart - 1];
+        let columnEnd = fragment.cols.lines[area.columnEnd - 1];
+
+        let x1 = columnStart.start + columnStart.breadth +
+          (bounds.left / currentZoom);
+        let x2 = columnEnd.start + (bounds.left / currentZoom);
+        let y1 = rowStart.start + rowStart.breadth +
+          (bounds.top / currentZoom);
+        let y2 = rowEnd.start + (bounds.top / currentZoom);
+
+        let path = "M" + x1 + "," + y1 + " " +
+                   "L" + x2 + "," + y1 + " " +
+                   "L" + x2 + "," + y2 + " " +
+                   "L" + x1 + "," + y2;
+        paths.push(path);
+      }
+    }
+
+    let box = this.getElement("areas");
+    box.setAttribute("d", paths.join(" "));
+  },
+
+  /**
+   * Hide the highlighter and the canvas.
+   */
   _hide() {
     setIgnoreLayoutChanges(true);
     this._hideGrid();
+    this._hideGridArea();
     setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   _hideGrid() {
     this.getElement("canvas").setAttribute("hidden", "true");
   },
 
   _showGrid() {
     this.getElement("canvas").removeAttribute("hidden");
-  }
+  },
+
+  _hideGridArea() {
+    this.getElement("elements").setAttribute("hidden", "true");
+  },
+
+  _showGridArea() {
+    this.getElement("elements").removeAttribute("hidden");
+  },
+
 });
 exports.CssGridHighlighter = CssGridHighlighter;
 
 /**
  * Stringify CSS Grid data as returned by node.getGridFragments.
  * This is useful to compare grid state at each update and redraw the highlighter if
  * needed.
  *