Bug 1378851 - Use ES classes in devtools highlighters rather than SDK heritage module; r=sole draft
authorPatrick Brosset <pbrosset@mozilla.com>
Fri, 21 Jul 2017 13:00:04 +0200
changeset 613011 084303bee8c0fad7385fb5767020b11797090c90
parent 613010 c8e3a3ee598db6163f423adfd39c2a523dedab54
child 638576 32c7e0faa6f5e96e5c3a4119fb12251688dd89f1
push id69686
push userbmo:pbrosset@mozilla.com
push dateFri, 21 Jul 2017 11:00:51 +0000
reviewerssole
bugs1378851
milestone56.0a1
Bug 1378851 - Use ES classes in devtools highlighters rather than SDK heritage module; r=sole MozReview-Commit-ID: 7ahFmbrMd8a
devtools/server/actors/highlighters/box-model.js
devtools/server/actors/highlighters/css-grid.js
devtools/server/actors/highlighters/css-transform.js
devtools/server/actors/highlighters/geometry-editor.js
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -1,15 +1,14 @@
  /* 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,
   createSVGNode,
   getBindingElementAndPseudo,
   hasPseudoClassLock,
   isNodeValid,
@@ -87,43 +86,41 @@ const PSEUDO_CLASSES = [":hover", ":acti
  *           <span class="box-model-infobar-pseudo-classes">:hover</span>
  *         </div>
  *       </div>
  *       <div class="box-model-infobar-arrow box-model-infobar-arrow-bottom"/>
  *     </div>
  *   </div>
  * </div>
  */
-function BoxModelHighlighter(highlighterEnv) {
-  AutoRefreshHighlighter.call(this, highlighterEnv);
-
-  this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
-    this._buildMarkup.bind(this));
+class BoxModelHighlighter extends AutoRefreshHighlighter {
+  constructor(highlighterEnv) {
+    super(highlighterEnv);
 
-  /**
-   * Optionally customize each region's fill color by adding an entry to the
-   * regionFill property: `highlighter.regionFill.margin = "red";
-   */
-  this.regionFill = {};
+    this.ID_CLASS_PREFIX = "box-model-";
+
+    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
+      this._buildMarkup.bind(this));
 
-  this.onPageHide = this.onPageHide.bind(this);
-  this.onWillNavigate = this.onWillNavigate.bind(this);
-
-  this.highlighterEnv.on("will-navigate", this.onWillNavigate);
+    /**
+     * Optionally customize each region's fill color by adding an entry to the
+     * regionFill property: `highlighter.regionFill.margin = "red";
+     */
+    this.regionFill = {};
 
-  let { pageListenerTarget } = highlighterEnv;
-  pageListenerTarget.addEventListener("pagehide", this.onPageHide);
-}
+    this.onPageHide = this.onPageHide.bind(this);
+    this.onWillNavigate = this.onWillNavigate.bind(this);
+
+    this.highlighterEnv.on("will-navigate", this.onWillNavigate);
 
-BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
-  typeName: "BoxModelHighlighter",
+    let { pageListenerTarget } = highlighterEnv;
+    pageListenerTarget.addEventListener("pagehide", this.onPageHide);
+  }
 
