Bug 1262439 - 7 - Delete the old eyedropper implementation; r=ochameau
authorPatrick Brosset <pbrosset@mozilla.com>
Thu, 09 Jun 2016 13:33:27 +0200
changeset 346332 17f383218326a3c3e26ecfd6634ca1fa7d12a43e
parent 346331 1a7579d0bf7461b5cd9362745b21f12c15321a79
child 346333 a7a882d122e36defe6c2a102a28ae7dfc16311c5
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau
bugs1262439
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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,837 +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, "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 = Services.appinfo.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; 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
@@ -126,19 +126,16 @@ devtools.jar:
     content/framework/connect/connect.xhtml (framework/connect/connect.xhtml)
     content/framework/connect/connect.css (framework/connect/connect.css)
     content/framework/connect/connect.js (framework/connect/connect.js)
     content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
     content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
     content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
     content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
     content/shared/widgets/spectrum.css (shared/widgets/spectrum.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',