Backed out 2 changesets (bug 1435373) for patch failing to be applied on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Tue, 10 Apr 2018 04:46:23 +0300
changeset 412502 fc0d5bbaed8b260052c9e03e5e59cfe3b4356dc8
parent 412501 67fabb2e4990654fbd0eede355bc09ae6f911bb9
child 412503 0d9149c03ebc16852adf5c91240cfdddfdc3b9de
push id62413
push userapavel@mozilla.com
push dateTue, 10 Apr 2018 01:46:38 +0000
treeherderautoland@fc0d5bbaed8b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1435373
milestone61.0a1
backs out0ea578dacf239fbb31c3d6e3f6b616d6f61da263
c3b0f6497bb73401f0847bb8e7d8f331673f45e1
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
Backed out 2 changesets (bug 1435373) for patch failing to be applied on a CLOSED TREE Backed out changeset 0ea578dacf23 (bug 1435373) Backed out changeset c3b0f6497bb7 (bug 1435373)
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_01.js
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_03.js
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_04.js
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_05.js
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_06.js
devtools/client/inspector/rules/test/browser_rules_shapes-toggle_07.js
devtools/client/inspector/test/browser_inspector_highlighter-cssshape_04.js
devtools/client/inspector/test/browser_inspector_highlighter-cssshape_05.js
devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06.js
devtools/client/inspector/test/browser_inspector_highlighter-cssshape_07.js
devtools/client/inspector/test/browser_inspector_highlighter-cssshape_iframe_01.js
devtools/client/inspector/test/head.js
devtools/server/actors/highlighters/shapes.js
devtools/server/actors/utils/shapes-utils.js
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_01.js
@@ -20,28 +20,28 @@ const TEST_URI = `
 
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = await openRuleView();
   let highlighters = view.highlighters;
 
-  info("Select a node with a shape value");
   await selectNode("#shape", inspector);
   let container = getRuleViewProperty(view, "#shape", "clip-path").valueSpan;
   let shapesToggle = container.querySelector(".ruleview-shapeswatch");
 
   info("Checking the initial state of the CSS shape toggle in the rule-view.");
   ok(shapesToggle, "Shapes highlighter toggle is visible.");
   ok(!shapesToggle.classList.contains("active"),
     "Shapes highlighter toggle button is not active.");
   ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
     "No CSS shapes highlighter exists in the rule-view.");
   ok(!highlighters.shapesHighlighterShown, "No CSS shapes highlighter is shown.");
+
   info("Toggling ON the CSS shapes highlighter from the rule-view.");
   let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   shapesToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS shapes highlighter is created and toggle button is active in " +
     "the rule-view.");
   ok(shapesToggle.classList.contains("active"),
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_03.js
@@ -59,18 +59,17 @@ add_task(async function() {
   container = getRuleViewProperty(view, ".shape", "clip-path").valueSpan;
   shapeToggle = container.querySelector(".ruleview-shapeswatch");
 
   info("Checking the state of the CSS shapes toggle for the second shapes container " +
     "in the rule-view.");
   ok(shapeToggle, "shapes highlighter toggle is visible.");
   ok(!shapeToggle.classList.contains("active"),
     "shapes highlighter toggle button is not active.");
-  ok(!highlighters.shapesHighlighterShown, "CSS shapes highlighter is still no longer" +
-    "shown due to selecting another node.");
+  ok(highlighters.shapesHighlighterShown, "CSS shapes highlighter is still shown.");
 
   info("Toggling ON the CSS shapes highlighter for the second shapes container " +
     "from the rule-view.");
   onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   shapeToggle.click();
   await onHighlighterShown;
 
   info("Checking the CSS shapes highlighter is created for the second shapes container " +
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_04.js
@@ -18,17 +18,16 @@ const TEST_URI = `
   <div id="shape"></div>
 `;
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = await openRuleView();
   let highlighters = view.highlighters;
 
-  info("Select a node with a shape value");
   await selectNode("#shape", inspector);
   let container = getRuleViewProperty(view, "#shape", "clip-path").valueSpan;
   let shapeToggle = container.querySelector(".ruleview-shapeswatch");
 
   info("Toggling ON the CSS shape highlighter from the rule-view.");
   let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   shapeToggle.click();
   await onHighlighterShown;
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_05.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_05.js
@@ -18,17 +18,16 @@ const TEST_URI = `
   <div id="shape"></div>
 `;
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view, testActor} = await openRuleView();
   let highlighters = view.highlighters;
 
-  info("Select a node with a shape value");
   await selectNode("#shape", inspector);
   let container = getRuleViewProperty(view, "#shape", "clip-path").valueSpan;
   let shapeToggle = container.querySelector(".ruleview-shapeswatch");
 
   info("Toggling ON the CSS shapes highlighter from the rule-view.");
   let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   shapeToggle.click();
   await onHighlighterShown;
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_06.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_06.js
@@ -20,17 +20,16 @@ const TEST_URI = `
   <div class="shape" id="shape2"></div>
 `;
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = await openRuleView();
   let highlighters = view.highlighters;
 
-  info("Selecting the first shapes container.");
   await selectNode("#shape1", inspector);
   let clipPathContainer = getRuleViewProperty(view, ".shape", "clip-path").valueSpan;
   let clipPathShapeToggle = clipPathContainer.querySelector(".ruleview-shapeswatch");
   let shapeOutsideContainer = getRuleViewProperty(view, ".shape",
     "shape-outside").valueSpan;
   let shapeOutsideToggle = shapeOutsideContainer.querySelector(".ruleview-shapeswatch");
 
   info("Toggling ON the CSS shapes highlighter for clip-path from the rule-view.");
@@ -59,9 +58,21 @@ add_task(async function() {
   clipPathShapeToggle = clipPathContainer.querySelector(".ruleview-shapeswatch");
   shapeOutsideContainer = getRuleViewProperty(view, ".shape",
     "shape-outside").valueSpan;
   shapeOutsideToggle = shapeOutsideContainer.querySelector(".ruleview-shapeswatch");
   ok(!clipPathShapeToggle.classList.contains("active"),
      "clip-path toggle button is not active.");
   ok(!shapeOutsideToggle.classList.contains("active"),
      "shape-outside toggle button is not active.");
+
+  info("Selecting the first shapes container.");
+  await selectNode("#shape1", inspector);
+  clipPathContainer = getRuleViewProperty(view, ".shape", "clip-path").valueSpan;
+  clipPathShapeToggle = clipPathContainer.querySelector(".ruleview-shapeswatch");
+  shapeOutsideContainer = getRuleViewProperty(view, ".shape",
+    "shape-outside").valueSpan;
+  shapeOutsideToggle = shapeOutsideContainer.querySelector(".ruleview-shapeswatch");
+  ok(!clipPathShapeToggle.classList.contains("active"),
+     "clip-path toggle button is not active.");
+  ok(shapeOutsideToggle.classList.contains("active"),
+     "shape-outside toggle button is active.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_07.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_07.js
@@ -19,21 +19,25 @@ const TEST_URI = `
 
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = await openRuleView();
   let highlighters = view.highlighters;
 
-  info("Select a node with a shape value");
   await selectNode("#shape", inspector);
   let container = getRuleViewProperty(view, "#shape", "clip-path").valueSpan;
   let shapesToggle = container.querySelector(".ruleview-shapeswatch");
 
+  info("Checking the initial state of the CSS shape toggle in the rule-view.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No CSS shapes highlighter exists in the rule-view.");
+  ok(!highlighters.shapesHighlighterShown, "No CSS shapes highlighter is shown.");
+
   info("Toggling ON the CSS shapes highlighter with transform mode on.");
   let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   EventUtils.sendMouseEvent({type: "click", metaKey: true, ctrlKey: true},
     shapesToggle, view.styleWindow);
   await onHighlighterShown;
 
   info("Checking the CSS shapes highlighter is created and transform mode is on");
   ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
--- a/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_04.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_04.js
@@ -5,320 +5,233 @@
 "use strict";
 
 // Test that shapes are updated correctly on mouse events.
 
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
 
 add_task(async function() {
-  let env = await openInspectorForURL(TEST_URL);
-  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(env);
-  let {testActor, inspector} = env;
-  let view = selectRuleView(inspector);
-  let highlighters = view.highlighters;
+  let inspector = await openInspectorForURL(TEST_URL);
+  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+  let {testActor} = inspector;
 
-  let config = {inspector, view, highlighters, testActor, helper};
-
-  await testPolygonMovePoint(config);
-  await testPolygonAddPoint(config);
-  await testPolygonRemovePoint(config);
-  await testCircleMoveCenter(config);
-  await testEllipseMoveRadius(config);
-  await testInsetMoveEdges(config);
+  await testPolygonMovePoint(testActor, helper);
+  await testPolygonAddPoint(testActor, helper);
+  await testPolygonRemovePoint(testActor, helper);
+  await testCircleMoveCenter(testActor, helper);
+  await testEllipseMoveRadius(testActor, helper);
+  await testInsetMoveEdges(testActor, helper);
 
   helper.finalize();
 });
 