-  ID_CLASS_PREFIX: "box-model-",
-
-  _buildMarkup: function () {
+  _buildMarkup() {
     let doc = this.win.document;
 
     let highlighterContainer = doc.createElement("div");
     highlighterContainer.className = "highlighter-container box-model";
 
     // Build the root wrapper, used to adapt to the page zoom.
     let rootWrapper = createNode(this.win, {
       parent: highlighterContainer,
@@ -252,86 +249,86 @@ BoxModelHighlighter.prototype = extend(A
       attributes: {
         "class": "infobar-dimensions",
         "id": "infobar-dimensions"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     return highlighterContainer;
-  },
+  }
 
   /**
    * Destroy the nodes. Remove listeners.
    */
-  destroy: function () {
+  destroy() {
     this.highlighterEnv.off("will-navigate", this.onWillNavigate);
 
     let { pageListenerTarget } = this.highlighterEnv;
     if (pageListenerTarget) {
       pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
     }
 
     this.markup.destroy();
     AutoRefreshHighlighter.prototype.destroy.call(this);
-  },
+  }
 
-  getElement: function (id) {
+  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
    * @return {Boolean}
    */
-  _isNodeValid: function (node) {
+  _isNodeValid(node) {
     return node && (isNodeValid(node) || isNodeValid(node, nodeConstants.TEXT_NODE));
-  },
+  }
 
   /**
    * Show the highlighter on a given node
    */
-  _show: function () {
+  _show() {
     if (BOX_MODEL_REGIONS.indexOf(this.options.region) == -1) {
       this.options.region = "content";
     }
 
     let shown = this._update();
     this._trackMutations();
     this.emit("ready");
     return shown;
-  },
+  }
 
   /**
    * Track the current node markup mutations so that the node info bar can be
    * updated to reflects the node's attributes
    */
-  _trackMutations: function () {
+  _trackMutations() {
     if (isNodeValid(this.currentNode)) {
       let win = this.currentNode.ownerGlobal;
       this.currentNodeObserver = new win.MutationObserver(this.update);
       this.currentNodeObserver.observe(this.currentNode, {attributes: true});
     }
-  },
+  }
 
-  _untrackMutations: function () {
+  _untrackMutations() {
     if (isNodeValid(this.currentNode) && this.currentNodeObserver) {
       this.currentNodeObserver.disconnect();
       this.currentNodeObserver = null;
     }
-  },
+  }
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
    * Should be called whenever node size or attributes change
    */
-  _update: function () {
+  _update() {
     let shown = false;
     setIgnoreLayoutChanges(true);
 
     if (this._updateBoxModel()) {
       // Show the infobar only if configured to do so and the node is an element or a text
       // node.
       if (!this.options.hideInfoBar && (
           this.currentNode.nodeType === this.currentNode.ELEMENT_NODE ||
@@ -345,75 +342,75 @@ BoxModelHighlighter.prototype = extend(A
     } else {
       // Nothing to highlight (0px rectangle like a <script> tag for instance)
       this._hide();
     }
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
 
     return shown;
-  },
+  }
 
-  _scrollUpdate: function () {
+  _scrollUpdate() {
     this._moveInfobar();
-  },
+  }
 
   /**
    * Hide the highlighter, the outline and the infobar.
    */
-  _hide: function () {
+  _hide() {
     setIgnoreLayoutChanges(true);
 
     this._untrackMutations();
     this._hideBoxModel();
     this._hideInfobar();
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
-  },
+  }
 
   /**
    * Hide the infobar
    */
-  _hideInfobar: function () {
+  _hideInfobar() {
     this.getElement("infobar-container").setAttribute("hidden", "true");
-  },
+  }
 
   /**
    * Show the infobar
    */
-  _showInfobar: function () {
+  _showInfobar() {
     this.getElement("infobar-container").removeAttribute("hidden");
     this._updateInfobar();
-  },
+  }
 
   /**
    * Hide the box model
    */
-  _hideBoxModel: function () {
+  _hideBoxModel() {
     this.getElement("elements").setAttribute("hidden", "true");
-  },
+  }
 
   /**
    * Show the box model
    */
-  _showBoxModel: function () {
+  _showBoxModel() {
     this.getElement("elements").removeAttribute("hidden");
-  },
+  }
 
   /**
    * Calculate an outer quad based on the quads returned by getAdjustedQuads.
    * The BoxModelHighlighter may highlight more than one boxes, so in this case
    * create a new quad that "contains" all of these quads.
    * This is useful to position the guides and infobar.
    * This may happen if the BoxModelHighlighter is used to highlight an inline
    * element that spans line breaks.
    * @param {String} region The box-model region to get the outer quad for.
    * @return {Object} A quad-like object {p1,p2,p3,p4,bounds}
    */
-  _getOuterQuad: function (region) {
+  _getOuterQuad(region) {
     let quads = this.currentQuads[region];
     if (!quads.length) {
       return null;
     }
 
     let quad = {
       p1: {x: Infinity, y: Infinity},
       p2: {x: -Infinity, y: Infinity},
@@ -447,25 +444,25 @@ BoxModelHighlighter.prototype = extend(A
       quad.bounds.right = Math.max(quad.bounds.right, q.bounds.right);
     }
     quad.bounds.x = quad.bounds.left;
     quad.bounds.y = quad.bounds.top;
     quad.bounds.width = quad.bounds.right - quad.bounds.left;
     quad.bounds.height = quad.bounds.bottom - quad.bounds.top;
 
     return quad;
-  },
+  }
 
   /**
    * Update the box model as per the current node.
    *
    * @return {boolean}
    *         True if the current node has a box model to be highlighted
    */
-  _updateBoxModel: function () {
+  _updateBoxModel() {
     let options = this.options;
     options.region = options.region || "content";
 
     if (!this._nodeNeedsHighlighting()) {
       this._hideBoxModel();
       return false;
     }
 
@@ -511,19 +508,19 @@ BoxModelHighlighter.prototype = extend(A
       }
     }
 
     // Un-zoom the root wrapper if the page was zoomed.
     let rootId = this.ID_CLASS_PREFIX + "elements";
     this.markup.scaleRootElement(this.currentNode, rootId);
 
     return true;
-  },
+  }
 
-  _getBoxPathCoordinates: function (boxQuad, nextBoxQuad) {
+  _getBoxPathCoordinates(boxQuad, nextBoxQuad) {
     let {p1, p2, p3, p4} = boxQuad;
 
     let path;
     if (!nextBoxQuad || !this.options.onlyRegionArea) {
       // If this is the content box (inner-most box) or if we're not being asked
       // to highlight only region areas, then draw a simple rectangle.
       path = "M" + p1.x + "," + p1.y + " " +
              "L" + p2.x + "," + p2.y + " " +
@@ -540,30 +537,30 @@ BoxModelHighlighter.prototype = extend(A
              "L" + np1.x + "," + np1.y + " " +
              "L" + np4.x + "," + np4.y + " " +
              "L" + np3.x + "," + np3.y + " " +
              "L" + np2.x + "," + np2.y + " " +
              "L" + np1.x + "," + np1.y;
     }
 
     return path;
-  },
+  }
 
   /**
    * Can the current node be highlighted? Does it have quads.
    * @return {Boolean}
    */
-  _nodeNeedsHighlighting: function () {
+  _nodeNeedsHighlighting() {
     return this.currentQuads.margin.length ||
            this.currentQuads.border.length ||
            this.currentQuads.padding.length ||
            this.currentQuads.content.length;
-  },
+  }
 
-  _getOuterBounds: function () {
+  _getOuterBounds() {
     for (let region of ["margin", "border", "padding", "content"]) {
       let quad = this._getOuterQuad(region);
 
       if (!quad) {
         // Invisible element such as a script tag.
         break;
       }
 
@@ -579,24 +576,24 @@ BoxModelHighlighter.prototype = extend(A
       height: 0,
       left: 0,
       right: 0,
       top: 0,
       width: 0,
       x: 0,
       y: 0
     };
-  },
+  }
 
   /**
    * We only want to show guides for horizontal and vertical edges as this helps
    * to line them up. This method finds these edges and displays a guide there.
    * @param {String} region The region around which the guides should be shown.
    */
-  _showGuides: function (region) {
+  _showGuides(region) {
     let {p1, p2, p3, p4} = this._getOuterQuad(region);
 
     let allX = [p1.x, p2.x, p3.x, p4.x].sort((a, b) => a - b);
     let allY = [p1.y, p2.y, p3.y, p4.y].sort((a, b) => a - b);
     let toShowX = [];
     let toShowY = [];
 
     for (let arr of [allX, allY]) {
@@ -614,34 +611,34 @@ BoxModelHighlighter.prototype = extend(A
       }
     }
 
     // Move guide into place or hide it if no valid co-ordinate was found.
     this._updateGuide("top", Math.round(toShowY[0]));
     this._updateGuide("right", Math.round(toShowX[1]) - 1);
     this._updateGuide("bottom", Math.round(toShowY[1] - 1));
     this._updateGuide("left", Math.round(toShowX[0]));
-  },
+  }
 
-  _hideGuides: function () {
+  _hideGuides() {
     for (let side of BOX_MODEL_SIDES) {
       this.getElement("guide-" + side).setAttribute("hidden", "true");
     }
-  },
+  }
 
   /**
    * Move a guide to the appropriate position and display it. If no point is
    * passed then the guide is hidden.
    *
    * @param  {String} side
    *         The guide to update
    * @param  {Integer} point
    *         x or y co-ordinate. If this is undefined we hide the guide.
    */
-  _updateGuide: function (side, point = -1) {
+  _updateGuide(side, point = -1) {
     let guide = this.getElement("guide-" + side);
 
     if (point <= 0) {
       guide.setAttribute("hidden", "true");
       return false;
     }
 
     if (side === "top" || side === "bottom") {
@@ -654,22 +651,22 @@ BoxModelHighlighter.prototype = extend(A
       guide.setAttribute("y1", "0");
       guide.setAttribute("x2", point + "");
       guide.setAttribute("y2", "100%");
     }
 
     guide.removeAttribute("hidden");
 
     return true;
-  },
+  }
 
   /**
    * Update node information (displayName#id.class)
    */
-  _updateInfobar: function () {
+  _updateInfobar() {
     if (!this.currentNode) {
       return;
     }
 
     let {bindingElement: node, pseudo} =
         getBindingElementAndPseudo(this.currentNode);
 
     // Update the tag, id, classes, pseudo-classes and dimensions
@@ -698,44 +695,45 @@ BoxModelHighlighter.prototype = extend(A
 
     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._moveInfobar();
-  },
+  }
 
-  _getPseudoClasses: function (node) {
+  _getPseudoClasses(node) {
     if (node.nodeType !== nodeConstants.ELEMENT_NODE) {
       // hasPseudoClassLock can only be used on Elements.
       return [];
     }
 
     return PSEUDO_CLASSES.filter(pseudo => hasPseudoClassLock(node, pseudo));
-  },
+  }
 
   /**
    * Move the Infobar to the right place in the highlighter.
    */
-  _moveInfobar: function () {
+  _moveInfobar() {
     let bounds = this._getOuterBounds();
     let container = this.getElement("infobar-container");
 
     moveInfobar(container, bounds, this.win);
-  },
+  }
 
-  onPageHide: function ({ target }) {
+  onPageHide({ target }) {
     // If a pagehide event is triggered for current window's highlighter, hide the
     // highlighter.
     if (target.defaultView === this.win) {
       this.hide();
     }
-  },
+  }
 
-  onWillNavigate: function ({ isTopLevel }) {
+  onWillNavigate({ isTopLevel }) {
     if (isTopLevel) {
       this.hide();
     }
   }
-});
+}
+
 exports.BoxModelHighlighter = BoxModelHighlighter;
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -1,16 +1,15 @@
 /* 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 Services = require("Services");
-const { extend } = require("sdk/core/heritage");
 const { AutoRefreshHighlighter } = require("./auto-refresh");
 const {
   CanvasFrameAnonymousContentHelper,
   createNode,
   createSVGNode,
   moveInfobar,
 } = require("./utils/markup");
 const {
@@ -339,47 +338,45 @@ function drawRoundedRect(ctx, x, y, widt
  *       <div class="css-grid-infobar-text">
  *         <span class="css-grid-line-infobar-number">Grid Line Number</span>
  *         <span class="css-grid-line-infobar-names">Grid Line Names></span>
  *       </div>
  *     </div>
  *   </div>
  * </div>
  */
-function CssGridHighlighter(highlighterEnv) {
-  AutoRefreshHighlighter.call(this, highlighterEnv);
+class CssGridHighlighter extends AutoRefreshHighlighter {
+  constructor(highlighterEnv) {
+    super(highlighterEnv);
 
-  this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
-    this._buildMarkup.bind(this));
+    this.ID_CLASS_PREFIX = "css-grid-";
 
-  this.onNavigate = this.onNavigate.bind(this);
-  this.onPageHide = this.onPageHide.bind(this);
-  this.onWillNavigate = this.onWillNavigate.bind(this);
+    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
+      this._buildMarkup.bind(this));
 
-  this.highlighterEnv.on("navigate", this.onNavigate);
-  this.highlighterEnv.on("will-navigate", this.onWillNavigate);
-
-  let { pageListenerTarget } = highlighterEnv;
-  pageListenerTarget.addEventListener("pagehide", this.onPageHide);
+    this.onNavigate = this.onNavigate.bind(this);
+    this.onPageHide = this.onPageHide.bind(this);
+    this.onWillNavigate = this.onWillNavigate.bind(this);
 
-  // Initialize the <canvas> position to the top left corner of the page
-  this._canvasPosition = {
-    x: 0,
-    y: 0
-  };
+    this.highlighterEnv.on("navigate", this.onNavigate);
+    this.highlighterEnv.on("will-navigate", this.onWillNavigate);
+
+    let { pageListenerTarget } = highlighterEnv;
+    pageListenerTarget.addEventListener("pagehide", this.onPageHide);
 
-  // Calling `calculateCanvasPosition` anyway since the highlighter could be initialized
-  // on a page that has scrolled already.
-  this.calculateCanvasPosition();
-}
+    // Initialize the <canvas> position to the top left corner of the page
+    this._canvasPosition = {
+      x: 0,
+      y: 0
+    };
 
-CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
-  typeName: "CssGridHighlighter",
-
-  ID_CLASS_PREFIX: "css-grid-",
+    // Calling `calculateCanvasPosition` anyway since the highlighter could be initialized
+    // on a page that has scrolled already.
+    this.calculateCanvasPosition();
+  }
 
   _buildMarkup() {
     let container = createNode(this.win, {
       attributes: {
         "class": "highlighter-container"
       }
     });
 
@@ -584,50 +581,50 @@ CssGridHighlighter.prototype = extend(Au
       attributes: {
         "class": "line-infobar-names",
         "id": "line-infobar-names"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     return container;
-  },
+  }
 
   destroy() {
     let { highlighterEnv } = this;
     highlighterEnv.off("navigate", this.onNavigate);
     highlighterEnv.off("will-navigate", this.onWillNavigate);
 
     let { pageListenerTarget } = highlighterEnv;
     if (pageListenerTarget) {
       pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
     }
 
     this.markup.destroy();
 
     // Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
     this._clearCache();
     AutoRefreshHighlighter.prototype.destroy.call(this);
-  },
+  }
 
   getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
-  },
+  }
 
   get ctx() {
     return this.canvas.getCanvasContext("2d");
-  },
+  }
 
   get canvas() {
     return this.getElement("canvas");
-  },
+  }
 
   get color() {
     return this.options.color || DEFAULT_GRID_COLOR;
-  },
+  }
 
   /**
    * Gets the grid gap pattern used to render the gap regions based on the device
    * pixel ratio given.
    *
    * @param {Number} devicePixelRatio
    *         The device pixel ratio we want the pattern for.
    * @param  {Object} dimension
@@ -673,156 +670,156 @@ CssGridHighlighter.prototype = extend(Au
     ctx.restore();
 
     let pattern = ctx.createPattern(canvas, "repeat");
 
     gridPatternMap.set(dimension, pattern);
     gCachedGridPattern.set(devicePixelRatio, gridPatternMap);
 
     return pattern;
-  },
+  }
 
   /**
    * Called when the page navigates. Used to clear the cached gap patterns and avoid
    * using DeadWrapper objects as gap patterns the next time.
    */
   onNavigate() {
     this._clearCache();
-  },
+  }
 
-  onPageHide: function ({ target }) {
+  onPageHide({ target }) {
     // If a page hide event is triggered for current window's highlighter, hide the
     // highlighter.
     if (target.defaultView === this.win) {
       this.hide();
     }
-  },
+  }
 
   onWillNavigate({ isTopLevel }) {
     if (isTopLevel) {
       this.hide();
     }
-  },
+  }
 
   _show() {
     if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
       this.hide();
       return false;
     }
 
     // The grid pattern cache should be cleared in case the color changed.
     this._clearCache();
 
     // Hide the canvas, grid element highlights and infobar.
     this._hide();
 
     return this._update();
-  },
+  }
 
   _clearCache() {
     gCachedGridPattern.clear();
-  },
+  }
 
   /**
    * Shows the grid area highlight for the given area name.
    *
    * @param  {String} areaName
    *         Grid area name.
    */
   showGridArea(areaName) {
     this.renderGridArea(areaName);
-  },
+  }
 
   /**
    * Shows all the grid area highlights for the current grid.
    */
   showAllGridAreas() {
     this.renderGridArea();
-  },
+  }
 
   /**
    * Clear the grid area highlights.
    */
   clearGridAreas() {
     let areas = this.getElement("areas");
     areas.setAttribute("d", "");
-  },
+  }
 
   /**
    * Shows the grid cell highlight for the given grid cell options.
    *
    * @param  {Number} options.gridFragmentIndex
    *         Index of the grid fragment to render the grid cell highlight.
    * @param  {Number} options.rowNumber
    *         Row number of the grid cell to highlight.
    * @param  {Number} options.columnNumber
    *         Column number of the grid cell to highlight.
    */
   showGridCell({ gridFragmentIndex, rowNumber, columnNumber }) {
     this.renderGridCell(gridFragmentIndex, rowNumber, columnNumber);
-  },
+  }
 
   /**
    * Shows the grid line highlight for the given grid line options.
    *
    * @param  {Number} options.gridFragmentIndex
    *         Index of the grid fragment to render the grid line highlight.
    * @param  {Number} options.lineNumber
    *         Line number of the grid line to highlight.
    * @param  {String} options.type
    *         The dimension type of the grid line.
    */
   showGridLineNames({ gridFragmentIndex, lineNumber, type }) {
     this.renderGridLineNames(gridFragmentIndex, lineNumber, type);
-  },
+  }
 
   /**
    * Clear the grid cell highlights.
    */
   clearGridCell() {
     let cells = this.getElement("cells");
     cells.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;
-  },
+  }
 
   /**
    * Is a given grid fragment valid? i.e. does it actually have tracks? In some cases, we
    * may have a fragment that defines column tracks but doesn't have any rows (or vice
    * versa). In which case we do not want to draw anything for that fragment.
    *
    * @param {Object} fragment
    * @return {Boolean}
    */
   isValidFragment(fragment) {
     return fragment.cols.tracks.length && fragment.rows.tracks.length;
-  },
+  }
 
   /**
    * The AutoRefreshHighlighter's _hasMoved method returns true only if the
    * element's quads have changed. Override it so it also returns true if the
    * element's grid has changed (which can happen when you change the
    * grid-template-* CSS properties with the highlighter displayed).
    */
   _hasMoved() {
     let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);
 
     let oldGridData = stringifyGridFragments(this.gridData);
     this.gridData = this.currentNode.getGridFragments();
     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.
    */
   _update() {
     setIgnoreLayoutChanges(true);
@@ -878,17 +875,17 @@ CssGridHighlighter.prototype = extend(Au
     this._showGrid();
     this._showGridElements();
 
     root.setAttribute("style",
       `position:absolute; width:${width}px;height:${height}px; overflow:hidden`);
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
     return true;
-  },
+  }
 
   /**
    * Update the grid information displayed in the grid area info bar.
    *
    * @param  {GridArea} area
    *         The grid area object.
    * @param  {Object} bounds
    *          A DOMRect-like object represent the grid area rectangle.
@@ -902,17 +899,17 @@ CssGridHighlighter.prototype = extend(Au
     this.getElement("area-infobar-name").setTextContent(area.name);
     this.getElement("area-infobar-dimensions").setTextContent(dim);
 
     let container = this.getElement("area-infobar-container");
     moveInfobar(container, bounds, this.win, {
       position: "bottom",
       hideIfOffscreen: true
     });
-  },
+  }
 
   /**
    * Update the grid information displayed in the grid cell info bar.
    *
    * @param  {Number} rowNumber
    *         The grid cell's row number.
    * @param  {Number} columnNumber
    *         The grid cell's column number.
@@ -930,17 +927,17 @@ CssGridHighlighter.prototype = extend(Au
     this.getElement("cell-infobar-position").setTextContent(position);
     this.getElement("cell-infobar-dimensions").setTextContent(dim);
 
     let container = this.getElement("cell-infobar-container");
     moveInfobar(container, bounds, this.win, {
       position: "top",
       hideIfOffscreen: true
     });
-  },
+  }
 
   /**
    * Update the grid information displayed in the grid line info bar.
    *
    * @param  {String} gridLineNames
    *         Comma-separated string of names for the grid line.
    * @param  {Number} gridLineNumber
    *         The grid line number.
@@ -951,29 +948,29 @@ CssGridHighlighter.prototype = extend(Au
    */
   _updateGridLineInfobar(gridLineNames, gridLineNumber, x, y) {
     this.getElement("line-infobar-number").setTextContent(gridLineNumber);
     this.getElement("line-infobar-names").setTextContent(gridLineNames);
 
     let container = this.getElement("line-infobar-container");
     moveInfobar(container,
       getBoundsFromPoints([{x, y}, {x, y}, {x, y}, {x, y}]), this.win);
-  },
+  }
 
   /**
    * The <canvas>'s position needs to be updated if the page scrolls too much, in order
    * to give the illusion that it always covers the viewport.
    */
   _scrollUpdate() {
     let hasPositionChanged = this.calculateCanvasPosition();
 
     if (hasPositionChanged) {
       this._update();
     }
-  },
+  }
 
   /**
    * This method is responsible to do the math that updates the <canvas>'s position,
    * in accordance with the page's scroll, document's size, canvas size, and
    * viewport's size.
    * It's called when a page's scroll is detected.
    *
    * @return {Boolean} `true` if the <canvas> position was updated, `false` otherwise.
@@ -1023,34 +1020,34 @@ CssGridHighlighter.prototype = extend(Au
       this._canvasPosition.x = Math.min(leftThreshold, rightBoundary);
       hasUpdated = true;
     } else if (x > leftBoundary && x > leftThreshold) {
       this._canvasPosition.x = Math.max(rightThreshold, leftBoundary);
       hasUpdated = true;
     }
 
     return hasUpdated;
-  },
+  }
 
   /**
    * Updates the <canvas> element's style in accordance with the current window's
    * devicePixelRatio, and the position calculated in `calculateCanvasPosition`; it also
    * clears the drawing context.
    */
   updateCanvasElement() {
     let size = CANVAS_SIZE / this.win.devicePixelRatio;
     let { x, y } = this._canvasPosition;
 
     // Resize the canvas taking the dpr into account so as to have crisp lines, and
     // translating it to give the perception that it always covers the viewport.
     this.canvas.setAttribute("style",
       `width:${size}px;height:${size}px; transform: translate(${x}px, ${y}px);`);
 
     this.ctx.clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
-  },
+  }
 
   /**
    * Updates the current matrices for both canvas drawing and SVG, taking in account the
    * following transformations, in this order:
    *   1. The scale given by the display pixel ratio.
    *   2. The translation to the top left corner of the element.
    *   3. The scale given by the current zoom.
    *   4. The translation given by the top and left padding of the element.
@@ -1084,33 +1081,33 @@ CssGridHighlighter.prototype = extend(Au
       m = multiply(m, nodeMatrix);
       this.hasNodeTransformations = true;
     }
 
     // Finally, we translate the origin based on the node's padding and border values.
     m = multiply(m, translate(paddingLeft + borderLeft, paddingTop + borderTop));
 
     this.currentMatrix = m;
-  },
+  }
 
   getFirstRowLinePos(fragment) {
     return fragment.rows.lines[0].start;
-  },
+  }
 
   getLastRowLinePos(fragment) {
     return fragment.rows.lines[fragment.rows.lines.length - 1].start;
-  },
+  }
 
   getFirstColLinePos(fragment) {
     return fragment.cols.lines[0].start;
-  },
+  }
 
   getLastColLinePos(fragment) {
     return fragment.cols.lines[fragment.cols.lines.length - 1].start;
-  },
+  }
 
   /**
    * Get the GridLine index of the last edge of the explicit grid for a grid dimension.
    *
    * @param  {GridTracks} tracks
    *         The grid track of a given grid dimension.
    * @return {Number} index of the last edge of the explicit grid for a grid dimension.
    */
