Bug 1262439 - 7 - Delete the old eyedropper implementation; r=ochameau draft
authorPatrick Brosset <pbrosset@mozilla.com>
Thu, 09 Jun 2016 13:33:27 +0200
changeset 385013 e770b7c1203c6488cc8db56b55f350ea9890085a
parent 385012 c7deaa4cbe06bac70cac7c15ae2688b6bd49edbd
child 385014 1aa0d4db036a145c7304505f7d58b0ece3b3f5a1
push id22388
push userpbrosset@mozilla.com
push dateThu, 07 Jul 2016 13:03:20 +0000
reviewersochameau
bugs1262439
milestone50.0a1
Bug 1262439 - 7 - Delete the old eyedropper implementation; r=ochameau MozReview-Commit-ID: FFrbWHiA8f3
devtools/client/eyedropper/crosshairs.css
devtools/client/eyedropper/eyedropper-child.js
devtools/client/eyedropper/eyedropper.js
devtools/client/eyedropper/eyedropper.xul
devtools/client/eyedropper/moz.build
devtools/client/eyedropper/nocursor.css
devtools/client/eyedropper/test/.eslintrc
devtools/client/eyedropper/test/browser.ini
devtools/client/eyedropper/test/browser_eyedropper_basic.js
devtools/client/eyedropper/test/browser_eyedropper_cmd.js
devtools/client/eyedropper/test/color-block.html
devtools/client/eyedropper/test/head.js
devtools/client/jar.mn
devtools/client/moz.build
deleted file mode 100644
--- a/devtools/client/eyedropper/crosshairs.css
+++ /dev/null
@@ -1,3 +0,0 @@
-* {
-  cursor: crosshair !important;
-}
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/eyedropper/eyedropper-child.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* 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/. */
-
-var { interfaces: Ci } = Components;
-
-addMessageListener("Eyedropper:RequestContentScreenshot", sendContentScreenshot);
-
-function sendContentScreenshot() {
-  let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-  let scale = content.getInterface(Ci.nsIDOMWindowUtils).fullZoom;
-  let width = content.innerWidth;
-  let height = content.innerHeight;
-  canvas.width = width * scale;
-  canvas.height = height * scale;
-  canvas.mozOpaque = true;
-
-  let ctx = canvas.getContext("2d");
-
-  ctx.scale(scale, scale);
-  ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff");
-
-  sendAsyncMessage("Eyedropper:Screenshot", canvas.toDataURL());
-}
deleted file mode 100644
--- a/devtools/client/eyedropper/eyedropper.js
+++ /dev/null
@@ -1,841 +0,0 @@
-/* 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 {rgbToHsl, rgbToColorName} = require("devtools/shared/css-color").colorUtils;
-const {Cc, Ci} = require("chrome");
-const Telemetry = require("devtools/client/shared/telemetry");
-const EventEmitter = require("devtools/shared/event-emitter");
-const promise = require("promise");
-const defer = require("devtools/shared/defer");
-const Services = require("Services");
-
-loader.lazyGetter(this, "clipboardHelper", function () {
-  return Cc["@mozilla.org/widget/clipboardhelper;1"]
-    .getService(Ci.nsIClipboardHelper);
-});
-
-loader.lazyGetter(this, "ssService", function () {
-  return Cc["@mozilla.org/content/style-sheet-service;1"]
-    .getService(Ci.nsIStyleSheetService);
-});
-
-loader.lazyGetter(this, "ioService", function () {
-  return Cc["@mozilla.org/network/io-service;1"]
-    .getService(Ci.nsIIOService);
-});
-
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
-
-loader.lazyGetter(this, "XULRuntime", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
-});
-
-loader.lazyGetter(this, "l10n", () => Services.strings
-  .createBundle("chrome://devtools/locale/eyedropper.properties"));
-
-const EYEDROPPER_URL = "chrome://devtools/content/eyedropper/eyedropper.xul";
-const CROSSHAIRS_URL = "chrome://devtools/content/eyedropper/crosshairs.css";
-const NOCURSOR_URL = "chrome://devtools/content/eyedropper/nocursor.css";
-
-const ZOOM_PREF = "devtools.eyedropper.zoom";
-const FORMAT_PREF = "devtools.defaultColorUnit";
-
-const CANVAS_WIDTH = 96;
-const CANVAS_OFFSET = 3; // equals the border width of the canvas.
-const CLOSE_DELAY = 750;
-
-const HEX_BOX_WIDTH = CANVAS_WIDTH + CANVAS_OFFSET * 2;
-const HSL_BOX_WIDTH = 158;
-
-/**
- * Manage instances of eyedroppers for windows. Registering here isn't
- * necessary for creating an eyedropper, but can be used for testing.
- */
-var EyedropperManager = {
-  _instances: new WeakMap(),
-
-  getInstance: function (chromeWindow) {
-    return this._instances.get(chromeWindow);
-  },
-
-  createInstance: function (chromeWindow, options) {
-    let dropper = this.getInstance(chromeWindow);
-    if (dropper) {
-      return dropper;
-    }
-
-    dropper = new Eyedropper(chromeWindow, options);
-    this._instances.set(chromeWindow, dropper);
-
-    dropper.on("destroy", () => {
-      this.deleteInstance(chromeWindow);
-    });
-
-    return dropper;
-  },
-
-  deleteInstance: function (chromeWindow) {
-    this._instances.delete(chromeWindow);
-  }
-};
-
-exports.EyedropperManager = EyedropperManager;
-
-/**
- * Eyedropper widget. Once opened, shows zoomed area above current pixel and
- * displays the color value of the center pixel. Clicking on the window will
- * close the widget and fire a 'select' event. If 'copyOnSelect' is true, the color
- * will also be copied to the clipboard.
- *
- * let eyedropper = new Eyedropper(window);
- * eyedropper.open();
- *
- * eyedropper.once("select", (ev, color) => {
- *   console.log(color);  // "rgb(20, 50, 230)"
- * })
- *
- * @param {DOMWindow} chromeWindow
- *        window to inspect
- * @param {object} opts
- *        optional options object, with 'copyOnSelect', 'context'
- */
-function Eyedropper(chromeWindow, opts = { copyOnSelect: true, context: "other" }) {
-  this.copyOnSelect = opts.copyOnSelect;
-
-  this._onFirstMouseMove = this._onFirstMouseMove.bind(this);
-  this._onMouseMove = this._onMouseMove.bind(this);
-  this._onMouseDown = this._onMouseDown.bind(this);
-  this._onKeyDown = this._onKeyDown.bind(this);
-  this._onFrameLoaded = this._onFrameLoaded.bind(this);
-
-  this._chromeWindow = chromeWindow;
-  this._chromeDocument = chromeWindow.document;
-
-  this._OS = XULRuntime.OS;
-
-  this._dragging = true;
-  this.loaded = false;
-
-  this._mouseMoveCounter = 0;
-
-  this.format = Services.prefs.getCharPref(FORMAT_PREF); // color value format
-  this.zoom = Services.prefs.getIntPref(ZOOM_PREF);      // zoom level - integer
-
-  this._zoomArea = {
-    x: 0,          // the left coordinate of the center of the inspected region
-    y: 0,          // the top coordinate of the center of the inspected region
-    width: CANVAS_WIDTH,      // width of canvas to draw zoomed area onto
-    height: CANVAS_WIDTH      // height of canvas
-  };
-
-  if (this._contentTab) {
-    let mm = this._contentTab.linkedBrowser.messageManager;
-    mm.loadFrameScript("resource://devtools/client/eyedropper/eyedropper-child.js", true);
-  }
-
-  // record if this was opened via the picker or standalone
-  var telemetry = new Telemetry();
-  if (opts.context == "command") {
-    telemetry.toolOpened("eyedropper");
-  }
-  else if (opts.context == "menu") {
-    telemetry.toolOpened("menueyedropper");
-  }
-  else if (opts.context == "picker") {
-    telemetry.toolOpened("pickereyedropper");
-  }
-
-  EventEmitter.decorate(this);
-}
-
-exports.Eyedropper = Eyedropper;
-
-Eyedropper.prototype = {
-  /**
-   * Get the number of cells (blown-up pixels) per direction in the grid.
-   */
-  get cellsWide() {
-    // Canvas will render whole "pixels" (cells) only, and an even
-    // number at that. Round up to the nearest even number of pixels.
-    let cellsWide = Math.ceil(this._zoomArea.width / this.zoom);
-    cellsWide += cellsWide % 2;
-
-    return cellsWide;
-  },
-
-  /**
-   * Get the size of each cell (blown-up pixel) in the grid.
-   */
-  get cellSize() {
-    return this._zoomArea.width / this.cellsWide;
-  },
-
-  /**
-   * Get index of cell in the center of the grid.
-   */
-  get centerCell() {
-    return Math.floor(this.cellsWide / 2);
-  },
-
-  /**
-   * Get color of center cell in the grid.
-   */
-  get centerColor() {
-    let x, y;
-    x = y = (this.centerCell * this.cellSize) + (this.cellSize / 2);
-    let rgb = this._ctx.getImageData(x, y, 1, 1).data;
-    return rgb;
-  },
-
-  get _contentTab() {
-    return this._chromeWindow.gBrowser && this._chromeWindow.gBrowser.selectedTab;
-  },
-
-  /**
-   * Fetch a screenshot of the content.
-   *
-   * @return {promise}
-   *         Promise that resolves with the screenshot as a dataURL
-   */
-  getContentScreenshot: function () {
-    if (!this._contentTab) {
-        return promise.resolve(null);
-    }
-
-    let deferred = defer();
-
-    let mm = this._contentTab.linkedBrowser.messageManager;
-    function onScreenshot(message) {
-      mm.removeMessageListener("Eyedropper:Screenshot", onScreenshot);
-      deferred.resolve(message.data);
-    }
-    mm.addMessageListener("Eyedropper:Screenshot", onScreenshot);
-    mm.sendAsyncMessage("Eyedropper:RequestContentScreenshot");
-
-    return deferred.promise;
-  },
-
-  /**
-   * Start the eyedropper. Add listeners for a mouse move in the window to
-   * show the eyedropper.
-   */
-  open: function () {
-    if (this.isOpen) {
-      // the eyedropper is aready open, don't create another panel.
-      return promise.resolve();
-    }
-
-    this.isOpen = true;
-
-    this._showCrosshairs();
-
-    // Get screenshot of content so we can inspect colors
-    return this.getContentScreenshot().then((dataURL) => {
-      // The data url may be null, e.g. if there is no content tab
-      if (dataURL) {
-        this._contentImage = new this._chromeWindow.Image();
-        this._contentImage.src = dataURL;
-
-        // Wait for screenshot to load
-        let imageLoaded = promise.defer();
-        this._contentImage.onload = imageLoaded.resolve
-        return imageLoaded.promise;
-      }
-    }).then(() => {
-      // Then start showing the eyedropper UI
-      this._chromeDocument.addEventListener("mousemove", this._onFirstMouseMove);
-      this.isStarted = true;
-      this.emit("started");
-    });
-  },
-
-  /**
-   * Called on the first mouse move over the window. Opens the eyedropper
-   * panel where the mouse is.
-   */
-  _onFirstMouseMove: function (event) {
-    this._chromeDocument.removeEventListener("mousemove", this._onFirstMouseMove);
-
-    this._panel = this._buildPanel();
-
-    let popupSet = this._chromeDocument.querySelector("#mainPopupSet");
-    popupSet.appendChild(this._panel);
-
-    let { panelX, panelY } = this._getPanelCoordinates(event);
-    this._panel.openPopupAtScreen(panelX, panelY);
-
-    this._setCoordinates(event);
-
-    this._addListeners();
-
-    // hide cursor as we'll be showing the panel over the mouse instead.
-    this._hideCrosshairs();
-    this._hideCursor();
-  },
-
-  /**
-   * Whether the coordinates are over the content or chrome.
-   *
-   * @param {number} clientX
-   *        x-coordinate of mouse relative to browser window.
-   * @param {number} clientY
-   *        y-coordinate of mouse relative to browser window.
-   */
-  _isInContent: function (clientX, clientY) {
-    let box = this._contentTab && this._contentTab.linkedBrowser.getBoundingClientRect();
-    if (box &&
-        clientX > box.left &&
-        clientX < box.right &&
-        clientY > box.top &&
-        clientY < box.bottom) {
-      return true;
-    }
-    return false;
-  },
-
-  /**
-   * Set the current coordinates to inspect from where a mousemove originated.
-   *
-   * @param {MouseEvent} event
-   *        Event for the mouse move.
-   */
-  _setCoordinates: function (event) {
-    let inContent = this._isInContent(event.clientX, event.clientY);
-    let win = this._chromeWindow;
-
-    // offset of mouse from browser window
-    let x = event.clientX;
-    let y = event.clientY;
-
-    if (inContent) {
-      // calculate the offset of the mouse from the content window
-      let box = this._contentTab.linkedBrowser.getBoundingClientRect();
-      x = x - box.left;
-      y = y - box.top;
-
-      this._zoomArea.contentWidth = box.width;
-      this._zoomArea.contentHeight = box.height;
-    }
-    this._zoomArea.inContent = inContent;
-
-    // don't let it inspect outside the browser window
-    x = Math.max(0, Math.min(x, win.outerWidth - 1));
-    y = Math.max(0, Math.min(y, win.outerHeight - 1));
-
-    this._zoomArea.x = x;
-    this._zoomArea.y = y;
-  },
-
-  /**
-   * Build and add a new eyedropper panel to the window.
-   *
-   * @return {Panel}
-   *         The XUL panel holding the eyedropper UI.
-   */
-  _buildPanel: function () {
-    let panel = this._chromeDocument.createElement("panel");
-    panel.setAttribute("noautofocus", true);
-    panel.setAttribute("noautohide", true);
-    panel.setAttribute("level", "floating");
-    panel.setAttribute("class", "devtools-eyedropper-panel");
-
-    let iframe = this._iframe = this._chromeDocument.createElement("iframe");
-    iframe.addEventListener("load", this._onFrameLoaded, true);
-    iframe.setAttribute("flex", "1");
-    iframe.setAttribute("transparent", "transparent");
-    iframe.setAttribute("allowTransparency", true);
-    iframe.setAttribute("class", "devtools-eyedropper-iframe");
-    iframe.setAttribute("src", EYEDROPPER_URL);
-    iframe.setAttribute("width", CANVAS_WIDTH);
-    iframe.setAttribute("height", CANVAS_WIDTH);
-
-    panel.appendChild(iframe);
-
-    return panel;
-  },
-
-  /**
-   * Event handler for the panel's iframe's load event. Emits
-   * a "load" event from this eyedropper object.
-   */
-  _onFrameLoaded: function () {
-    this._iframe.removeEventListener("load", this._onFrameLoaded, true);
-
-    this._iframeDocument = this._iframe.contentDocument;
-    this._colorPreview = this._iframeDocument.querySelector("#color-preview");
-    this._colorValue = this._iframeDocument.querySelector("#color-value");
-
-    // value box will be too long for hex values and too short for hsl
-    let valueBox = this._iframeDocument.querySelector("#color-value-box");
-    if (this.format == "hex") {
-      valueBox.style.width = HEX_BOX_WIDTH + "px";
-    }
-    else if (this.format == "hsl") {
-      valueBox.style.width = HSL_BOX_WIDTH + "px";
-    }
-
-    this._canvas = this._iframeDocument.querySelector("#canvas");
-    this._ctx = this._canvas.getContext("2d");
-
-    // so we preserve the clear pixel boundaries
-    this._ctx.mozImageSmoothingEnabled = false;
-
-    this._drawWindow();
-
-    this._addPanelListeners();
-    this._iframe.focus();
-
-    this.loaded = true;
-    this.emit("load");
-  },
-
-  /**
-   * Add key listeners to the panel.
-   */
-  _addPanelListeners: function () {
-    this._iframeDocument.addEventListener("keydown", this._onKeyDown);
-
-    let closeCmd = this._iframeDocument.getElementById("eyedropper-cmd-close");
-    closeCmd.addEventListener("command", this.destroy.bind(this), true);
-
-    let copyCmd = this._iframeDocument.getElementById("eyedropper-cmd-copy");
-    copyCmd.addEventListener("command", this.selectColor.bind(this), true);
-  },
-
-  /**
-   * Remove listeners from the panel.
-   */
-  _removePanelListeners: function () {
-    this._iframeDocument.removeEventListener("keydown", this._onKeyDown);
-  },
-
-  /**
-   * Add mouse event listeners to the document we're inspecting.
-   */
-  _addListeners: function () {
-    this._chromeDocument.addEventListener("mousemove", this._onMouseMove);
-    this._chromeDocument.addEventListener("mousedown", this._onMouseDown);
-  },
-
-  /**
-   * Remove mouse event listeners from the document we're inspecting.
-   */
-  _removeListeners: function () {
-    this._chromeDocument.removeEventListener("mousemove", this._onFirstMouseMove);
-    this._chromeDocument.removeEventListener("mousemove", this._onMouseMove);
-    this._chromeDocument.removeEventListener("mousedown", this._onMouseDown);
-  },
-
-  /**
-   * Hide the cursor.
-   */
-  _hideCursor: function () {
-    registerStyleSheet(NOCURSOR_URL);
-  },
-
-  /**
-   * Reset the cursor back to default.
-   */
-  _resetCursor: function () {
-    unregisterStyleSheet(NOCURSOR_URL);
-  },
-
-  /**
-   * Show a crosshairs as the mouse cursor
-   */
-  _showCrosshairs: function () {
-    registerStyleSheet(CROSSHAIRS_URL);
-  },
-
-  /**
-   * Reset cursor.
-   */
-  _hideCrosshairs: function () {
-    unregisterStyleSheet(CROSSHAIRS_URL);
-  },
-
-  /**
-   * Event handler for a mouse move over the page we're inspecting.
-   * Preview the area under the cursor, and move panel to be under the cursor.
-   *
-   * @param  {DOMEvent} event
-   *         MouseEvent for the mouse moving
-   */
-  _onMouseMove: function (event) {
-    if (!this._dragging || !this._panel || !this._canvas) {
-      return;
-    }
-
-    if (this._OS == "Linux" && ++this._mouseMoveCounter % 2 == 0) {
-      // skip every other mousemove to preserve performance.
-      return;
-    }
-
-    this._setCoordinates(event);
-    this._drawWindow();
-
-    let { panelX, panelY } = this._getPanelCoordinates(event);
-    this._movePanel(panelX, panelY);
-  },
-
-  /**
-   * Get coordinates of where the eyedropper panel should go based on
-   * the current coordinates of the mouse cursor.
-   *
-   * @param {MouseEvent} event
-   *        object with properties 'screenX' and 'screenY'
-   *
-   * @return {object}
-  *          object with properties 'panelX', 'panelY'
-   */
-  _getPanelCoordinates: function ({screenX, screenY}) {
-    let win = this._chromeWindow;
-    let offset = CANVAS_WIDTH / 2 + CANVAS_OFFSET;
-
-    let panelX = screenX - offset;
-    let windowX = win.screenX + (win.outerWidth - win.innerWidth);
-    let maxX = win.screenX + win.outerWidth - offset - 1;
-
-    let panelY = screenY - offset;
-    let windowY = win.screenY + (win.outerHeight - win.innerHeight);
-    let maxY = win.screenY + win.outerHeight - offset - 1;
-
-    // don't let the panel move outside the browser window
-    panelX = Math.max(windowX - offset, Math.min(panelX, maxX));
-    panelY = Math.max(windowY - offset, Math.min(panelY, maxY));
-
-    return { panelX: panelX, panelY: panelY };
-  },
-
-  /**
-   * Move the eyedropper panel to the given coordinates.
-   *
-   * @param  {number} screenX
-   *         left coordinate on the screen
-   * @param  {number} screenY
-   *         top coordinate
-   */
-  _movePanel: function (screenX, screenY) {
-    this._panelX = screenX;
-    this._panelY = screenY;
-
-    this._panel.moveTo(screenX, screenY);
-  },
-
-  /**
-   * Handler for the mouse down event on the inspected page. This means a
-   * click, so we'll select the color that's currently hovered.
-   *
-   * @param  {Event} event
-   *         DOM MouseEvent object
-   */
-  _onMouseDown: function (event) {
-    event.preventDefault();
-    event.stopPropagation();
-
-    this.selectColor();
-  },
-
-  /**
-   * Select the current color that's being previewed. Fire a
-   * "select" event with the color as an rgb string.
-   */
-  selectColor: function () {
-    if (this._isSelecting) {
-      return;
-    }
-    this._isSelecting = true;
-    this._dragging = false;
-
-    this.emit("select", this._colorValue.value);
-
-    if (this.copyOnSelect) {
-      this.copyColor(this.destroy.bind(this));
-    }
-    else {
-      this.destroy();
-    }
-  },
-
-  /**
-   * Copy the currently inspected color to the clipboard.
-   *
-   * @param  {Function} callback
-   *         Callback to be called when the color is in the clipboard.
-   */
-  copyColor: function (callback) {
-    clearTimeout(this._copyTimeout);
-
-    let color = this._colorValue.value;
-    clipboardHelper.copyString(color);
-
-    this._colorValue.classList.add("highlight");
-    this._colorValue.value = "✓ " + l10n.GetStringFromName("colorValue.copied");
-
-    this._copyTimeout = setTimeout(() => {
-      this._colorValue.classList.remove("highlight");
-      this._colorValue.value = color;
-
-      if (callback) {
-        callback();
-      }
-    }, CLOSE_DELAY);
-  },
-
-  /**
-   * Handler for the keydown event on the panel. Either copy the color
-   * or move the panel in a direction depending on the key pressed.
-   *
-   * @param  {Event} event
-   *         DOM KeyboardEvent object
-   */
-  _onKeyDown: function (event) {
-    if (event.metaKey && event.keyCode === event.DOM_VK_C) {
-      this.copyColor();
-      return;
-    }
-
-    let offsetX = 0;
-    let offsetY = 0;
-    let modifier = 1;
-
-    if (event.keyCode === event.DOM_VK_LEFT) {
-      offsetX = -1;
-    }
-    if (event.keyCode === event.DOM_VK_RIGHT) {
-      offsetX = 1;
-    }
-    if (event.keyCode === event.DOM_VK_UP) {
-      offsetY = -1;
-    }
-    if (event.keyCode === event.DOM_VK_DOWN) {
-      offsetY = 1;
-    }
-    if (event.shiftKey) {
-      modifier = 10;
-    }
-
-    offsetY *= modifier;
-    offsetX *= modifier;
-
-    if (offsetX !== 0 || offsetY !== 0) {
-      this._zoomArea.x += offsetX;
-      this._zoomArea.y += offsetY;
-
-      this._drawWindow();
-
-      this._movePanel(this._panelX + offsetX, this._panelY + offsetY);
-
-      event.preventDefault();
-    }
-  },
-
-  /**
-   * Draw the inspected area onto the canvas using the zoom level.
-   */
-  _drawWindow: function () {
-    let { width, height, x, y, inContent,
-          contentWidth, contentHeight } = this._zoomArea;
-
-    let zoomedWidth = width / this.zoom;
-    let zoomedHeight = height / this.zoom;
-
-    let leftX = x - (zoomedWidth / 2);
-    let topY = y - (zoomedHeight / 2);
-
-    // draw the portion of the window we're inspecting
-    if (inContent) {
-      // draw from content source image "s" to destination rect "d"
-      let sx = leftX;
-      let sy = topY;
-      let sw = zoomedWidth;
-      let sh = zoomedHeight;
-      let dx = 0;
-      let dy = 0;
-
-      // we're at the content edge, so we have to crop the drawing
-      if (leftX < 0) {
-        sx = 0;
-        sw = zoomedWidth + leftX;
-        dx = -leftX;
-      }
-      else if (leftX + zoomedWidth > contentWidth) {
-        sw = contentWidth - leftX;
-      }
-      if (topY < 0) {
-        sy = 0;
-        sh = zoomedHeight + topY;
-        dy = -topY;
-      }
-      else if (topY + zoomedHeight > contentHeight) {
-        sh = contentHeight - topY;
-      }
-      let dw = sw;
-      let dh = sh;
-
-      // we don't want artifacts when we're inspecting the edges of content
-      if (leftX < 0 || topY < 0 ||
-          leftX + zoomedWidth > contentWidth ||
-          topY + zoomedHeight > contentHeight) {
-        this._ctx.fillStyle = "white";
-        this._ctx.fillRect(0, 0, width, height);
-      }
-
-      // draw from the screenshot to the eyedropper canvas
-      this._ctx.drawImage(this._contentImage, sx, sy, sw,
-                          sh, dx, dy, dw, dh);
-    }
-    else {
-      // the mouse is over the chrome, so draw that instead of the content
-      this._ctx.drawWindow(this._chromeWindow, leftX, topY, zoomedWidth,
-                           zoomedHeight, "white");
-    }
-
-    // now scale it
-    this._ctx.drawImage(this._canvas, 0, 0, zoomedWidth, zoomedHeight,
-                                      0, 0, width, height);
-
-    let rgb = this.centerColor;
-    this._colorPreview.style.backgroundColor = toColorString(rgb, "rgb");
-    this._colorValue.value = toColorString(rgb, this.format);
-
-    if (this.zoom > 2) {
-      // grid at 2x is too busy
-      this._drawGrid();
-    }
-    this._drawCrosshair();
-  },
-
-  /**
-   * Draw a grid on the canvas representing pixel boundaries.
-   */
-  _drawGrid: function () {
-    let { width, height } = this._zoomArea;
-
-    this._ctx.lineWidth = 1;
-    this._ctx.strokeStyle = "rgba(143, 143, 143, 0.2)";
-
-    for (let i = 0; i < width; i += this.cellSize) {
-      this._ctx.beginPath();
-      this._ctx.moveTo(i - .5, 0);
-      this._ctx.lineTo(i - .5, height);
-      this._ctx.stroke();
-
-      this._ctx.beginPath();
-      this._ctx.moveTo(0, i - .5);
-      this._ctx.lineTo(width, i - .5);
-      this._ctx.stroke();
-    }
-  },
-
-  /**
-   * Draw a box on the canvas to highlight the center cell.
-   */
-  _drawCrosshair: function () {
-    let x, y;
-    x = y = this.centerCell * this.cellSize;
-
-    this._ctx.lineWidth = 1;
-    this._ctx.lineJoin = "miter";
-    this._ctx.strokeStyle = "rgba(0, 0, 0, 1)";
-    this._ctx.strokeRect(x - 1.5, y - 1.5, this.cellSize + 2, this.cellSize + 2);
-
-    this._ctx.strokeStyle = "rgba(255, 255, 255, 1)";
-    this._ctx.strokeRect(x - 0.5, y - 0.5, this.cellSize, this.cellSize);
-  },
-
-  /**
-   * Destroy the eyedropper and clean up. Emits a "destroy" event.
-   */
-  destroy: function () {
-    this._resetCursor();
-    this._hideCrosshairs();
-
-    if (this._panel) {
-      this._panel.hidePopup();
-      this._panel.remove();
-      this._panel = null;
-    }
-    this._removePanelListeners();
-    this._removeListeners();
-
-    this.isStarted = false;
-    this.isOpen = false;
-    this._isSelecting = false;
-
-    this.emit("destroy");
-  }
-};
-
-/**
- * Add a user style sheet that applies to all documents.
- */
-function registerStyleSheet(url) {
-  var uri = ioService.newURI(url, null, null);
-  if (!ssService.sheetRegistered(uri, ssService.AGENT_SHEET)) {
-    ssService.loadAndRegisterSheet(uri, ssService.AGENT_SHEET);
-  }
-}
-
-/**
- * Remove a user style sheet.
- */
-function unregisterStyleSheet(url) {
-  var uri = ioService.newURI(url, null, null);
-  if (ssService.sheetRegistered(uri, ssService.AGENT_SHEET)) {
-    ssService.unregisterSheet(uri, ssService.AGENT_SHEET);
-  }
-}
-
-/**
- * Get a formatted CSS color string from a color value.
- *
- * @param {array} rgb
- *        Rgb values of a color to format
- * @param {string} format
- *        Format of string. One of "hex", "rgb", "hsl", "name"
- *
- * @return {string}
- *        Formatted color value, e.g. "#FFF" or "hsl(20, 10%, 10%)"
- */
-function toColorString(rgb, format) {
-  let [r, g, b] = rgb;
-
-  switch (format) {
-    case "hex":
-      return hexString(rgb);
-    case "rgb":
-      return "rgb(" + r + ", " + g + ", " + b + ")";
-    case "hsl":
-      let [h, s, l] = rgbToHsl(rgb);
-      return "hsl(" + h + ", " + s + "%, " + l + "%)";
-    case "name":
-      let str;
-      try {
-        str = rgbToColorName(r, g, b);
-      } catch (e) {
-        str = hexString(rgb);
-      }
-      return str;
-    default:
-      return hexString(rgb);
-  }
-}
-
-/**
- * Produce a hex-formatted color string from rgb values.
- *
- * @param {array} rgb
- *        Rgb values of color to stringify
- *
- * @return {string}
- *        Hex formatted string for color, e.g. "#FFEE00"
- */
-function hexString([r, g, b]) {
-  let val = (1 << 24) + (r << 16) + (g << 8) + (b << 0);
-  return "#" + val.toString(16).substr(-6).toUpperCase();
-}
deleted file mode 100644
--- a/devtools/client/eyedropper/eyedropper.xul
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- 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/. -->
-
-<!DOCTYPE window []>
-
-<?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?>
-<?xml-stylesheet href="chrome://devtools/skin/eyedropper.css" type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        no-theme="true">
-  <script type="application/javascript;version=1.8"
-          src="chrome://devtools/content/shared/theme-switching.js"/>
-  <commandset id="eyedropper-commandset">
-    <command id="eyedropper-cmd-close"
-             oncommand="void(0);"/>
-    <command id="eyedropper-cmd-copy"
-             oncommand="void(0);"/>
-  </commandset>
-
-  <keyset id="eyedropper-keyset">
-    <key id="eyedropper-key-escape"
-         keycode="VK_ESCAPE"
-         command="eyedropper-cmd-close"/>
-    <key id="eyedropper-key-enter"
-         keycode="VK_RETURN"
-         command="eyedropper-cmd-copy"/>
-  </keyset>
-
-  <box id="canvas-overflow">
-    <canvas id="canvas" xmlns="http://www.w3.org/1999/xhtml" width="96" height="96">
-    </canvas>
-  </box>
-  <hbox id="color-value-container">
-    <hbox id="color-value-box">
-      <box id="color-preview">
-      </box>
-      <label id="color-value" class="devtools-monospace">
-      </label>
-    </hbox>
-  </hbox>
-</window>
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/eyedropper/moz.build
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-DevToolsModules(
-    'eyedropper-child.js',
-    'eyedropper.js'
-)
-
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
deleted file mode 100644
--- a/devtools/client/eyedropper/nocursor.css
+++ /dev/null
@@ -1,3 +0,0 @@
-* {
-  cursor: none !important;
-}
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/eyedropper/test/.eslintrc
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  // Extend from the shared list of defined globals for mochitests.
-  "extends": "../../../.eslintrc.mochitests"
-}
deleted file mode 100644
--- a/devtools/client/eyedropper/test/browser.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[DEFAULT]
-tags = devtools
-subsuite = clipboard
-support-files =
-  color-block.html
-  head.js
-  !/devtools/client/commandline/test/helpers.js
-  !/devtools/client/framework/test/shared-head.js
-
-[browser_eyedropper_basic.js]
-skip-if = os == "win" && debug # bug 963492
-[browser_eyedropper_cmd.js]
-skip-if = true # bug 1278400
deleted file mode 100644
--- a/devtools/client/eyedropper/test/browser_eyedropper_basic.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-const TESTCASE_URI = CHROME_URL_ROOT + "color-block.html";
-const DIV_COLOR = "#0000FF";
-
-/**
- * Test basic eyedropper widget functionality:
- *  - Opening eyedropper and pressing ESC closes the eyedropper
- *  - Opening eyedropper and clicking copies the center color
- */
-add_task(function* () {
-  yield addTab(TESTCASE_URI);
-
-  info("added tab");
-
-  yield testEscape();
-
-  info("testing selecting a color");
-
-  yield testSelect();
-});
-
-function* testEscape() {
-  let dropper = new Eyedropper(window);
-
-  yield inspectPage(dropper, false);
-
-  let destroyed = dropper.once("destroy");
-  pressESC();
-  yield destroyed;
-
-  ok(true, "escape closed the eyedropper");
-}
-
-function* testSelect() {
-  let dropper = new Eyedropper(window);
-
-  let selected = dropper.once("select");
-  let copied = waitForClipboard(() => {}, DIV_COLOR);
-
-  yield inspectPage(dropper);
-
-  let color = yield selected;
-  is(color, DIV_COLOR, "correct color selected");
-
-  // wait for DIV_COLOR to be copied to the clipboard
-  yield copied;
-}
-
-/* Helpers */
-
-function* inspectPage(dropper, click = true) {
-  yield dropper.open();
-
-  info("dropper opened");
-
-  let target = document.documentElement;
-  let win = window;
-
-  // get location of the <div> in the content, offset from browser window
-  let box = gBrowser.selectedBrowser.getBoundingClientRect();
-  let x = box.left + 100;
-  let y = box.top + 100;
-
-  EventUtils.synthesizeMouse(target, x, y, { type: "mousemove" }, win);
-
-  yield dropperLoaded(dropper);
-
-  EventUtils.synthesizeMouse(target, x + 10, y + 10, { type: "mousemove" }, win);
-
-  if (click) {
-    EventUtils.synthesizeMouse(target, x + 10, y + 10, {}, win);
-  }
-}
-
-function pressESC() {
-  EventUtils.synthesizeKey("VK_ESCAPE", { });
-}
deleted file mode 100644
--- a/devtools/client/eyedropper/test/browser_eyedropper_cmd.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Tests that the eyedropper command works
-
-const TESTCASE_URI = CHROME_URL_ROOT + "color-block.html";
-const DIV_COLOR = "#0000FF";
-
-function test() {
-  return Task.spawn(spawnTest).then(finish, helpers.handleError);
-}
-
-function* spawnTest() {
-  let options = yield helpers.openTab(TESTCASE_URI);
-  yield helpers.openToolbar(options);
-
-  yield helpers.audit(options, [
-    {
-      setup: "eyedropper",
-      check: {
-        input: "eyedropper"
-      },
-      exec: { output: "" }
-    },
-  ]);
-
-  yield inspectAndWaitForCopy();
-
-  yield helpers.closeToolbar(options);
-  yield helpers.closeTab(options);
-}
-
-function inspectAndWaitForCopy() {
-  let copied = waitForClipboard(() => {}, DIV_COLOR);
-  let ready = inspectPage(); // resolves once eyedropper is destroyed
-
-  return Promise.all([copied, ready]);
-}
-
-function inspectPage() {
-  let target = document.documentElement;
-  let win = window;
-
-  // get location of the <div> in the content, offset from browser window
-  let box = gBrowser.selectedBrowser.getBoundingClientRect();
-  let x = box.left + 100;
-  let y = box.top + 100;
-
-  let dropper = EyedropperManager.getInstance(window);
-
-  return dropperStarted(dropper).then(() => {
-    EventUtils.synthesizeMouse(target, x, y, { type: "mousemove" }, win);
-
-    return dropperLoaded(dropper).then(() => {
-      EventUtils.synthesizeMouse(target, x + 10, y + 10, { type: "mousemove" }, win);
-
-      EventUtils.synthesizeMouse(target, x + 10, y + 10, {}, win);
-      return dropper.once("destroy");
-    });
-  });
-}
deleted file mode 100644
--- a/devtools/client/eyedropper/test/color-block.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype html>
-<html>
-<head>
-  <title>basic eyedropper test case</title>
-  <style type="text/css">
-  body {
-    background: #f99;
-  }
-
-  #test {
-    margin: 100px;
-    background-color: blue;
-    width: 20px;
-    height: 20px;
-  }
-  </style>
-</head>
-<body>
-  <div id="test">
-  </div>
-</body>
-</html>
deleted file mode 100644
--- a/devtools/client/eyedropper/test/head.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// shared-head.js handles imports, constants, and utility functions
-Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
-Services.scriptloader.loadSubScript(TEST_DIR + "../../../commandline/test/helpers.js", this);
-
-const { Eyedropper, EyedropperManager } = require("devtools/client/eyedropper/eyedropper");
-
-function waitForClipboard(setup, expected) {
-  let deferred = defer();
-  SimpleTest.waitForClipboard(expected, setup, deferred.resolve, deferred.reject);
-  return deferred.promise;
-}
-
-function dropperStarted(dropper) {
-  if (dropper.isStarted) {
-    return promise.resolve();
-  }
-  return dropper.once("started");
-}
-
-function dropperLoaded(dropper) {
-  if (dropper.loaded) {
-    return promise.resolve();
-  }
-  return dropper.once("load");
-}
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -129,19 +129,16 @@ devtools.jar:
     content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
     content/shared/widgets/spectrum-frame.xhtml (shared/widgets/spectrum-frame.xhtml)
     content/shared/widgets/cubic-bezier-frame.xhtml (shared/widgets/cubic-bezier-frame.xhtml)
     content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
     content/shared/widgets/mdn-docs-frame.xhtml (shared/widgets/mdn-docs-frame.xhtml)
     content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
     content/shared/widgets/filter-frame.xhtml (shared/widgets/filter-frame.xhtml)
     content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