-async function getComputedPropertyValue(selector, property, inspector) {
-  let highlightedNode = await getNodeFront(selector, inspector);
-  let computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
-  return computedStyle[property].value;
-}
-
-async function setup(config) {
-  const { view, selector, property, inspector } = config;
-  info(`Turn on shapes highlighter for ${selector}`);
-  await selectNode(selector, inspector);
-  await toggleShapesHighlighter(view, selector, property, true);
-}
+async function testPolygonMovePoint(testActor, helper) {
+  info("Displaying polygon");
+  await helper.show("#polygon", {mode: "cssClipPath"});
+  let { mouse, highlightedNode } = helper;
 
-async function teardown(config) {
-  const { view, selector, property } = config;
-  info(`Turn off shapes highlighter for ${selector}`);
-  await toggleShapesHighlighter(view, selector, property, false);
-}
-
-async function testPolygonMovePoint(config) {
-  const {inspector, view, highlighters, testActor, helper} = config;
-  const selector = "#polygon";
-  const property = "clip-path";
-
-  await setup({selector, property, ...config});
-
-  let points = await testActor.getHighlighterNodeAttribute(
-    "shapes-polygon", "points", highlighters.highlighters[HIGHLIGHTER_TYPE]);
+  let points = await helper.getElementAttribute("shapes-polygon", "points");
   let [x, y] = points.split(" ")[0].split(",");
-  let quads = await testActor.getAllAdjustedQuads(selector);
+  let quads = await testActor.getAllAdjustedQuads("#polygon");
   let { top, left, width, height } = quads.border[0].bounds;
   x = left + width * x / 100;
   y = top + height * y / 100;
   let dx = width / 10;
   let dy = height / 10;
 
-  let onRuleViewChanged = view.once("ruleview-changed");
   info("Moving first polygon point");
-  let { mouse } = helper;
   await mouse.down(x, y);
   await mouse.move(x + dx, y + dy);
   await mouse.up();
   await testActor.reflow();
-  info("Waiting for rule view changed from shape change");
-  await onRuleViewChanged;
 
-  let definition = await getComputedPropertyValue(selector, property, inspector);
+  let computedStyle = await highlightedNode.getComputedStyle();
+  let definition = computedStyle["clip-path"].value;
   ok(definition.includes(`${dx}px ${dy}px`), `Point moved to ${dx}px ${dy}px`);
-
-  await teardown({selector, property, ...config});
 }
 
-async function testPolygonAddPoint(config) {
-  const {inspector, view, highlighters, testActor, helper} = config;
-  const selector = "#polygon";
-  const property = "clip-path";
-
-  await setup({selector, property, ...config});
+async function testPolygonAddPoint(testActor, helper) {
+  await helper.show("#polygon", {mode: "cssClipPath"});
+  let { mouse, highlightedNode } = helper;
 
   // Move first point to have same x as second point, then double click between
   // the two points to add a new one.
-  let points = await testActor.getHighlighterNodeAttribute(
-    "shapes-polygon", "points", highlighters.highlighters[HIGHLIGHTER_TYPE]);
+  let points = await helper.getElementAttribute("shapes-polygon", "points");
   let pointsArray = points.split(" ");
-  let quads = await testActor.getAllAdjustedQuads(selector);
+  let quads = await testActor.getAllAdjustedQuads("#polygon");
   let { top, left, width, height } = quads.border[0].bounds;
   let [x1, y1] = pointsArray[0].split(",");
   let [x2, y2] = pointsArray[1].split(",");
   x1 = left + width * x1 / 100;
   x2 = left + width * x2 / 100;
   y1 = top + height * y1 / 100;
   y2 = top + height * y2 / 100;
 
-  let { mouse } = helper;
   await mouse.down(x1, y1);
   await mouse.move(x2, y1);
   await mouse.up();
   await testActor.reflow();
 
   let newPointX = x2;
   let newPointY = (y1 + y2) / 2;
   let options = {
     selector: ":root",
     x: newPointX,
     y: newPointY,
     center: false,
     options: {clickCount: 2}
   };
 
-  let onRuleViewChanged = view.once("ruleview-changed");
   info("Adding new polygon point");
   await testActor.synthesizeMouse(options);
   await testActor.reflow();
-  info("Waiting for rule view changed from shape change");
-  await onRuleViewChanged;
 
+  let computedStyle = await highlightedNode.getComputedStyle();
+  let definition = computedStyle["clip-path"].value;
   // Decimal precision for coordinates with percentage units is 2
   let precision = 2;
   // Round to the desired decimal precision and cast to Number to remove trailing zeroes.
   newPointX = Number((newPointX * 100 / width).toFixed(precision));
   newPointY = Number((newPointY * 100 / height).toFixed(precision));
-  let definition = await getComputedPropertyValue(selector, property, inspector);
   ok(definition.includes(`${newPointX}% ${newPointY}%`),
      "Point successfuly added");
-
-  await teardown({selector, property, ...config});
 }
 
-async function testPolygonRemovePoint(config) {
-  const {inspector, highlighters, testActor, helper} = config;
-  const selector = "#polygon";
-  const property = "clip-path";
+async function testPolygonRemovePoint(testActor, helper) {
+  await helper.show("#polygon", {mode: "cssClipPath"});
+  let { highlightedNode } = helper;
 
-  await setup({selector, property, ...config});
-
-  let points = await testActor.getHighlighterNodeAttribute(
-    "shapes-polygon", "points", highlighters.highlighters[HIGHLIGHTER_TYPE]);
+  let points = await helper.getElementAttribute("shapes-polygon", "points");
   let [x, y] = points.split(" ")[0].split(",");
-  let quads = await testActor.getAllAdjustedQuads(selector);
+  let quads = await testActor.getAllAdjustedQuads("#polygon");
   let { top, left, width, height } = quads.border[0].bounds;
 
   let options = {
     selector: ":root",
     x: left + width * x / 100,
     y: top + height * y / 100,
     center: false,
     options: {clickCount: 2}
   };
 
-  info("Move mouse over first point in highlighter");
-  let onEventHandled = highlighters.once("highlighter-event-handled");
-  let { mouse } = helper;
-  await mouse.move(options.x, options.y);
-  await onEventHandled;
-  let markerHidden = await testActor.getHighlighterNodeAttribute(
-    "shapes-marker-hover", "hidden", highlighters.highlighters[HIGHLIGHTER_TYPE]);
-  ok(!markerHidden, "Marker on highlighter is visible");
+  info("Removing first polygon point");
+  await testActor.synthesizeMouse(options);
+  await testActor.reflow();
 
-  info("Double click on first point in highlighter");
-  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-  await testActor.synthesizeMouse(options);
-  info("Waiting for shape changes to apply");
-  await onShapeChangeApplied;
-  let definition = await getComputedPropertyValue(selector, property, inspector);
+  let computedStyle = await highlightedNode.getComputedStyle();
+  let definition = computedStyle["clip-path"].value;
   ok(!definition.includes(`${x}% ${y}%`), "Point successfully removed");
-
-  await teardown({selector, property, ...config});
 }
 
-async function testCircleMoveCenter(config) {
-  const {inspector, highlighters, testActor, helper} = config;
-  const selector = "#circle";
-  const property = "clip-path";
+async function testCircleMoveCenter(testActor, helper) {
+  await helper.show("#circle", {mode: "cssClipPath"});
+  let { mouse, highlightedNode } = helper;
 
-  await setup({selector, property, ...config});
-
-  let cx = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-ellipse", "cx", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let cy = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-ellipse", "cy", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let quads = await testActor.getAllAdjustedQuads(selector);
+  let cx = parseFloat(await helper.getElementAttribute("shapes-ellipse", "cx"));
+  let cy = parseFloat(await helper.getElementAttribute("shapes-ellipse", "cy"));
+  let quads = await testActor.getAllAdjustedQuads("#circle");
   let { width, height } = quads.border[0].bounds;
   let cxPixel = width * cx / 100;
   let cyPixel = height * cy / 100;
   let dx = width / 10;
   let dy = height / 10;
 
-  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   info("Moving circle center");
-  let { mouse } = helper;
-  await mouse.down(cxPixel, cyPixel, selector);
-  await mouse.move(cxPixel + dx, cyPixel + dy, selector);
-  await mouse.up(cxPixel + dx, cyPixel + dy, selector);
+  await mouse.down(cxPixel, cyPixel, "#circle");
+  await mouse.move(cxPixel + dx, cyPixel + dy, "#circle");
+  await mouse.up(cxPixel + dx, cyPixel + dy, "#circle");
   await testActor.reflow();
-  await onShapeChangeApplied;
 
-  let definition = await getComputedPropertyValue(selector, property, inspector);
+  let computedStyle = await highlightedNode.getComputedStyle();
+  let definition = computedStyle["clip-path"].value;
   ok(definition.includes(`at ${cx + 10}% ${cy + 10}%`),
      "Circle center successfully moved");
-
-  await teardown({selector, property, ...config});
 }
 
-async function testEllipseMoveRadius(config) {
-  const {inspector, highlighters, testActor, helper} = config;
-  const selector = "#ellipse";
-  const property = "clip-path";
-
-  await setup({selector, property, ...config});
+async function testEllipseMoveRadius(testActor, helper) {
+  await helper.show("#ellipse", {mode: "cssClipPath"});
+  let { mouse, highlightedNode } = helper;
 
-  let rx = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-ellipse", "rx", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let ry = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-ellipse", "ry", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let cx = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-ellipse", "cx", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let cy = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-ellipse", "cy", highlighters.highlighters[HIGHLIGHTER_TYPE]));
+  let rx = parseFloat(await helper.getElementAttribute("shapes-ellipse", "rx"));
+  let ry = parseFloat(await helper.getElementAttribute("shapes-ellipse", "ry"));
+  let cx = parseFloat(await helper.getElementAttribute("shapes-ellipse", "cx"));
+  let cy = parseFloat(await helper.getElementAttribute("shapes-ellipse", "cy"));
   let quads = await testActor.getAllAdjustedQuads("#ellipse");
   let { width, height } = quads.content[0].bounds;
-  let highlightedNode = await getNodeFront(selector, inspector);
-  let computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
+  let computedStyle = await highlightedNode.getComputedStyle();
   let paddingTop = parseFloat(computedStyle["padding-top"].value);
   let paddingLeft = parseFloat(computedStyle["padding-left"].value);
   let cxPixel = paddingLeft + width * cx / 100;
   let cyPixel = paddingTop + height * cy / 100;
   let rxPixel = cxPixel + width * rx / 100;
   let ryPixel = cyPixel + height * ry / 100;
   let dx = width / 10;
   let dy = height / 10;
 
-  let { mouse } = helper;
   info("Moving ellipse rx");
-  await mouse.down(rxPixel, cyPixel, selector);
-  await mouse.move(rxPixel + dx, cyPixel, selector);
-  await mouse.up(rxPixel + dx, cyPixel, selector);
+  await mouse.down(rxPixel, cyPixel, "#ellipse");
+  await mouse.move(rxPixel + dx, cyPixel, "#ellipse");
+  await mouse.up(rxPixel + dx, cyPixel, "#ellipse");
   await testActor.reflow();
 
   info("Moving ellipse ry");
-  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-  await mouse.down(cxPixel, ryPixel, selector);
-  await mouse.move(cxPixel, ryPixel - dy, selector);
-  await mouse.up(cxPixel, ryPixel - dy, selector);
+  await mouse.down(cxPixel, ryPixel, "#ellipse");
+  await mouse.move(cxPixel, ryPixel - dy, "#ellipse");
+  await mouse.up(cxPixel, ryPixel - dy, "#ellipse");
   await testActor.reflow();
-  await onShapeChangeApplied;
 
-  let definition = await getComputedPropertyValue(selector, property, inspector);
+  computedStyle = await highlightedNode.getComputedStyle();
+  let definition = computedStyle["clip-path"].value;
   ok(definition.includes(`${rx + 10}% ${ry - 10}%`),
      "Ellipse radiuses successfully moved");
-
-  await teardown({selector, property, ...config});
 }
 
-async function testInsetMoveEdges(config) {
-  const {inspector, highlighters, testActor, helper} = config;
-  const selector = "#inset";
-  const property = "clip-path";
-
-  await setup({selector, property, ...config});
+async function testInsetMoveEdges(testActor, helper) {
+  await helper.show("#inset", {mode: "cssClipPath"});
+  let { mouse, highlightedNode } = helper;
 
-  let x = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-rect", "x", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let y = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-rect", "y", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let width = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-rect", "width", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let height = parseFloat(await testActor.getHighlighterNodeAttribute(
-    "shapes-rect", "height", highlighters.highlighters[HIGHLIGHTER_TYPE]));
-  let quads = await testActor.getAllAdjustedQuads(selector);
+  let x = parseFloat(await helper.getElementAttribute("shapes-rect", "x"));
+  let y = parseFloat(await helper.getElementAttribute("shapes-rect", "y"));
+  let width = parseFloat(await helper.getElementAttribute("shapes-rect", "width"));
+  let height = parseFloat(await helper.getElementAttribute("shapes-rect", "height"));
+  let quads = await testActor.getAllAdjustedQuads("#inset");
   let { width: elemWidth, height: elemHeight } = quads.content[0].bounds;
 
   let left = elemWidth * x / 100;
   let top = elemHeight * y / 100;
   let right = left + elemWidth * width / 100;
   let bottom = top + elemHeight * height / 100;
   let xCenter = (left + right) / 2;
   let yCenter = (top + bottom) / 2;
   let dx = elemWidth / 10;
   let dy = elemHeight / 10;
-  let { mouse } = helper;
 
   info("Moving inset top");
-  await mouse.down(xCenter, top, selector);
-  await mouse.move(xCenter, top + dy, selector);
-  await mouse.up(xCenter, top + dy, selector);
+  await mouse.down(xCenter, top, "#inset");
+  await mouse.move(xCenter, top + dy, "#inset");
+  await mouse.up(xCenter, top + dy, "#inset");
   await testActor.reflow();
 
   info("Moving inset bottom");
-  await mouse.down(xCenter, bottom, selector);
-  await mouse.move(xCenter, bottom + dy, selector);
-  await mouse.up(xCenter, bottom + dy, selector);
+  await mouse.down(xCenter, bottom, "#inset");
+  await mouse.move(xCenter, bottom + dy, "#inset");
+  await mouse.up(xCenter, bottom + dy, "#inset");
   await testActor.reflow();
 
   info("Moving inset left");
-  await mouse.down(left, yCenter, selector);
-  await mouse.move(left + dx, yCenter, selector);
-  await mouse.up(left + dx, yCenter, selector);
+  await mouse.down(left, yCenter, "#inset");
+  await mouse.move(left + dx, yCenter, "#inset");
+  await mouse.up(left + dx, yCenter, "#inset");
   await testActor.reflow();
 
   info("Moving inset right");
-  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-  await mouse.down(right, yCenter, selector);
-  await mouse.move(right + dx, yCenter, selector);
-  await mouse.up(right + dx, yCenter, selector);
+  await mouse.down(right, yCenter, "#inset");
+  await mouse.move(right + dx, yCenter, "#inset");
+  await mouse.up(right + dx, yCenter, "#inset");
   await testActor.reflow();
-  await onShapeChangeApplied;
 
-  let definition = await getComputedPropertyValue(selector, property, inspector);
+  let computedStyle = await highlightedNode.getComputedStyle();
+  let definition = computedStyle["clip-path"].value;
   ok(definition.includes(
     `${top + dy}px ${elemWidth - right - dx}px ${100 - y - height - 10}% ${x + 10}%`),
      "Inset edges successfully moved");
-
-  await teardown({selector, property, ...config});
 }
--- a/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_05.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_05.js
@@ -2,114 +2,109 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test hovering over shape points in the rule-view and shapes highlighter.
 
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
+
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
+const CSS_SHAPES_ENABLED_PREF = "devtools.inspector.shapesHighlighter.enabled";
 
 add_task(async function() {
+  await pushPref(CSS_SHAPES_ENABLED_PREF, true);
   let env = await openInspectorForURL(TEST_URL);
   let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(env);
   let { testActor, inspector } = env;
   let view = selectRuleView(inspector);
   let highlighters = view.highlighters;
-  let config = { inspector, view, highlighters, testActor, helper };
 
-  await highlightFromRuleView(config);
-  await highlightFromHighlighter(config);
+  await highlightFromRuleView(inspector, view, highlighters, testActor);
+  await highlightFromHighlighter(view, highlighters, testActor, helper);
 });
 
-async function setup(config) {
-  const { view, selector, property, inspector } = config;
-  info(`Turn on shapes highlighter for ${selector}`);
-  await selectNode(selector, inspector);
-  await toggleShapesHighlighter(view, selector, property, true);
-}
-
-async function teardown(config) {
-  const { view, selector, property } = config;
-  info(`Turn off shapes highlighter for ${selector}`);
-  await toggleShapesHighlighter(view, selector, property, false);
-}
-/*
-* Test that points hovered in the rule view will highlight corresponding points
-* in the shapes highlighter on the page.
-*/
-async function highlightFromRuleView(config) {
-  const { view, highlighters, testActor } = config;
-  const selector = "#polygon";
-  const property = "clip-path";
-
-  await setup({ selector, property, ...config });
-
-  let container = getRuleViewProperty(view, selector, property).valueSpan;
+async function highlightFromRuleView(inspector, view, highlighters, testActor) {
+  await selectNode("#polygon", inspector);
+  await toggleShapesHighlighter(view, highlighters, "#polygon", "clip-path", true);
+  let container = getRuleViewProperty(view, "#polygon", "clip-path").valueSpan;
   let shapesToggle = container.querySelector(".ruleview-shapeswatch");
 
   let highlighterFront = highlighters.highlighters[HIGHLIGHTER_TYPE];
   let markerHidden = await testActor.getHighlighterNodeAttribute(
     "shapes-marker-hover", "hidden", highlighterFront);
   ok(markerHidden, "Hover marker on highlighter is not visible");
 
   info("Hover over point 0 in rule view");
   let pointSpan = container.querySelector(".ruleview-shape-point[data-point='0']");
   let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   EventUtils.synthesizeMouseAtCenter(pointSpan, {type: "mousemove"}, view.styleWindow);
   await onHighlighterShown;
 
-  info("Point in shapes highlighter is marked when same point in rule view is hovered");
+  ok(pointSpan.classList.contains("active"), "Hovered span is active");
+  is(highlighters.state.shapes.options.hoverPoint, "0",
+     "Hovered point is saved to state");
+
   markerHidden = await testActor.getHighlighterNodeAttribute(
     "shapes-marker-hover", "hidden", highlighterFront);
   ok(!markerHidden, "Marker on highlighter is visible");
 
   info("Move mouse off point");
   onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   EventUtils.synthesizeMouseAtCenter(shapesToggle, {type: "mousemove"}, view.styleWindow);
   await onHighlighterShown;
 
+  ok(!pointSpan.classList.contains("active"), "Hovered span is no longer active");
+  is(highlighters.state.shapes.options.hoverPoint, null, "Hovered point is null");
+
   markerHidden = await testActor.getHighlighterNodeAttribute(
     "shapes-marker-hover", "hidden", highlighterFront);
   ok(markerHidden, "Marker on highlighter is not visible");
 
-  await teardown({selector, property, ...config});
+  info("Hide shapes highlighter");
+  await toggleShapesHighlighter(view, highlighters, "#polygon", "clip-path", false);
 }
 
-/*
-* Test that points hovered in the shapes highlighter on the page will highlight
-* corresponding points in the rule view.
-*/
-async function highlightFromHighlighter(config) {
-  const { view, highlighters, testActor, helper } = config;
-  const selector = "#polygon";
-  const property = "clip-path";
-
-  await setup({ selector, property, ...config });
-
+async function highlightFromHighlighter(view, highlighters, testActor, helper) {
   let highlighterFront = highlighters.highlighters[HIGHLIGHTER_TYPE];
   let { mouse } = helper;
-  let container = getRuleViewProperty(view, selector, property).valueSpan;
+
+  await toggleShapesHighlighter(view, highlighters, "#polygon", "clip-path", true);
+  let container = getRuleViewProperty(view, "#polygon", "clip-path").valueSpan;
 
   info("Hover over first point in highlighter");
   let onEventHandled = highlighters.once("highlighter-event-handled");
   await mouse.move(0, 0);
   await onEventHandled;
   let markerHidden = await testActor.getHighlighterNodeAttribute(
     "shapes-marker-hover", "hidden", highlighterFront);
   ok(!markerHidden, "Marker on highlighter is visible");
 
-  info("Point in rule view is marked when same point in shapes highlighter is hovered");
   let pointSpan = container.querySelector(".ruleview-shape-point[data-point='0']");
   ok(pointSpan.classList.contains("active"), "Span for point 0 is active");
+  is(highlighters.state.shapes.hoverPoint, "0", "Hovered point is saved to state");
+
+  info("Check that point is still highlighted after moving it");
+  await mouse.down(0, 0);
+  await mouse.move(10, 10);
+  await mouse.up(10, 10);
+  markerHidden = await testActor.getHighlighterNodeAttribute(
+    "shapes-marker-hover", "hidden", highlighterFront);
+  ok(!markerHidden, "Marker on highlighter is visible after moving point");
+
+  container = getRuleViewProperty(view, "element", "clip-path").valueSpan;
+  pointSpan = container.querySelector(".ruleview-shape-point[data-point='0']");
+  ok(pointSpan.classList.contains("active"),
+     "Span for point 0 is active after moving point");
+  is(highlighters.state.shapes.hoverPoint, "0",
+     "Hovered point is saved to state after moving point");
 
   info("Move mouse off point");
   onEventHandled = highlighters.once("highlighter-event-handled");
   await mouse.move(100, 100);
   await onEventHandled;
   markerHidden = await testActor.getHighlighterNodeAttribute(
     "shapes-marker-hover", "hidden", highlighterFront);
   ok(markerHidden, "Marker on highlighter is no longer visible");
   ok(!pointSpan.classList.contains("active"), "Span for point 0 is no longer active");
-
-  await teardown({ selector, property, ...config });
+  is(highlighters.state.shapes.hoverPoint, null, "Hovered point is null");
 }
--- a/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06.js
@@ -3,183 +3,145 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Test that shapes are updated correctly on mouse events in transform mode.
 
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
-const SHAPE_SELECTORS = ["#polygon-transform", "#circle", "#ellipse", "#inset"];
+const SHAPE_IDS = ["#polygon-transform", "#circle", "#ellipse", "#inset"];
 
 add_task(async function() {
-  let env = await openInspectorForURL(TEST_URL);
-  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(env);
-  let {testActor, inspector} = env;
-  let view = selectRuleView(inspector);
-  let highlighters = view.highlighters;
-  let config = { inspector, view, highlighters, testActor, helper };
+  let inspector = await openInspectorForURL(TEST_URL);
+  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+  let {testActor} = inspector;
 
-  await testTranslate(config);
-  await testScale(config);
+  await testTranslate(testActor, helper);
+  await testScale(testActor, helper);
+
+  helper.finalize();
 });
 
-async function setup(config) {
-  const { inspector, view, selector, property, options } = config;
-  await selectNode(selector, inspector);
-  await toggleShapesHighlighter(view, selector, property, true, options);
-}
-
-async function teardown(config) {
-  const { view, selector, property } = config;
-  info(`Turn off shapes highlighter for ${selector}`);
-  await toggleShapesHighlighter(view, selector, property, false);
-}
-
-async function testTranslate(config) {
-  const { testActor, helper, highlighters } = config;
-  const options = { transformMode: true };
-  const property = "clip-path";
-
-  for (let selector of SHAPE_SELECTORS) {
-    await setup({selector, property, options, ...config});
+async function testTranslate(testActor, helper) {
+  for (let shape of SHAPE_IDS) {
+    info(`Displaying ${shape}`);
+    await helper.show(shape, {mode: "cssClipPath", transformMode: true});
     let { mouse } = helper;
 
-    let { center, width, height } = await getBoundingBoxInPx({selector, ...config});
+    let { center, width, height } = await getBoundingBoxInPx(testActor, helper, shape);
     let [x, y] = center;
     let dx = width / 10;
     let dy = height / 10;
-    let onShapeChangeApplied;
 
-    info(`Translating ${selector}`);
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(x, y, selector);
-    await mouse.move(x + dx, y + dy, selector);
-    await mouse.up(x + dx, y + dy, selector);
-    await onShapeChangeApplied;
-
-    let newBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(newBB.center[0], x, `${selector} translated on y axis`);
-    isnot(newBB.center[1], y, `${selector} translated on x axis`);
+    info(`Translating ${shape}`);
+    await mouse.down(x, y, shape);
+    await mouse.move(x + dx, y + dy, shape);
+    await mouse.up(x + dx, y + dy, shape);
+    await testActor.reflow();
 
-    info(`Translating ${selector} back`);
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(x + dx, y + dy, selector);
-    await mouse.move(x, y, selector);
-    await mouse.up(x, y, selector);
+    let newBB = await getBoundingBoxInPx(testActor, helper);
+    isnot(newBB.center[0], x, `${shape} translated on y axis`);
+    isnot(newBB.center[1], y, `${shape} translated on x axis`);
+
+    info(`Translating ${shape} back`);
+    await mouse.down(x + dx, y + dy, shape);
+    await mouse.move(x, y, shape);
+    await mouse.up(x, y, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    newBB = await getBoundingBoxInPx({selector, ...config});
-    is(newBB.center[0], x, `${selector} translated back on x axis`);
-    is(newBB.center[1], y, `${selector} translated back on y axis`);
-
-    await teardown({selector, property, ...config});
+    newBB = await getBoundingBoxInPx(testActor, helper, shape);
+    is(newBB.center[0], x, `${shape} translated back on x axis`);
+    is(newBB.center[1], y, `${shape} translated back on y axis`);
   }
 }
 
-async function testScale(config) {
-  const { testActor, helper, highlighters } = config;
-  const options = { transformMode: true };
-  const property = "clip-path";
-
-  for (let selector of SHAPE_SELECTORS) {
-    await setup({selector, property, options, ...config});
+async function testScale(testActor, helper) {
+  for (let shape of SHAPE_IDS) {
+    info(`Displaying ${shape}`);
+    await helper.show(shape, {mode: "cssClipPath", transformMode: true});
     let { mouse } = helper;
 
     let { nw, width,
-          height, center } = await getBoundingBoxInPx({selector, ...config});
+          height, center } = await getBoundingBoxInPx(testActor, helper, shape);
 
     // if the top or left edges are not visible, move the shape so it is.
     if (nw[0] < 0 || nw[1] < 0) {
       let [x, y] = center;
       let dx = Math.max(0, -nw[0]);
       let dy = Math.max(0, -nw[1]);
-      await mouse.down(x, y, selector);
-      await mouse.move(x + dx, y + dy, selector);
-      await mouse.up(x + dx, y + dy, selector);
+      await mouse.down(x, y, shape);
+      await mouse.move(x + dx, y + dy, shape);
+      await mouse.up(x + dx, y + dy, shape);
       await testActor.reflow();
       nw[0] += dx;
       nw[1] += dy;
     }
     let dx = width / 10;
     let dy = height / 10;
-    let onShapeChangeApplied;
 
     info("Scaling from nw");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(nw[0], nw[1], selector);
-    await mouse.move(nw[0] + dx, nw[1] + dy, selector);
-    await mouse.up(nw[0] + dx, nw[1] + dy, selector);
+    await mouse.down(nw[0], nw[1], shape);
+    await mouse.move(nw[0] + dx, nw[1] + dy, shape);
+    await mouse.up(nw[0] + dx, nw[1] + dy, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let nwBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(nwBB.nw[0], nw[0], `${selector} nw moved right after nw scale`);
-    isnot(nwBB.nw[1], nw[1], `${selector} nw moved down after nw scale`);
-    isnot(nwBB.width, width, `${selector} width reduced after nw scale`);
-    isnot(nwBB.height, height, `${selector} height reduced after nw scale`);
+    let nwBB = await getBoundingBoxInPx(testActor, helper, shape);
+    isnot(nwBB.nw[0], nw[0], `${shape} nw moved right after nw scale`);
+    isnot(nwBB.nw[1], nw[1], `${shape} nw moved down after nw scale`);
+    isnot(nwBB.width, width, `${shape} width reduced after nw scale`);
+    isnot(nwBB.height, height, `${shape} height reduced after nw scale`);
 
     info("Scaling from ne");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(nwBB.ne[0], nwBB.ne[1], selector);
-    await mouse.move(nwBB.ne[0] - dx, nwBB.ne[1] + dy, selector);
-    await mouse.up(nwBB.ne[0] - dx, nwBB.ne[1] + dy, selector);
+    await mouse.down(nwBB.ne[0], nwBB.ne[1], shape);
+    await mouse.move(nwBB.ne[0] - dx, nwBB.ne[1] + dy, shape);
+    await mouse.up(nwBB.ne[0] - dx, nwBB.ne[1] + dy, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let neBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(neBB.ne[0], nwBB.ne[0], `${selector} ne moved right after ne scale`);
-    isnot(neBB.ne[1], nwBB.ne[1], `${selector} ne moved down after ne scale`);
-    isnot(neBB.width, nwBB.width, `${selector} width reduced after ne scale`);
-    isnot(neBB.height, nwBB.height, `${selector} height reduced after ne scale`);
+    let neBB = await getBoundingBoxInPx(testActor, helper, shape);
+    isnot(neBB.ne[0], nwBB.ne[0], `${shape} ne moved right after ne scale`);
+    isnot(neBB.ne[1], nwBB.ne[1], `${shape} ne moved down after ne scale`);
+    isnot(neBB.width, nwBB.width, `${shape} width reduced after ne scale`);
+    isnot(neBB.height, nwBB.height, `${shape} height reduced after ne scale`);
 
     info("Scaling from sw");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(neBB.sw[0], neBB.sw[1], selector);
-    await mouse.move(neBB.sw[0] + dx, neBB.sw[1] - dy, selector);
-    await mouse.up(neBB.sw[0] + dx, neBB.sw[1] - dy, selector);
+    await mouse.down(neBB.sw[0], neBB.sw[1], shape);
+    await mouse.move(neBB.sw[0] + dx, neBB.sw[1] - dy, shape);
+    await mouse.up(neBB.sw[0] + dx, neBB.sw[1] - dy, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let swBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(swBB.sw[0], neBB.sw[0], `${selector} sw moved right after sw scale`);
-    isnot(swBB.sw[1], neBB.sw[1], `${selector} sw moved down after sw scale`);
-    isnot(swBB.width, neBB.width, `${selector} width reduced after sw scale`);
-    isnot(swBB.height, neBB.height, `${selector} height reduced after sw scale`);
+    let swBB = await getBoundingBoxInPx(testActor, helper, shape);
+    isnot(swBB.sw[0], neBB.sw[0], `${shape} sw moved right after sw scale`);
+    isnot(swBB.sw[1], neBB.sw[1], `${shape} sw moved down after sw scale`);
+    isnot(swBB.width, neBB.width, `${shape} width reduced after sw scale`);
+    isnot(swBB.height, neBB.height, `${shape} height reduced after sw scale`);
 
     info("Scaling from se");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(swBB.se[0], swBB.se[1], selector);
-    await mouse.move(swBB.se[0] - dx, swBB.se[1] - dy, selector);
-    await mouse.up(swBB.se[0] - dx, swBB.se[1] - dy, selector);
+    await mouse.down(swBB.se[0], swBB.se[1], shape);
+    await mouse.move(swBB.se[0] - dx, swBB.se[1] - dy, shape);
+    await mouse.up(swBB.se[0] - dx, swBB.se[1] - dy, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let seBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(seBB.se[0], swBB.se[0], `${selector} se moved right after se scale`);
-    isnot(seBB.se[1], swBB.se[1], `${selector} se moved down after se scale`);
-    isnot(seBB.width, swBB.width, `${selector} width reduced after se scale`);
-    isnot(seBB.height, swBB.height, `${selector} height reduced after se scale`);
-
-    await teardown({selector, property, ...config});
+    let seBB = await getBoundingBoxInPx(testActor, helper, shape);
+    isnot(seBB.se[0], swBB.se[0], `${shape} se moved right after se scale`);
+    isnot(seBB.se[1], swBB.se[1], `${shape} se moved down after se scale`);
+    isnot(seBB.width, swBB.width, `${shape} width reduced after se scale`);
+    isnot(seBB.height, swBB.height, `${shape} height reduced after se scale`);
   }
 }
 
-async function getBoundingBoxInPx(config) {
-  const { testActor, selector, inspector, highlighters } = config;
-  let quads = await testActor.getAllAdjustedQuads(selector);
+async function getBoundingBoxInPx(testActor, helper, shape = "#polygon") {
+  let quads = await testActor.getAllAdjustedQuads(shape);
   let { width, height } = quads.content[0].bounds;
-  let highlightedNode = await getNodeFront(selector, inspector);
-  let computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
+  let computedStyle = await helper.highlightedNode.getComputedStyle();
   let paddingTop = parseFloat(computedStyle["padding-top"].value);
   let paddingLeft = parseFloat(computedStyle["padding-left"].value);
+
   // path is always of form "Mx y Lx y Lx y Lx y Z", where x/y are numbers
-  let path = await testActor.getHighlighterNodeAttribute(
-    "shapes-bounding-box", "d", highlighters.highlighters[HIGHLIGHTER_TYPE]);
+  let path = await helper.getElementAttribute("shapes-bounding-box", "d");
   let coords = path.replace(/[MLZ]/g, "").split(" ").map((n, i) => {
     return i % 2 === 0 ? paddingLeft + width * n / 100 : paddingTop + height * n / 100;
   });
 
   let nw = [coords[0], coords[1]];
   let ne = [coords[2], coords[3]];
   let se = [coords[4], coords[5]];
   let sw = [coords[6], coords[7]];
--- a/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_07.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_07.js
@@ -3,140 +3,111 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Test that shapes are updated correctly for scaling on one axis in transform mode.
 
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
-const SHAPE_SELECTORS = ["#polygon-transform", "#ellipse"];
+const SHAPE_IDS = ["#polygon-transform", "#ellipse"];
 
 add_task(async function() {
-  let env = await openInspectorForURL(TEST_URL);
-  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(env);
-  let {testActor, inspector} = env;
-  let view = selectRuleView(inspector);
-  let highlighters = view.highlighters;
-  let config = { inspector, view, highlighters, testActor, helper };
+  let inspector = await openInspectorForURL(TEST_URL);
+  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+  let {testActor} = inspector;
 
-  await testOneDimScale(config);
+  await testOneDimScale(testActor, helper);
+
+  helper.finalize();
 });
 
-async function setup(config) {
-  const { inspector, view, selector, property, options } = config;
-  await selectNode(selector, inspector);
-  await toggleShapesHighlighter(view, selector, property, true, options);
-}
-
-async function teardown(config) {
-  const { view, selector, property } = config;
-  info(`Turn off shapes highlighter for ${selector}`);
-  await toggleShapesHighlighter(view, selector, property, false);
-}
-
-async function testOneDimScale(config) {
-  const { testActor, helper, highlighters } = config;
-  const options = { transformMode: true };
-  const property = "clip-path";
-
-  for (let selector of SHAPE_SELECTORS) {
-    await setup({selector, property, options, ...config});
+async function testOneDimScale(testActor, helper) {
+  for (let shape of SHAPE_IDS) {
+    info(`Displaying ${shape}`);
+    await helper.show(shape, {mode: "cssClipPath", transformMode: true});
     let { mouse } = helper;
 
     let { nw, width,
-          height, center } = await getBoundingBoxInPx({selector, ...config});
+          height, center } = await getBoundingBoxInPx(testActor, helper, shape);
 
     // if the top or left edges are not visible, move the shape so it is.
     if (nw[0] < 0 || nw[1] < 0) {
       let [x, y] = center;
       let dx = Math.max(0, -nw[0]);
       let dy = Math.max(0, -nw[1]);
-      await mouse.down(x, y, selector);
-      await mouse.move(x + dx, y + dy, selector);
-      await mouse.up(x + dx, y + dy, selector);
+      await mouse.down(x, y, shape);
+      await mouse.move(x + dx, y + dy, shape);
+      await mouse.up(x + dx, y + dy, shape);
       await testActor.reflow();
       nw[0] += dx;
       nw[1] += dy;
     }
     let dx = width / 10;
     let dy = height / 10;
-    let onShapeChangeApplied;
 
     info("Scaling from w");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(nw[0], center[1], selector);
-    await mouse.move(nw[0] + dx, center[1], selector);
-    await mouse.up(nw[0] + dx, center[1], selector);
+    await mouse.down(nw[0], center[1], shape);
+    await mouse.move(nw[0] + dx, center[1], shape);
+    await mouse.up(nw[0] + dx, center[1], shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let wBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(wBB.nw[0], nw[0], `${selector} nw moved right after w scale`);
-    is(wBB.nw[1], nw[1], `${selector} nw not moved down after w scale`);
-    isnot(wBB.width, width, `${selector} width reduced after w scale`);
-    is(wBB.height, height, `${selector} height not reduced after w scale`);
+    let wBB = await getBoundingBoxInPx(testActor, helper, shape);
+    isnot(wBB.nw[0], nw[0], `${shape} nw moved right after w scale`);
+    is(wBB.nw[1], nw[1], `${shape} nw not moved down after w scale`);
+    isnot(wBB.width, width, `${shape} width reduced after w scale`);
+    is(wBB.height, height, `${shape} height not reduced after w scale`);
 
     info("Scaling from e");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(wBB.ne[0], center[1], selector);
-    await mouse.move(wBB.ne[0] - dx, center[1], selector);
-    await mouse.up(wBB.ne[0] - dx, center[1], selector);
+    await mouse.down(wBB.ne[0], center[1], shape);
+    await mouse.move(wBB.ne[0] - dx, center[1], shape);
+    await mouse.up(wBB.ne[0] - dx, center[1], shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let eBB = await getBoundingBoxInPx({selector, ...config});
-    isnot(eBB.ne[0], wBB.ne[0], `${selector} ne moved left after e scale`);
-    is(eBB.ne[1], wBB.ne[1], `${selector} ne not moved down after e scale`);
-    isnot(eBB.width, wBB.width, `${selector} width reduced after e scale`);
-    is(eBB.height, wBB.height, `${selector} height not reduced after e scale`);
+    let eBB = await getBoundingBoxInPx(testActor, helper, shape);
+    isnot(eBB.ne[0], wBB.ne[0], `${shape} ne moved left after e scale`);
+    is(eBB.ne[1], wBB.ne[1], `${shape} ne not moved down after e scale`);
+    isnot(eBB.width, wBB.width, `${shape} width reduced after e scale`);
+    is(eBB.height, wBB.height, `${shape} height not reduced after e scale`);
 
     info("Scaling from s");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(eBB.center[0], eBB.sw[1], selector);
-    await mouse.move(eBB.center[0], eBB.sw[1] - dy, selector);
-    await mouse.up(eBB.center[0], eBB.sw[1] - dy, selector);
+    await mouse.down(eBB.center[0], eBB.sw[1], shape);
+    await mouse.move(eBB.center[0], eBB.sw[1] - dy, shape);
+    await mouse.up(eBB.center[0], eBB.sw[1] - dy, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let sBB = await getBoundingBoxInPx({selector, ...config});
-    is(sBB.sw[0], eBB.sw[0], `${selector} sw not moved right after w scale`);
-    isnot(sBB.sw[1], eBB.sw[1], `${selector} sw moved down after w scale`);
-    is(sBB.width, eBB.width, `${selector} width not reduced after w scale`);
-    isnot(sBB.height, eBB.height, `${selector} height reduced after w scale`);
+    let sBB = await getBoundingBoxInPx(testActor, helper, shape);
+    is(sBB.sw[0], eBB.sw[0], `${shape} sw not moved right after w scale`);
+    isnot(sBB.sw[1], eBB.sw[1], `${shape} sw moved down after w scale`);
+    is(sBB.width, eBB.width, `${shape} width not reduced after w scale`);
+    isnot(sBB.height, eBB.height, `${shape} height reduced after w scale`);
 
     info("Scaling from n");
-    onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
-    await mouse.down(sBB.center[0], sBB.nw[1], selector);
-    await mouse.move(sBB.center[0], sBB.nw[1] + dy, selector);
-    await mouse.up(sBB.center[0], sBB.nw[1] + dy, selector);
+    await mouse.down(sBB.center[0], sBB.nw[1], shape);
+    await mouse.move(sBB.center[0], sBB.nw[1] + dy, shape);
+    await mouse.up(sBB.center[0], sBB.nw[1] + dy, shape);
     await testActor.reflow();
-    await onShapeChangeApplied;
 
-    let nBB = await getBoundingBoxInPx({selector, ...config});
-    is(nBB.nw[0], sBB.nw[0], `${selector} nw not moved right after n scale`);
-    isnot(nBB.nw[1], sBB.nw[1], `${selector} nw moved down after n scale`);
-    is(nBB.width, sBB.width, `${selector} width reduced after n scale`);
-    isnot(nBB.height, sBB.height, `${selector} height not reduced after n scale`);
-
-    await teardown({selector, property, ...config});
+    let nBB = await getBoundingBoxInPx(testActor, helper, shape);
+    is(nBB.nw[0], sBB.nw[0], `${shape} nw not moved right after n scale`);
+    isnot(nBB.nw[1], sBB.nw[1], `${shape} nw moved down after n scale`);
+    is(nBB.width, sBB.width, `${shape} width reduced after n scale`);
+    isnot(nBB.height, sBB.height, `${shape} height not reduced after n scale`);
   }
 }
 
-async function getBoundingBoxInPx(config) {
-  const { testActor, selector, inspector, highlighters } = config;
-  let quads = await testActor.getAllAdjustedQuads(selector);
+async function getBoundingBoxInPx(testActor, helper, shape = "#polygon") {
+  let quads = await testActor.getAllAdjustedQuads(shape);
   let { width, height } = quads.content[0].bounds;
-  let highlightedNode = await getNodeFront(selector, inspector);
-  let computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
+  let computedStyle = await helper.highlightedNode.getComputedStyle();
   let paddingTop = parseFloat(computedStyle["padding-top"].value);
   let paddingLeft = parseFloat(computedStyle["padding-left"].value);
+
   // path is always of form "Mx y Lx y Lx y Lx y Z", where x/y are numbers
-  let path = await testActor.getHighlighterNodeAttribute(
-    "shapes-bounding-box", "d", highlighters.highlighters[HIGHLIGHTER_TYPE]);
+  let path = await helper.getElementAttribute("shapes-bounding-box", "d");
   let coords = path.replace(/[MLZ]/g, "").split(" ").map((n, i) => {
     return i % 2 === 0 ? paddingLeft + width * n / 100 : paddingTop + height * n / 100;
   });
 
   let nw = [coords[0], coords[1]];
   let ne = [coords[2], coords[3]];
   let se = [coords[4], coords[5]];
   let sw = [coords[6], coords[7]];
--- a/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_iframe_01.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_iframe_01.js
@@ -5,55 +5,42 @@
 "use strict";
 
 // Test that shapes in iframes are updated correctly on mouse events.
 
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes_iframe.html";
 const HIGHLIGHTER_TYPE = "ShapesHighlighter";
 
 add_task(async function() {
-  let env = await openInspectorForURL(TEST_URL);
-  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(env);
-  let {testActor, inspector} = env;
-  let view = selectRuleView(inspector);
-  let highlighters = view.highlighters;
-  let config = {inspector, view, highlighters, testActor, helper};
+  let inspector = await openInspectorForURL(TEST_URL);
+  let helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+  let {testActor} = inspector;
 
-  await testPolygonIframeMovePoint(config);
+  await testPolygonIframeMovePoint(testActor, helper);
+
+  await helper.finalize();
 });
 
-async function testPolygonIframeMovePoint(config) {
-  const { inspector, view, testActor, helper } = config;
-  const selector = "#polygon";
-  const property = "clip-path";
+async function testPolygonIframeMovePoint(testActor, helper) {
+  info("Displaying polygon");
+  await helper.show("#polygon", {mode: "cssClipPath"}, "#frame");
+  let { mouse, highlightedNode } = helper;
 
-  info(`Turn on shapes highlighter for ${selector}`);
-  // Get a reference to the highlighter's target node inside the iframe.
-  let highlightedNode = await getNodeFrontInFrame(selector, "#frame", inspector);
-  // Select the nested node so toggling of the shapes highlighter works from the rule view
-  await selectNode(highlightedNode, inspector);
-  await toggleShapesHighlighter(view, selector, property, true);
-  let { mouse } = helper;
-
-  let onRuleViewChanged = view.once("ruleview-changed");
   info("Moving polygon point visible in iframe");
   await mouse.down(10, 10);
   await mouse.move(20, 20);
   await mouse.up();
   await testActor.reflow();
-  await onRuleViewChanged;
 
-  let computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
+  let computedStyle = await highlightedNode.getComputedStyle();
   let definition = computedStyle["clip-path"].value;
   ok(definition.includes("10px 10px"), "Point moved to 10px 10px");
 
-  onRuleViewChanged = view.once("ruleview-changed");
   info("Moving polygon point not visible in iframe");
   await mouse.down(110, 410);
   await mouse.move(120, 420);
   await mouse.up();
   await testActor.reflow();
-  await onRuleViewChanged;
 
-  computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
+  computedStyle = await highlightedNode.getComputedStyle();
   definition = computedStyle["clip-path"].value;
   ok(definition.includes("110px 51.25%"), "Point moved to 110px 51.25%");
 }
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -790,44 +790,36 @@ async function getDisplayedNodeTextConte
 }
 
 /**
  * Toggle the shapes highlighter by simulating a click on the toggle
  * in the rules view with the given selector and property
  *
  * @param {CssRuleView} view
  *        The instance of the rule-view panel
+ * @param {Object} highlighters
+ *        The highlighters instance of the rule-view panel
  * @param {String} selector
  *        The selector in the rule-view to look for the property in
  * @param {String} property
  *        The name of the property
  * @param {Boolean} show
  *        If true, the shapes highlighter is being shown. If false, it is being hidden
- * @param {Options} options
- *        Config option for the shapes highlighter. Contains:
- *        - {Boolean} transformMode: wether to show the highlighter in transforms mode
  */
-async function toggleShapesHighlighter(view, selector, property, show, options = {}) {
-  info(`Toggle shapes highlighter ${show ? "on" : "off"} for ${property} on ${selector}`);
-  const highlighters = view.highlighters;
-  const container = getRuleViewProperty(view, selector, property).valueSpan;
-  const shapesToggle = container.querySelector(".ruleview-shapeswatch");
-
-  let metaKey = options.transformMode;
-  let ctrlKey = options.transformMode;
-
+async function toggleShapesHighlighter(view, highlighters, selector, property, show) {
+  info("Toggle shapes highlighter");
+  let container = getRuleViewProperty(view, selector, property).valueSpan;
+  let shapesToggle = container.querySelector(".ruleview-shapeswatch");
   if (show) {
     let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
-    EventUtils.sendMouseEvent({type: "click", metaKey, ctrlKey },
-      shapesToggle, view.styleWindow);
+    shapesToggle.click();
     await onHighlighterShown;
   } else {
     let onHighlighterHidden = highlighters.once("shapes-highlighter-hidden");
-    EventUtils.sendMouseEvent({type: "click", metaKey, ctrlKey },
-      shapesToggle, view.styleWindow);
+    shapesToggle.click();
     await onHighlighterHidden;
   }
 }
 
 /**
  * Expand the provided markup container programatically and  wait for all children to
  * update.
  */
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -836,17 +836,18 @@ class ShapesHighlighter extends AutoRefr
       let [newX, newY] = apply(this.transformMatrix, vector);
       let precisionX = getDecimalPrecision(unitX);
       let precisionY = getDecimalPrecision(unitY);
       newX = (newX * ratioX).toFixed(precisionX);
       newY = (newY * ratioY).toFixed(precisionY);
 
       return `${newX}${unitX} ${newY}${unitY}`;
     }).join(", ");
-    polygonDef = `polygon(${polygonDef}) ${this.geometryBox}`.trim();
+    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
+                                      `polygon(${polygonDef})`;
 
     this.currentNode.style.setProperty(this.property, polygonDef, "important");
   }
 
   /**
    * Transform a circle depending on the current transformation matrix.
    * @param {Number} transX the number of pixels the shape is translated on the x axis
    *                 before scaling
@@ -859,19 +860,21 @@ class ShapesHighlighter extends AutoRefr
     let [newCx, newCy] = apply(this.transformMatrix, [valueX / ratioX, valueY / ratioY]);
     if (transX !== null) {
       // As part of scaling, the shape is translated to be tangent to the line y=0.
       // To get the new radius, we translate the new cx back to that point and get
       // the distance to the line y=0.
       radius = `${Math.abs((newCx - transX) * ratioRad)}${unitRad}`;
     }
 
-    let circleDef = `circle(${radius} at ${newCx * ratioX}${unitX} ` +
-        `${newCy * ratioY}${unitY}) ${this.geometryBox}`.trim();
-    this.emit("highlighter-event", { type: "shape-change", value: circleDef });
+    let circleDef = (this.geometryBox) ?
+      `circle(${radius} at ${newCx * ratioX}${unitX} ` +
+        `${newCy * ratioY}${unitY} ${this.geometryBox}` :
+      `circle(${radius} at ${newCx * ratioX}${unitX} ${newCy * ratioY}${unitY}`;
+    this.currentNode.style.setProperty(this.property, circleDef, "important");
   }
 
   /**
    * Transform an ellipse depending on the current transformation matrix.
    * @param {Number} transX the number of pixels the shape is translated on the x axis
    *                 before scaling
    * @param {Number} transY the number of pixels the shape is translated on the y axis
    *                 before scaling
@@ -885,19 +888,22 @@ class ShapesHighlighter extends AutoRefr
     if (transX !== null && transY !== null) {
       // As part of scaling, the shape is translated to be tangent to the lines y=0 & x=0.
       // To get the new radii, we translate the new center back to that point and get the
       // distances to the line x=0 and y=0.
       rx = `${Math.abs((newCx - transX) * ratioRX)}${unitRX}`;
       ry = `${Math.abs((newCy - transY) * ratioRY)}${unitRY}`;
     }
 
-    let ellipseDef = `ellipse(${rx} ${ry} at ${newCx * ratioX}${unitX} ` +
-          `${newCy * ratioY}${unitY}) ${this.geometryBox}`.trim();
-    this.emit("highlighter-event", { type: "shape-change", value: ellipseDef });
+    let ellipseDef = (this.geometryBox) ?
+        `ellipse(${rx} ${ry} at ${newCx * ratioX}${unitX} ` +
+          `${newCy * ratioY}${unitY}) ${this.geometryBox}` :
+        `ellipse(${rx} ${ry} at ${newCx * ratioX}${unitX} ` +
+          `${newCy * ratioY}${unitY})`;
+    this.currentNode.style.setProperty(this.property, ellipseDef, "important");
   }
 
   /**
    * Transform an inset depending on the current transformation matrix.
    */
   _transformInset() {
     let { top, left, right, bottom } = this[_dragging].pointsInfo;
     let { width, height } = this.currentDimensions;
@@ -969,17 +975,18 @@ class ShapesHighlighter extends AutoRefr
     let newX = (valueX + deltaX).toFixed(precisionX);
     let newY = (valueY + deltaY).toFixed(precisionY);
 
     let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
     polygonDef += this.coordUnits.map((coords, i) => {
       return (i === point) ?
         `${newX}${unitX} ${newY}${unitY}` : `${coords[0]} ${coords[1]}`;
     }).join(", ");
-    polygonDef = `polygon(${polygonDef}) ${this.geometryBox}`.trim();
+    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
+                                      `polygon(${polygonDef})`;
 
     this.currentNode.style.setProperty(this.property, polygonDef, "important");
   }
 
   /**
    * Set the inline style of the polygon, adding a new point.
    * TODO: Bug 1436054 - Do not default to percentage unit when inserting new point.
    * https://bugzilla.mozilla.org/show_bug.cgi?id=1436054
@@ -989,17 +996,18 @@ class ShapesHighlighter extends AutoRefr
    * @param {Number} y the y coordinate of the new point
    */
   _addPolygonPoint(after, x, y) {
     let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
     polygonDef += this.coordUnits.map((coords, i) => {
       return (i === after) ? `${coords[0]} ${coords[1]}, ${x}% ${y}%` :
                              `${coords[0]} ${coords[1]}`;
     }).join(", ");
-    polygonDef = `polygon(${polygonDef}) ${this.geometryBox}`.trim();
+    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
+                                      `polygon(${polygonDef})`;
 
     this.hoveredPoint = after + 1;
     this._emitHoverEvent(this.hoveredPoint);
     this.currentNode.style.setProperty(this.property, polygonDef, "important");
   }
 
   /**
    * Set the inline style of the polygon, deleting the given point.
@@ -1007,17 +1015,18 @@ class ShapesHighlighter extends AutoRefr
    */
   _deletePolygonPoint(point) {
     let coordinates = this.coordUnits.slice();
     coordinates.splice(point, 1);
     let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
     polygonDef += coordinates.map((coords, i) => {
       return `${coords[0]} ${coords[1]}`;
     }).join(", ");
-    polygonDef = `polygon(${polygonDef}) ${this.geometryBox}`.trim();
+    polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
+                                      `polygon(${polygonDef})`;
 
     this.hoveredPoint = null;
     this._emitHoverEvent(this.hoveredPoint);
     this.currentNode.style.setProperty(this.property, polygonDef, "important");
   }
   /**
    * Handle a click when highlighting a circle.
    * @param {Number} pageX the x coordinate of the click
@@ -1072,31 +1081,34 @@ class ShapesHighlighter extends AutoRefr
     let { radius, cx, cy } = this.coordUnits;
 
     if (point === "center") {
       let { unitX, unitY, valueX, valueY, ratioX, ratioY, x, y} = this[_dragging];
       let deltaX = (pageX - x) * ratioX;
       let deltaY = (pageY - y) * ratioY;
       let newCx = `${valueX + deltaX}${unitX}`;
       let newCy = `${valueY + deltaY}${unitY}`;
-      // if not defined by the user, geometryBox will be an empty string; trim() cleans up
-      let circleDef = `circle(${radius} at ${newCx} ${newCy}) ${this.geometryBox}`.trim();
+      let circleDef = (this.geometryBox) ?
+            `circle(${radius} at ${newCx} ${newCy}) ${this.geometryBox}` :
+            `circle(${radius} at ${newCx} ${newCy})`;
 
       this.currentNode.style.setProperty(this.property, circleDef, "important");
     } else if (point === "radius") {
       let { value, unit, origRadius, ratio } = this[_dragging];
       // convert center point to px, then get distance between center and mouse.
       let { x: pageCx, y: pageCy } = this.convertPercentToPageCoords(this.coordinates.cx,
                                                                      this.coordinates.cy);
       let newRadiusPx = getDistance(pageCx, pageCy, pageX, pageY);
 
       let delta = (newRadiusPx - origRadius) * ratio;
       let newRadius = `${value + delta}${unit}`;
 
-      let circleDef = `circle(${newRadius} at ${cx} ${cy}) ${this.geometryBox}`.trim();
+      let circleDef = (this.geometryBox) ?
+                      `circle(${newRadius} at ${cx} ${cy} ${this.geometryBox}` :
+                      `circle(${newRadius} at ${cx} ${cy}`;
 
       this.currentNode.style.setProperty(this.property, circleDef, "important");
     }
   }
 
   /**
    * Handle a click when highlighting an ellipse.
    * @param {Number} pageX the x coordinate of the click
@@ -1160,40 +1172,43 @@ class ShapesHighlighter extends AutoRefr
     let { rx, ry, cx, cy } = this.coordUnits;
 
     if (point === "center") {
       let { unitX, unitY, valueX, valueY, ratioX, ratioY, x, y} = this[_dragging];
       let deltaX = (pageX - x) * ratioX;
       let deltaY = (pageY - y) * ratioY;
       let newCx = `${valueX + deltaX}${unitX}`;
       let newCy = `${valueY + deltaY}${unitY}`;
-      let ellipseDef =
-        `ellipse(${rx} ${ry} at ${newCx} ${newCy}) ${this.geometryBox}`.trim();
+      let ellipseDef = (this.geometryBox) ?
+        `ellipse(${rx} ${ry} at ${newCx} ${newCy}) ${this.geometryBox}` :
+        `ellipse(${rx} ${ry} at ${newCx} ${newCy})`;
 
       this.currentNode.style.setProperty(this.property, ellipseDef, "important");
     } else if (point === "rx") {
       let { value, unit, origRadius, ratio } = this[_dragging];
       let newRadiusPercent = Math.abs(percentX - this.coordinates.cx);
       let { width } = this.currentDimensions;
       let delta = ((newRadiusPercent / 100 * width) - origRadius) * ratio;
       let newRadius = `${value + delta}${unit}`;
 
-      let ellipseDef =
-        `ellipse(${newRadius} ${ry} at ${cx} ${cy}) ${this.geometryBox}`.trim();
+      let ellipseDef = (this.geometryBox) ?
+        `ellipse(${newRadius} ${ry} at ${cx} ${cy}) ${this.geometryBox}` :
+        `ellipse(${newRadius} ${ry} at ${cx} ${cy})`;
 
       this.currentNode.style.setProperty(this.property, ellipseDef, "important");
     } else if (point === "ry") {
       let { value, unit, origRadius, ratio } = this[_dragging];
       let newRadiusPercent = Math.abs(percentY - this.coordinates.cy);
       let { height } = this.currentDimensions;
       let delta = ((newRadiusPercent / 100 * height) - origRadius) * ratio;
       let newRadius = `${value + delta}${unit}`;
 
-      let ellipseDef =
-        `ellipse(${rx} ${newRadius} at ${cx} ${cy}) ${this.geometryBox}`.trim();
+      let ellipseDef = (this.geometryBox) ?
+        `ellipse(${rx} ${newRadius} at ${cx} ${cy}) ${this.geometryBox}` :
+        `ellipse(${rx} ${newRadius} at ${cx} ${cy})`;
 
       this.currentNode.style.setProperty(this.property, ellipseDef, "important");
     }
   }
 
   /**
    * Handle a click when highlighting an inset.
    * @param {Number} pageX the x coordinate of the click
--- a/devtools/server/actors/utils/shapes-utils.js
+++ b/devtools/server/actors/utils/shapes-utils.js
@@ -4,17 +4,17 @@
  * Get the distance between two points on a plane.
  * @param {Number} x1 the x coord of the first point
  * @param {Number} y1 the y coord of the first point
  * @param {Number} x2 the x coord of the second point
  * @param {Number} y2 the y coord of the second point
  * @returns {Number} the distance between the two points
  */
 const getDistance = (x1, y1, x2, y2) => {
-  return Math.round(Math.hypot(x2 - x1, y2 - y1));
+  return Math.hypot(x2 - x1, y2 - y1);
 };
 
 /**
  * Determine if the given x/y coords are along the edge of the given ellipse.
  * We allow for a small area around the edge that still counts as being on the edge.
  * @param {Number} x the x coordinate of the click
  * @param {Number} y the y coordinate of the click
  * @param {Number} cx the x coordinate of the center of the ellipse