@@ -1119,17 +1116,17 @@ CssGridHighlighter.prototype = extend(Au
 
     // Traverse the grid track backwards until we find an explicit track.
     while (trackIndex >= 0 && tracks[trackIndex].type != "explicit") {
       trackIndex--;
     }
 
     // The grid line index is the grid track index + 1.
     return trackIndex + 1;
-  },
+  }
 
   renderFragment(fragment) {
     if (!this.isValidFragment(fragment)) {
       return;
     }
 
     this.renderLines(fragment.cols, COLUMNS, "left", "top", "height",
                      this.getFirstRowLinePos(fragment),
@@ -1144,17 +1141,17 @@ CssGridHighlighter.prototype = extend(Au
 
     // Line numbers are rendered in a 2nd step to avoid overlapping with existing lines.
     if (this.options.showGridLineNumbers) {
       this.renderLineNumbers(fragment.cols, COLUMNS, "left", "top",
                        this.getFirstRowLinePos(fragment));
       this.renderLineNumbers(fragment.rows, ROWS, "top", "left",
                        this.getFirstColLinePos(fragment));
     }
-  },
+  }
 
   /**
    * Renders the grid area overlay on the css grid highlighter canvas.
    */
   renderGridAreaOverlay() {
     let padding = 1;
 
     for (let i = 0; i < this.gridData.length; i++) {
@@ -1191,17 +1188,17 @@ CssGridHighlighter.prototype = extend(Au
                         areaColStartLinePos, areaColEnd.start,
                         ROWS, "areaEdge");
 
         this.renderGridAreaName(fragment, area);
       }
     }
 
     this.ctx.restore();
-  },
+  }
 
   /**
    * Render grid area name on the containing grid area cell.
    *
    * @param  {Object} fragment
    *         The grid fragment of the grid container.
    * @param  {Object} area
    *         The area overlay to render on the CSS highlighter canvas.
@@ -1265,17 +1262,17 @@ CssGridHighlighter.prototype = extend(Au
         drawRoundedRect(this.ctx, rectXPos, rectYPos, boxWidth, boxHeight, radius);
 
         this.ctx.fillStyle = this.color;
         this.ctx.fillText(area.name, x, y + padding);
       }
     }
 
     this.ctx.restore();
-  },
+  }
 
   /**
    * Render the grid lines given the grid dimension information of the
    * column or row lines.
    *
    * @param  {GridDimension} gridDimension
    *         Column or row grid dimension object.
    * @param  {Object} quad.bounds
@@ -1317,17 +1314,17 @@ CssGridHighlighter.prototype = extend(Au
       // Render a second line to illustrate the gutter for non-zero breadth.
       if (line.breadth > 0) {
         this.renderGridGap(linePos, lineStartPos, lineEndPos, line.breadth,
                            dimensionType);
         this.renderLine(linePos + line.breadth, lineStartPos, lineEndPos, dimensionType,
                         gridDimension.tracks[i].type);
       }
     }
-  },
+  }
 
   /**
    * Render the grid lines given the grid dimension information of the
    * column or row lines.
    *
    * see @param for renderLines.
    */
   renderLineNumbers(gridDimension, dimensionType, mainSide, crossSide,
@@ -1348,17 +1345,17 @@ CssGridHighlighter.prototype = extend(Au
       // For such lines the API returns always 0 as line's number.
       if (line.number === 0) {
         continue;
       }
 
       this.renderGridLineNumber(line.number, linePos, lineStartPos, line.breadth,
         dimensionType);
     }
-  },
+  }
 
   /**
    * Render the grid line on the css grid highlighter canvas.
    *
    * @param  {Number} linePos
    *         The line position along the x-axis for a column grid line and
    *         y-axis for a row grid line.
    * @param  {Number} startPos
@@ -1407,17 +1404,17 @@ CssGridHighlighter.prototype = extend(Au
     if (GRID_LINES_PROPERTIES[lineType].lineWidth) {
       this.ctx.lineWidth = GRID_LINES_PROPERTIES[lineType].lineWidth * devicePixelRatio;
     } else {
       this.ctx.lineWidth = lineWidth;
     }
 
     this.ctx.stroke();
     this.ctx.restore();
-  },
+  }
 
   /**
    * Render the grid line number on the css grid highlighter canvas.
    *
    * @param  {Number} lineNumber
    *         The grid line number.
    * @param  {Number} linePos
    *         The line position along the x-axis for a column grid line and
@@ -1492,17 +1489,17 @@ CssGridHighlighter.prototype = extend(Au
     let radius = 2 * displayPixelRatio;
     drawRoundedRect(this.ctx, x, y, boxWidth, boxHeight, radius);
 
     // Write the line number inside of the rectangle.
     this.ctx.fillStyle = "black";
     this.ctx.fillText(lineNumber, x + padding, y + textHeight + padding);
 
     this.ctx.restore();
-  },
+  }
 
   /**
    * Render the grid gap area on the css grid highlighter canvas.
    *
    * @param  {Number} linePos
    *         The line position along the x-axis for a column grid line and
    *         y-axis for a row grid line.
    * @param  {Number} startPos
@@ -1546,17 +1543,17 @@ CssGridHighlighter.prototype = extend(Au
         endPos = this._winDimensions.width;
         startPos = -endPos;
       }
       drawRect(this.ctx, startPos, linePos, endPos, linePos + breadth,
         this.currentMatrix);
     }
     this.ctx.fill();
     this.ctx.restore();
-  },
+  }
 
   /**
    * 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.
    */