-    content/eyedropper/eyedropper.xul (eyedropper/eyedropper.xul)
-    content/eyedropper/crosshairs.css (eyedropper/crosshairs.css)
-    content/eyedropper/nocursor.css (eyedropper/nocursor.css)
     content/aboutdebugging/aboutdebugging.xhtml (aboutdebugging/aboutdebugging.xhtml)
     content/aboutdebugging/aboutdebugging.css (aboutdebugging/aboutdebugging.css)
     content/aboutdebugging/initializer.js (aboutdebugging/initializer.js)
     content/responsive.html/index.xhtml (responsive.html/index.xhtml)
     content/responsive.html/index.js (responsive.html/index.js)
     content/dom/dom.html (dom/dom.html)
     content/dom/content/dom-view.css (dom/content/dom-view.css)
     content/dom/main.js (dom/main.js)
--- a/devtools/client/moz.build
+++ b/devtools/client/moz.build
@@ -8,17 +8,16 @@ include('../templates.mozbuild')
 
 DIRS += [
     'aboutdebugging',
     'animationinspector',
     'canvasdebugger',
     'commandline',
     'debugger',
     'dom',
-    'eyedropper',
     'framework',
     'inspector',
     'jsonview',
     'locales',
     'memory',
     'netmonitor',
     'performance',
     'preferences',