@@ -1606,17 +1603,17 @@ CssGridHighlighter.prototype = extend(Au
           this._showGridAreaInfoBar();
           this._updateGridAreaInfobar(area, bounds);
         }
       }
     }
 
     let areas = this.getElement("areas");
     areas.setAttribute("d", paths.join(" "));
-  },
+  }
 
   /**
    * Render the grid cell highlight for the given grid fragment index, row and column
    * number.
    *
    * @param  {Number} gridFragmentIndex
    *         Index of the grid fragment to render the grid cell highlight.
    * @param  {Number} rowNumber
@@ -1661,17 +1658,17 @@ CssGridHighlighter.prototype = extend(Au
       y: Math.round(point.y / displayPixelRatio)
     })));
 
     let cells = this.getElement("cells");
     cells.setAttribute("d", getPathDescriptionFromPoints(svgPoints));
 
     this._showGridCellInfoBar();
     this._updateGridCellInfobar(rowNumber, columnNumber, bounds);
-  },
+  }
 
   /**
    * Render the grid line name highlight for the given grid fragment index, lineNumber,
    * and dimensionType.
    *
    * @param  {Number} gridFragmentIndex
    *         Index of the grid fragment to render the grid line highlight.
    * @param  {Number} lineNumber
@@ -1710,66 +1707,65 @@ CssGridHighlighter.prototype = extend(Au
       : colXPosition.start + (bounds.left / currentZoom);
 
     let y = dimensionType === ROWS
       ? linePos.start + (bounds.top / currentZoom)
       : rowYPosition.start + (bounds.top / currentZoom);
 
     this._showGridLineInfoBar();
     this._updateGridLineInfobar(names.join(", "), lineNumber, x, y);
-  },
+  }
 
   /**
    * Hide the highlighter, the canvas and the infobars.
    */
   _hide() {
     setIgnoreLayoutChanges(true);
     this._hideGrid();
     this._hideGridElements();
     this._hideGridAreaInfoBar();
     this._hideGridCellInfoBar();
     this._hideGridLineInfoBar();
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
-  },
+  }
 
   _hideGrid() {
     this.getElement("canvas").setAttribute("hidden", "true");
-  },
+  }
 
   _showGrid() {
     this.getElement("canvas").removeAttribute("hidden");
-  },
+  }
 
   _hideGridElements() {
     this.getElement("elements").setAttribute("hidden", "true");
-  },
+  }
 
   _showGridElements() {
     this.getElement("elements").removeAttribute("hidden");
-  },
+  }
 
   _hideGridAreaInfoBar() {
     this.getElement("area-infobar-container").setAttribute("hidden", "true");
-  },
+  }
 
   _showGridAreaInfoBar() {
     this.getElement("area-infobar-container").removeAttribute("hidden");
-  },
+  }
 
   _hideGridCellInfoBar() {
     this.getElement("cell-infobar-container").setAttribute("hidden", "true");
-  },
+  }
 
   _showGridCellInfoBar() {
     this.getElement("cell-infobar-container").removeAttribute("hidden");
-  },
+  }
 
   _hideGridLineInfoBar() {
     this.getElement("line-infobar-container").setAttribute("hidden", "true");
-  },
+  }
 
   _showGridLineInfoBar() {
     this.getElement("line-infobar-container").removeAttribute("hidden");
-  },
-
-});
+  }
+}
 
 exports.CssGridHighlighter = CssGridHighlighter;
--- a/devtools/server/actors/highlighters/css-transform.js
+++ b/devtools/server/actors/highlighters/css-transform.js
@@ -1,15 +1,14 @@
 /* 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, getComputedStyle,
   createSVGNode, createNode } = require("./utils/markup");
 const { setIgnoreLayoutChanges,
   getNodeBounds } = require("devtools/shared/layout/utils");
 
 // The minimum distance a line should be before it has an arrow marker-end
@@ -17,29 +16,27 @@ const ARROW_LINE_MIN_DISTANCE = 10;
 
 var MARKER_COUNTER = 1;
 
 /**
  * The CssTransformHighlighter is the class that draws an outline around a
  * transformed element and an outline around where it would be if untransformed
  * as well as arrows connecting the 2 outlines' corners.
  */
-function CssTransformHighlighter(highlighterEnv) {
-  AutoRefreshHighlighter.call(this, highlighterEnv);
+class CssTransformHighlighter extends AutoRefreshHighlighter {
+  constructor(highlighterEnv) {
+    super(highlighterEnv);
 
-  this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
-    this._buildMarkup.bind(this));
-}
+    this.ID_CLASS_PREFIX = "css-transform-";
 
-CssTransformHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
-  typeName: "CssTransformHighlighter",
+    this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
+      this._buildMarkup.bind(this));
+  }
 
-  ID_CLASS_PREFIX: "css-transform-",
-
-  _buildMarkup: function () {
+  _buildMarkup() {
     let container = createNode(this.win, {
       attributes: {
         "class": "highlighter-container"
       }
     });
 
     // The root wrapper is used to unzoom the highlighter when needed.
     let rootWrapper = createNode(this.win, {
@@ -125,79 +122,79 @@ CssTransformHighlighter.prototype = exte
           "class": "line",
           "marker-end": "url(#" + this.markerId + ")"
         },
         prefix: this.ID_CLASS_PREFIX
       });
     }
 
     return container;
-  },
+  }
 
   /**
    * Destroy the nodes. Remove listeners.
    */
-  destroy: function () {
+  destroy() {
     AutoRefreshHighlighter.prototype.destroy.call(this);
     this.markup.destroy();
-  },
+  }
 
-  getElement: function (id) {
+  getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
-  },
+  }
 
   /**
    * Show the highlighter on a given node
    */
-  _show: function () {
+  _show() {
     if (!this._isTransformed(this.currentNode)) {
       this.hide();
       return false;
     }
 
     return this._update();
-  },
+  }
 
   /**
    * Checks if the supplied node is transformed and not inline
    */
-  _isTransformed: function (node) {
+  _isTransformed(node) {
     let style = getComputedStyle(node);
     return style && (style.transform !== "none" && style.display !== "inline");
-  },
+  }
 
-  _setPolygonPoints: function (quad, id) {
+  _setPolygonPoints(quad, id) {
     let points = [];
     for (let point of ["p1", "p2", "p3", "p4"]) {
       points.push(quad[point].x + "," + quad[point].y);
     }
     this.getElement(id).setAttribute("points", points.join(" "));
-  },
+  }
 
-  _setLinePoints: function (p1, p2, id) {
+  _setLinePoints(p1, p2, id) {
     let line = this.getElement(id);
     line.setAttribute("x1", p1.x);
     line.setAttribute("y1", p1.y);
     line.setAttribute("x2", p2.x);
     line.setAttribute("y2", p2.y);
 
     let dist = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
     if (dist < ARROW_LINE_MIN_DISTANCE) {
       line.removeAttribute("marker-end");
     } else {
       line.setAttribute("marker-end", "url(#" + this.markerId + ")");
     }
-  },
+  }
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
    * Should be called whenever node size or attributes change
    */
-  _update: function () {
+  _update() {
     setIgnoreLayoutChanges(true);
 
     // Getting the points for the transformed shape
     let quads = this.currentQuads.border;
     if (!quads.length ||
         quads[0].bounds.width <= 0 || quads[0].bounds.height <= 0) {
       this._hideShapes();
       return false;
@@ -216,28 +213,29 @@ CssTransformHighlighter.prototype = exte
 
     // Adapt to the current zoom
     this.markup.scaleRootElement(this.currentNode, this.ID_CLASS_PREFIX + "root");
 
     this._showShapes();
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
     return true;
-  },
+  }
 
   /**
    * Hide the highlighter, the outline and the infobar.
    */
-  _hide: function () {
+  _hide() {
     setIgnoreLayoutChanges(true);
     this._hideShapes();
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
-  },
+  }
 
-  _hideShapes: function () {
+  _hideShapes() {
     this.getElement("elements").setAttribute("hidden", "true");
-  },
+  }
 
-  _showShapes: function () {
+  _showShapes() {
     this.getElement("elements").removeAttribute("hidden");
   }
-});
+}
+
 exports.CssTransformHighlighter = CssTransformHighlighter;
--- a/devtools/server/actors/highlighters/geometry-editor.js
+++ b/devtools/server/actors/highlighters/geometry-editor.js
@@ -1,15 +1,14 @@
 /* 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, getCSSStyleRules, getComputedStyle,
         createSVGNode, createNode } = require("./utils/markup");
 const { setIgnoreLayoutChanges, getAdjustedQuads } = require("devtools/shared/layout/utils");
 
 const GEOMETRY_LABEL_SIZE = 6;
 
 // List of all DOM Events subscribed directly to the document from the
@@ -197,50 +196,48 @@ exports.getDefinedGeometryProperties = g
  * The highlighter displays lines and labels for each of the defined properties
  * in and around the element (relative to the offset parent when one exists).
  * The highlighter also highlights the element itself and its offset parent if
  * there is one.
  *
  * Note that the class name contains the word Editor because the aim is for the
  * handles to be draggable in content to make the geometry editable.
  */
-function GeometryEditorHighlighter(highlighterEnv) {
-  AutoRefreshHighlighter.call(this, highlighterEnv);
+class GeometryEditorHighlighter extends AutoRefreshHighlighter {
+  constructor(highlighterEnv) {
+    super(highlighterEnv);
+
+    this.ID_CLASS_PREFIX = "geometry-editor-";
 
-  // The list of element geometry properties that can be set.
-  this.definedProperties = new Map();
+    // The list of element geometry properties that can be set.
+    this.definedProperties = new Map();
 
-  this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
-    this._buildMarkup.bind(this));
+    this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
+      this._buildMarkup.bind(this));
 
-  let { pageListenerTarget } = this.highlighterEnv;
+    let { pageListenerTarget } = this.highlighterEnv;
 
-  // Register the geometry editor instance to all events we're interested in.
-  DOM_EVENTS.forEach(type => pageListenerTarget.addEventListener(type, this));
+    // Register the geometry editor instance to all events we're interested in.
+    DOM_EVENTS.forEach(type => pageListenerTarget.addEventListener(type, this));
+
+    // Register the mousedown event for each Geometry Editor's handler.
+    // Those events are automatically removed when the markup is destroyed.
+    let onMouseDown = this.handleEvent.bind(this);
 
-  // Register the mousedown event for each Geometry Editor's handler.
-  // Those events are automatically removed when the markup is destroyed.
-  let onMouseDown = this.handleEvent.bind(this);
+    for (let side of GeoProp.SIDES) {
+      this.getElement("handler-" + side)
+        .addEventListener("mousedown", onMouseDown);
+    }
 
-  for (let side of GeoProp.SIDES) {
-    this.getElement("handler-" + side)
-      .addEventListener("mousedown", onMouseDown);
+    this.onWillNavigate = this.onWillNavigate.bind(this);
+
+    this.highlighterEnv.on("will-navigate", this.onWillNavigate);
   }
 
-  this.onWillNavigate = this.onWillNavigate.bind(this);
-
-  this.highlighterEnv.on("will-navigate", this.onWillNavigate);
-}
-
-GeometryEditorHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
-  typeName: "GeometryEditorHighlighter",
-
-  ID_CLASS_PREFIX: "geometry-editor-",
-
-  _buildMarkup: function () {
+  _buildMarkup() {
     let container = createNode(this.win, {
       attributes: {"class": "highlighter-container"}
     });
 
     let root = createNode(this.win, {
       parent: container,
       attributes: {
         "id": "root",
@@ -356,19 +353,19 @@ GeometryEditorHighlighter.prototype = ex
           "x": GeoProp.isHorizontal(name) ? "30" : "35",
           "y": "10"
         },
         prefix: this.ID_CLASS_PREFIX
       });
     }
 
     return container;
-  },
+  }
 
-  destroy: function () {
+  destroy() {
     // Avoiding exceptions if `destroy` is called multiple times; and / or the
     // highlighter environment was already destroyed.
     if (!this.highlighterEnv) {
       return;
     }
 
     let { pageListenerTarget } = this.highlighterEnv;
 
@@ -378,19 +375,19 @@ GeometryEditorHighlighter.prototype = ex
     }
 
     AutoRefreshHighlighter.prototype.destroy.call(this);
 
     this.markup.destroy();
     this.definedProperties.clear();
     this.definedProperties = null;
     this.offsetParent = null;
-  },
+  }
 
-  handleEvent: function (event, id) {
+  handleEvent(event, id) {
     // No event handling if the highlighter is hidden
     if (this.getElement("root").hasAttribute("hidden")) {
       return;
     }
 
     const { target, type, pageX, pageY } = event;
 
     switch (type) {
@@ -471,23 +468,23 @@ GeometryEditorHighlighter.prototype = ex
         // it will override the inline style too. To ensure Geometry Editor
         // will always update the element, we have to add `!important` as
         // well.
         this.currentNode.style.setProperty(
           side, (value + delta) + unit, "important");
 
         break;
     }
-  },
+  }
 
-  getElement: function (id) {
+  getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
-  },
+  }
 
-  _show: function () {
+  _show() {
     this.computedStyle = getComputedStyle(this.currentNode);
     let pos = this.computedStyle.position;
     // XXX: sticky positioning is ignored for now. To be implemented next.
     if (pos === "sticky") {
       this.hide();
       return false;
     }
 
@@ -495,19 +492,19 @@ GeometryEditorHighlighter.prototype = ex
     if (!hasUpdated) {
       this.hide();
       return false;
     }
 
     this.getElement("root").removeAttribute("hidden");
 
     return true;
-  },
+  }
 
-  _update: function () {
+  _update() {
     // At each update, the position or/and size may have changed, so get the
     // list of defined properties, and re-position the arrows and highlighters.
     this.definedProperties = getDefinedGeometryProperties(this.currentNode);
 
     if (!this.definedProperties.size) {
       console.warn("The element does not have editable geometry properties");
       return false;
     }
@@ -520,31 +517,31 @@ GeometryEditorHighlighter.prototype = ex
     this.updateArrows();
 
     // Avoid zooming the arrows when content is zoomed.
     let node = this.currentNode;
     this.markup.scaleRootElement(node, this.ID_CLASS_PREFIX + "root");
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
     return true;
-  },
+  }
 
   /**
    * Update the offset parent rectangle.
    * There are 3 different cases covered here:
    * - the node is absolutely/fixed positioned, and an offsetParent is defined
    *   (i.e. it's not just positioned in the viewport): the offsetParent node
    *   is highlighted (i.e. the rectangle is shown),
    * - the node is relatively positioned: the rectangle is shown where the node
    *   would originally have been (because that's where the relative positioning
    *   is calculated from),
    * - the node has no offset parent at all: the offsetParent rectangle is
    *   hidden.
    */
-  updateOffsetParent: function () {
+  updateOffsetParent() {
     // Get the offsetParent, if any.
     this.offsetParent = getOffsetParent(this.currentNode);
     // And the offsetParent quads.
     this.parentQuads = getAdjustedQuads(
         this.win, this.offsetParent.element, "padding");
 
     let el = this.getElement("offset-parent");
 
@@ -575,51 +572,51 @@ GeometryEditorHighlighter.prototype = ex
       }
     }
 
     if (isHighlighted) {
       el.removeAttribute("hidden");
     } else {
       el.setAttribute("hidden", "true");
     }
-  },
+  }
 
-  updateCurrentNode: function () {
+  updateCurrentNode() {
     let box = this.getElement("current-node");
     let {p1, p2, p3, p4} = this.currentQuads.margin[0];
     let attr = p1.x + "," + p1.y + " " +
                p2.x + "," + p2.y + " " +
                p3.x + "," + p3.y + " " +
                p4.x + "," + p4.y;
     box.setAttribute("points", attr);
     box.removeAttribute("hidden");
-  },
+  }
 
-  _hide: function () {
+  _hide() {
     setIgnoreLayoutChanges(true);
 
     this.getElement("root").setAttribute("hidden", "true");
     this.getElement("current-node").setAttribute("hidden", "true");
     this.getElement("offset-parent").setAttribute("hidden", "true");
     this.hideArrows();
 
     this.definedProperties.clear();
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
-  },
+  }
 
-  hideArrows: function () {
+  hideArrows() {
     for (let side of GeoProp.SIDES) {
       this.getElement("arrow-" + side).setAttribute("hidden", "true");
       this.getElement("label-" + side).setAttribute("hidden", "true");
       this.getElement("handler-" + side).setAttribute("hidden", "true");
     }
-  },
+  }
 
-  updateArrows: function () {
+  updateArrows() {
     this.hideArrows();
 
     // Position arrows always end at the node's margin box.
     let marginBox = this.currentQuads.margin[0].bounds;
 
     // Position the side arrows which need to be visible.
     // Arrows always start at the offsetParent edge, and end at the middle
     // position of the node's margin edge.
@@ -666,19 +663,19 @@ GeometryEditorHighlighter.prototype = ex
       let mainAxisStartPos = getSideArrowStartPos(side);
       let mainAxisEndPos = marginBox[side];
       let crossAxisPos = marginBox[GeoProp.crossAxisStart(side)] +
                          marginBox[GeoProp.crossAxisSize(side)] / 2;
 
       this.updateArrow(side, mainAxisStartPos, mainAxisEndPos, crossAxisPos,
                        sideProp.cssRule.style.getPropertyValue(side));
     }
-  },
+  }
 
-  updateArrow: function (side, mainStart, mainEnd, crossPos, labelValue) {
+  updateArrow(side, mainStart, mainEnd, crossPos, labelValue) {
     let arrowEl = this.getElement("arrow-" + side);
     let labelEl = this.getElement("label-" + side);
     let labelTextEl = this.getElement("label-text-" + side);
     let handlerEl = this.getElement("handler-" + side);
 
     // Position the arrow <line>.
     arrowEl.setAttribute(GeoProp.axis(side) + "1", mainStart);
     arrowEl.setAttribute(GeoProp.crossAxis(side) + "1", crossPos);
@@ -704,17 +701,18 @@ GeometryEditorHighlighter.prototype = ex
       }
     }
     let labelCross = crossPos;
     labelEl.setAttribute("transform", GeoProp.isHorizontal(side)
                          ? "translate(" + labelMain + " " + labelCross + ")"
                          : "translate(" + labelCross + " " + labelMain + ")");
     labelEl.removeAttribute("hidden");
     labelTextEl.setTextContent(labelValue);
-  },
+  }
 
   onWillNavigate({ isTopLevel }) {
     if (isTopLevel) {
       this.hide();
     }
-  },
-});
+  }
+}
+
 exports.GeometryEditorHighlighter = GeometryEditorHighlighter;