Merge m-c to fx-mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 06 Dec 2013 14:51:08 +0100
changeset 174841 86a06702345394da1eda977b16cd774aa2e852f0
parent 174840 0db59e40d72d13db16b4264a100d67673d7dc5f1 (current diff)
parent 174825 ae2c044c6418e4ba799d1438d52c85d3e2c496bd (diff)
child 174842 dccf3c3e7125c0a9144ecc9b5d3298d6550cab2d
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.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
Merge m-c to fx-mozilla-inbound
browser/devtools/shared/test/browser_csstransformpreview.js
browser/devtools/shared/widgets/CSSTransformPreviewer.js
browser/devtools/styleinspector/test/browser_bug726427_csstransform_tooltip.js
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "2f52419acd2edacec369138b143454dd8afccebd", 
+    "revision": "d10e0357c63bb565db8bdd3c23f062bfb9e21315", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/devtools/shared/test/browser.ini
+++ b/browser/devtools/shared/test/browser.ini
@@ -26,9 +26,8 @@ support-files =
 [browser_telemetry_toolboxtabs_options.js]
 [browser_telemetry_toolboxtabs_styleeditor.js]
 [browser_telemetry_toolboxtabs_webconsole.js]
 [browser_templater_basic.js]
 [browser_toolbar_basic.js]
 [browser_toolbar_tooltip.js]
 [browser_toolbar_webconsole_errors_count.js]
 [browser_spectrum.js]
-[browser_csstransformpreview.js]
deleted file mode 100644
--- a/browser/devtools/shared/test/browser_csstransformpreview.js
+++ /dev/null
@@ -1,139 +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/ */
-
-// Tests that the spectrum color picker works correctly
-
-const TEST_URI = "data:text/html;charset=utf-8,<div></div>";
-const {CSSTransformPreviewer} = devtools.require("devtools/shared/widgets/CSSTransformPreviewer");
-
-let doc, root;
-
-function test() {
-  waitForExplicitFinish();
-  addTab(TEST_URI, () => {
-    doc = content.document;
-    root = doc.querySelector("div");
-    startTests();
-  });
-}
-
-function endTests() {
-  doc = root = null;
-  gBrowser.removeCurrentTab();
-  finish();
-}
-
-function startTests() {
-  testCreateAndDestroyShouldAppendAndRemoveElements();
-}
-
-function testCreateAndDestroyShouldAppendAndRemoveElements() {
-  ok(root, "We have the root node to append the preview to");
-  is(root.childElementCount, 0, "Root node is empty");
-
-  let p = new CSSTransformPreviewer(root);
-  p.preview("matrix(1, -0.2, 0, 1, 0, 0)");
-  ok(root.childElementCount > 0, "Preview has appended elements");
-  ok(root.querySelector("canvas"), "Canvas preview element is here");
-
-  p.destroy();
-  is(root.childElementCount, 0, "Destroying preview removed all nodes");
-
-  testCanvasDimensionIsConstrainedByMaxDim();
-}
-
-function testCanvasDimensionIsConstrainedByMaxDim() {
-  let p = new CSSTransformPreviewer(root);
-  p.MAX_DIM = 500;
-  p.preview("scale(1)", "center", 1000, 1000);
-
-  let canvas = root.querySelector("canvas");
-  is(canvas.width, 500, "Canvas width is correct");
-  is(canvas.height, 500, "Canvas height is correct");
-
-  p.destroy();
-
-  testCallingPreviewSeveralTimesReusesTheSameCanvas();
-}
-
-function testCallingPreviewSeveralTimesReusesTheSameCanvas() {
-  let p = new CSSTransformPreviewer(root);
-
-  p.preview("scale(1)", "center", 1000, 1000);
-  let canvas = root.querySelector("canvas");
-
-  p.preview("rotate(90deg)");
-  let canvases = root.querySelectorAll("canvas");
-  is(canvases.length, 1, "Still one canvas element");
-  is(canvases[0], canvas, "Still the same canvas element");
-  p.destroy();
-
-  testCanvasDimensionAreCorrect();
-}
-
-function testCanvasDimensionAreCorrect() {
-  // Only test a few simple transformations
-  let p = new CSSTransformPreviewer(root);
-
-  // Make sure we have a square
-  let w = 200, h = w;
-  p.MAX_DIM = w;
-
-  // We can't test the content of the canvas here, just that, given a max width
-  // the aspect ratio of the canvas seems correct.
-
-  // Translate a square by its width, should be a rectangle
-  p.preview("translateX(200px)", "center", w, h);
-  let canvas = root.querySelector("canvas");
-  is(canvas.width, w, "width is correct");
-  is(canvas.height, h/2, "height is half of the width");
-
-  // Rotate on the top right corner, should be a rectangle
-  p.preview("rotate(-90deg)", "top right", w, h);
-  is(canvas.width, w, "width is correct");
-  is(canvas.height, h/2, "height is half of the width");
-
-  // Rotate on the bottom left corner, should be a rectangle
-  p.preview("rotate(90deg)", "top right", w, h);
-  is(canvas.width, w/2, "width is half of the height");
-  is(canvas.height, h, "height is correct");
-
-  // Scale from center, should still be a square
-  p.preview("scale(2)", "center", w, h);
-  is(canvas.width, w, "width is correct");
-  is(canvas.height, h, "height is correct");
-
-  // Skew from center, 45deg, should be a rectangle
-  p.preview("skew(45deg)", "center", w, h);
-  is(canvas.width, w, "width is correct");
-  is(canvas.height, h/2, "height is half of the height");
-
-  p.destroy();
-
-  testPreviewingInvalidTransformReturnsFalse();
-}
-
-function testPreviewingInvalidTransformReturnsFalse() {
-  let p = new CSSTransformPreviewer(root);
-  ok(!p.preview("veryWow(muchPx) suchTransform(soDeg)"), "Returned false for invalid transform");
-  ok(!p.preview("rotae(3deg)"), "Returned false for invalid transform");
-
-  // Verify the canvas is empty by checking the image data
-  let canvas = root.querySelector("canvas"), ctx = canvas.getContext("2d");
-  let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
-  for (let i = 0, n = data.length; i < n; i += 4) {
-    // Let's not log 250*250*4 asserts! Instead, just log when it fails
-    let red = data[i];
-    let green = data[i + 1];
-    let blue = data[i + 2];
-    let alpha = data[i + 3];
-    if (red !== 0 || green !== 0 || blue !== 0 || alpha !== 0) {
-      ok(false, "Image data is not empty after an invalid transformed was previewed");
-      break;
-    }
-  }
-
-  is(p.preview("translateX(30px)"), true, "Returned true for a valid transform");
-  endTests();
-}
deleted file mode 100644
--- a/browser/devtools/shared/widgets/CSSTransformPreviewer.js
+++ /dev/null
@@ -1,389 +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";
-
-/**
- * The CSSTransformPreview module displays, using a <canvas> a rectangle, with
- * a given width and height and its transformed version, given a css transform
- * property and origin. It also displays arrows from/to each corner.
- *
- * It is useful to visualize how a css transform affected an element. It can
- * help debug tricky transformations. It is used today in a tooltip, and this
- * tooltip is shown when hovering over a css transform declaration in the rule
- * and computed view panels.
- *
- * TODO: For now, it multiplies matrices itself to calculate the coordinates of
- * the transformed box, but that should be removed as soon as we can get access
- * to getQuads().
- */
-
-const HTML_NS = "http://www.w3.org/1999/xhtml";
-
-/**
- * The TransformPreview needs an element to output a canvas tag.
- *
- * Usage example:
- *
- * let t = new CSSTransformPreviewer(myRootElement);
- * t.preview("rotate(45deg)", "top left", 200, 400);
- * t.preview("skew(19deg)", "center", 100, 500);
- * t.preview("matrix(1, -0.2, 0, 1, 0, 0)");
- * t.destroy();
- *
- * @param {nsIDOMElement} parentEl
- *        Where the canvas will go
- */
-function CSSTransformPreviewer(parentEl) {
-  this.parentEl = parentEl;
-  this.doc = this.parentEl.ownerDocument;
-  this.canvas = null;
-  this.ctx = null;
-}
-
-module.exports.CSSTransformPreviewer = CSSTransformPreviewer;
-
-CSSTransformPreviewer.prototype = {
-  /**
-   * The preview look-and-feel can be changed using these properties
-   */
-  MAX_DIM: 250,
-  PAD: 5,
-  ORIGINAL_FILL: "#1F303F",
-  ORIGINAL_STROKE: "#B2D8FF",
-  TRANSFORMED_FILL: "rgba(200, 200, 200, .5)",
-  TRANSFORMED_STROKE: "#B2D8FF",
-  ARROW_STROKE: "#329AFF",
-  ORIGIN_STROKE: "#329AFF",
-  ARROW_TIP_HEIGHT: 10,
-  ARROW_TIP_WIDTH: 8,
-  CORNER_SIZE_RATIO: 6,
-
-  /**
-   * Destroy removes the canvas from the parentelement passed in the constructor
-   */
-  destroy: function() {
-    if (this.canvas) {
-      this.parentEl.removeChild(this.canvas);
-    }
-    if (this._hiddenDiv) {
-      this.parentEl.removeChild(this._hiddenDiv);
-    }
-    this.parentEl = this.canvas = this.ctx = this.doc = null;
-  },
-
-  _createMarkup: function() {
-    this.canvas = this.doc.createElementNS(HTML_NS, "canvas");
-
-    this.canvas.setAttribute("id", "canvas");
-    this.canvas.setAttribute("width", this.MAX_DIM);
-    this.canvas.setAttribute("height", this.MAX_DIM);
-    this.canvas.style.position = "relative";
-    this.parentEl.appendChild(this.canvas);
-
-    this.ctx = this.canvas.getContext("2d");
-  },
-
-  _getComputed: function(name, value, width, height) {
-    if (!this._hiddenDiv) {
-      // Create a hidden element to apply the style to
-      this._hiddenDiv = this.doc.createElementNS(HTML_NS, "div");
-      this._hiddenDiv.style.visibility = "hidden";
-      this._hiddenDiv.style.position = "absolute";
-      this.parentEl.appendChild(this._hiddenDiv);
-    }
-
-    // Camelcase the name
-    name = name.replace(/-([a-z]{1})/g, (m, letter) => letter.toUpperCase());
-
-    // Apply width and height to make sure computation is made correctly
-    this._hiddenDiv.style.width = width + "px";
-    this._hiddenDiv.style.height = height + "px";
-
-    // Show the hidden div, apply the style, read the computed style, hide the
-    // hidden div again
-    this._hiddenDiv.style.display = "block";
-    this._hiddenDiv.style[name] = value;
-    let computed = this.doc.defaultView.getComputedStyle(this._hiddenDiv);
-    let computedValue = computed[name];
-    this._hiddenDiv.style.display = "none";
-
-    return computedValue;
-  },
-
-  _getMatrixFromTransformString: function(transformStr) {
-    let matrix = transformStr.substring(0, transformStr.length - 1).
-      substring(transformStr.indexOf("(") + 1).split(",");
-
-    matrix.forEach(function(value, index) {
-      matrix[index] = parseFloat(value, 10);
-    });
-
-    let transformMatrix = null;
-
-    if (matrix.length === 6) {
-      // 2d transform
-      transformMatrix = [
-        [matrix[0], matrix[2], matrix[4], 0],
-        [matrix[1], matrix[3], matrix[5], 0],
-        [0,     0,     1,     0],
-        [0,     0,     0,     1]
-      ];
-    } else {
-      // 3d transform
-      transformMatrix = [
-        [matrix[0], matrix[4], matrix[8],  matrix[12]],
-        [matrix[1], matrix[5], matrix[9],  matrix[13]],
-        [matrix[2], matrix[6], matrix[10], matrix[14]],
-        [matrix[3], matrix[7], matrix[11], matrix[15]]
-      ];
-    }
-
-    return transformMatrix;
-  },
-
-  _getOriginFromOriginString: function(originStr) {
-    let offsets = originStr.split(" ");
-    offsets.forEach(function(item, index) {
-      offsets[index] = parseInt(item, 10);
-    });
-
-    return offsets;
-  },
-
-  _multiply: function(m1, m2) {
-    let m = [];
-    for (let m1Line = 0; m1Line < m1.length; m1Line++) {
-      m[m1Line] = 0;
-      for (let m2Col = 0; m2Col < m2.length; m2Col++) {
-        m[m1Line] += m1[m1Line][m2Col] * m2[m2Col];
-      }
-    }
-    return [m[0], m[1]];
-  },
-
-  _getTransformedPoint: function(matrix, point, origin) {
-    let pointMatrix = [point[0] - origin[0], point[1] - origin[1], 1, 1];
-    return this._multiply(matrix, pointMatrix);
-  },
-
-  _getTransformedPoints: function(matrix, rect, origin) {
-    return rect.map(point => {
-      let tPoint = this._getTransformedPoint(matrix, [point[0], point[1]], origin);
-      return [tPoint[0] + origin[0], tPoint[1] + origin[1]];
-    });
-  },
-
-  /**
-   * For canvas to avoid anti-aliasing
-   */
-  _round: x => Math.round(x) + .5,
-
-  _drawShape: function(points, fillStyle, strokeStyle) {
-    this.ctx.save();
-
-    this.ctx.lineWidth = 1;
-    this.ctx.strokeStyle = strokeStyle;
-    this.ctx.fillStyle = fillStyle;
-
-    this.ctx.beginPath();
-    this.ctx.moveTo(this._round(points[0][0]), this._round(points[0][1]));
-    for (var i = 1; i < points.length; i++) {
-      this.ctx.lineTo(this._round(points[i][0]), this._round(points[i][1]));
-    }
-    this.ctx.lineTo(this._round(points[0][0]), this._round(points[0][1]));
-    this.ctx.fill();
-    this.ctx.stroke();
-
-    this.ctx.restore();
-  },
-
-  _drawArrow: function(x1, y1, x2, y2) {
-    // do not draw if the line is too small
-    if (Math.abs(x2-x1) < 20 && Math.abs(y2-y1) < 20) {
-      return;
-    }
-
-    this.ctx.save();
-
-    this.ctx.strokeStyle = this.ARROW_STROKE;
-    this.ctx.fillStyle = this.ARROW_STROKE;
-    this.ctx.lineWidth = 1;
-
-    this.ctx.beginPath();
-    this.ctx.moveTo(this._round(x1), this._round(y1));
-    this.ctx.lineTo(this._round(x2), this._round(y2));
-    this.ctx.stroke();
-
-    this.ctx.beginPath();
-    this.ctx.translate(x2, y2);
-    let radians = Math.atan((y1 - y2) / (x1 - x2));
-    radians += ((x1 >= x2) ? -90 : 90) * Math.PI / 180;
-    this.ctx.rotate(radians);
-    this.ctx.moveTo(0, 0);
-    this.ctx.lineTo(this.ARROW_TIP_WIDTH / 2, this.ARROW_TIP_HEIGHT);
-    this.ctx.lineTo(-this.ARROW_TIP_WIDTH / 2, this.ARROW_TIP_HEIGHT);
-    this.ctx.closePath();
-    this.ctx.fill();
-
-    this.ctx.restore();
-  },
-
-  _drawOrigin: function(x, y) {
-    this.ctx.save();
-
-    this.ctx.strokeStyle = this.ORIGIN_STROKE;
-    this.ctx.fillStyle = this.ORIGIN_STROKE;
-
-    this.ctx.beginPath();
-    this.ctx.arc(x, y, 4, 0, 2 * Math.PI, false);
-    this.ctx.stroke();
-    this.ctx.fill();
-
-    this.ctx.restore();
-  },
-
-  /**
-   * Computes the largest width and height of all the given shapes and changes
-   * all of the shapes' points (by reference) so they fit into the configured
-   * MAX_DIM - 2*PAD area.
-   * @return {Object} A {w, h} giving the size the canvas should be
-   */
-  _fitAllShapes: function(allShapes) {
-    let allXs = [], allYs = [];
-    for (let shape of allShapes) {
-      for (let point of shape) {
-        allXs.push(point[0]);
-        allYs.push(point[1]);
-      }
-    }
-    let minX = Math.min.apply(Math, allXs);
-    let maxX = Math.max.apply(Math, allXs);
-    let minY = Math.min.apply(Math, allYs);
-    let maxY = Math.max.apply(Math, allYs);
-
-    let spanX = maxX - minX;
-    let spanY = maxY - minY;
-    let isWide = spanX > spanY;
-
-    let cw = isWide ? this.MAX_DIM :
-      this.MAX_DIM * Math.min(spanX, spanY) / Math.max(spanX, spanY);
-    let ch = !isWide ? this.MAX_DIM :
-      this.MAX_DIM * Math.min(spanX, spanY) / Math.max(spanX, spanY);
-
-    let mapX = x => this.PAD + ((cw - 2 * this.PAD) / (maxX - minX)) * (x - minX);
-    let mapY = y => this.PAD + ((ch - 2 * this.PAD) / (maxY - minY)) * (y - minY);
-
-    for (let shape of allShapes) {
-      for (let point of shape) {
-        point[0] = mapX(point[0]);
-        point[1] = mapY(point[1]);
-      }
-    }
-
-    return {w: cw, h: ch};
-  },
-
-  _drawShapes: function(shape, corner, transformed, transformedCorner) {
-    this._drawOriginal(shape);
-    this._drawOriginalCorner(corner);
-    this._drawTransformed(transformed);
-    this._drawTransformedCorner(transformedCorner);
-  },
-
-  _drawOriginal: function(points) {
-    this._drawShape(points, this.ORIGINAL_FILL, this.ORIGINAL_STROKE);
-  },
-
-  _drawTransformed: function(points) {
-    this._drawShape(points, this.TRANSFORMED_FILL, this.TRANSFORMED_STROKE);
-  },
-
-  _drawOriginalCorner: function(points) {
-    this._drawShape(points, this.ORIGINAL_STROKE, this.ORIGINAL_STROKE);
-  },
-
-  _drawTransformedCorner: function(points) {
-    this._drawShape(points, this.TRANSFORMED_STROKE, this.TRANSFORMED_STROKE);
-  },
-
-  _drawArrows: function(shape, transformed) {
-    this._drawArrow(shape[0][0], shape[0][1], transformed[0][0], transformed[0][1]);
-    this._drawArrow(shape[1][0], shape[1][1], transformed[1][0], transformed[1][1]);
-    this._drawArrow(shape[2][0], shape[2][1], transformed[2][0], transformed[2][1]);
-    this._drawArrow(shape[3][0], shape[3][1], transformed[3][0], transformed[3][1]);
-  },
-
-  /**
-   * Draw a transform preview
-   *
-   * @param {String} transform
-   *        The css transform value as a string, as typed by the user, as long
-   *        as it can be computed by the browser
-   * @param {String} origin
-   *        Same as above for the transform-origin value. Defaults to "center"
-   * @param {Number} width
-   *        The width of the container. Defaults to 200
-   * @param {Number} height
-   *        The height of the container. Defaults to 200
-   * @return {Boolean} Whether or not the preview could be created. Will return
-   *         false for instance if the transform is invalid
-   */
-  preview: function(transform, origin="center", width=200, height=200) {
-    // Create/clear the canvas
-    if (!this.canvas) {
-      this._createMarkup();
-    }
-    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
-
-    // Get computed versions of transform and origin
-    transform = this._getComputed("transform", transform, width, height);
-    if (transform && transform !== "none") {
-      origin = this._getComputed("transform-origin", origin, width, height);
-
-      // Get the matrix, origin and width height data for the previewed element
-      let originData = this._getOriginFromOriginString(origin);
-      let matrixData = this._getMatrixFromTransformString(transform);
-
-      // Compute the original box rect and transformed box rect
-      let shapePoints = [
-        [0, 0],
-        [width, 0],
-        [width, height],
-        [0, height]
-      ];
-      let transformedPoints = this._getTransformedPoints(matrixData, shapePoints, originData);
-
-      // Do the same for the corner triangle shape
-      let cornerSize = Math.min(shapePoints[2][1] - shapePoints[1][1],
-        shapePoints[1][0] - shapePoints[0][0]) / this.CORNER_SIZE_RATIO;
-      let cornerPoints = [
-        [shapePoints[1][0], shapePoints[1][1]],
-        [shapePoints[1][0], shapePoints[1][1] + cornerSize],
-        [shapePoints[1][0] - cornerSize, shapePoints[1][1]]
-      ];
-      let transformedCornerPoints = this._getTransformedPoints(matrixData, cornerPoints, originData);
-
-      // Resize points to fit everything in the canvas
-      let {w, h} = this._fitAllShapes([
-        shapePoints,
-        transformedPoints,
-        cornerPoints,
-        transformedCornerPoints,
-        [originData]
-      ]);
-
-      this.canvas.setAttribute("width", w);
-      this.canvas.setAttribute("height", h);
-
-      this._drawShapes(shapePoints, cornerPoints, transformedPoints, transformedCornerPoints)
-      this._drawArrows(shapePoints, transformedPoints);
-      this._drawOrigin(originData[0], originData[1]);
-
-      return true;
-    } else {
-      return false;
-    }
-  }
-};
--- a/browser/devtools/shared/widgets/Tooltip.js
+++ b/browser/devtools/shared/widgets/Tooltip.js
@@ -7,17 +7,16 @@
 const {Cc, Cu, Ci} = require("chrome");
 const promise = require("sdk/core/promise");
 const IOService = Cc["@mozilla.org/network/io-service;1"]
   .getService(Ci.nsIIOService);
 const {Spectrum} = require("devtools/shared/widgets/Spectrum");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {colorUtils} = require("devtools/css-color");
 const Heritage = require("sdk/core/heritage");
-const {CSSTransformPreviewer} = require("devtools/shared/widgets/CSSTransformPreviewer");
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "setNamedTimeout",
   "resource:///modules/devtools/ViewHelpers.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "clearNamedTimeout",
   "resource:///modules/devtools/ViewHelpers.jsm");
@@ -96,16 +95,17 @@ let PanelFactory = {
    *        An options store to get some configuration from
    */
   get: function(doc, options) {
     // Create the tooltip
     let panel = doc.createElement("panel");
     panel.setAttribute("hidden", true);
     panel.setAttribute("ignorekeys", true);
 
+    // Prevent the click used to close the panel from being consumed
     panel.setAttribute("consumeoutsideclicks", options.get("consumeOutsideClick"));
     panel.setAttribute("noautofocus", options.get("noAutoFocus"));
     panel.setAttribute("type", "arrow");
     panel.setAttribute("level", "top");
 
     panel.setAttribute("class", "devtools-tooltip theme-tooltip-panel");
     doc.querySelector("window").appendChild(panel);
 
@@ -224,20 +224,16 @@ Tooltip.prototype = {
     this.panel.hidden = true;
     this.panel.hidePopup();
   },
 
   isShown: function() {
     return this.panel.state !== "closed" && this.panel.state !== "hiding";
   },
 
-  setSize: function(width, height) {
-    this.panel.sizeTo(width, height);
-  },
-
   /**
    * Empty the tooltip's content
    */
   empty: function() {
     while (this.panel.hasChildNodes()) {
       this.panel.removeChild(this.panel.firstChild);
     }
   },
@@ -303,26 +299,25 @@ Tooltip.prototype = {
    *
    * Note that if you call this function a second time, it will itself call
    * stopTogglingOnHover before adding mouse tracking listeners again.
    *
    * @param {node} baseNode
    *        The container for all target nodes
    * @param {Function} targetNodeCb
    *        A function that accepts a node argument and returns true or false
-   *        (or a promise that resolves or rejects) to signify if the tooltip
-   *        should be shown on that node or not.
+   *        to signify if the tooltip should be shown on that node or not.
    *        Additionally, the function receives a second argument which is the
    *        tooltip instance itself, to be used to add/modify the content of the
    *        tooltip if needed. If omitted, the tooltip will be shown everytime.
    * @param {Number} showDelay
    *        An optional delay that will be observed before showing the tooltip.
    *        Defaults to this.defaultShowDelay.
    */
-  startTogglingOnHover: function(baseNode, targetNodeCb, showDelay=this.defaultShowDelay) {
+  startTogglingOnHover: function(baseNode, targetNodeCb, showDelay = this.defaultShowDelay) {
     if (this._basedNode) {
       this.stopTogglingOnHover();
     }
 
     this._basedNode = baseNode;
     this._showDelay = showDelay;
     this._targetNodeCb = targetNodeCb || (() => true);
 
@@ -357,22 +352,17 @@ Tooltip.prototype = {
       this._lastHovered = event.target;
       setNamedTimeout(this.uid, this._showDelay, () => {
         this._showOnHover(event.target);
       });
     }
   },
 
   _showOnHover: function(target) {
-    let res = this._targetNodeCb(target, this);
-    if (res && res.then) {
-      res.then(() => {
-        this.show(target);
-      });
-    } else if (res) {
+    if (this._targetNodeCb(target, this)) {
       this.show(target);
     }
   },
 
   _onBaseNodeMouseLeave: function() {
     clearNamedTimeout(this.uid);
     this._lastHovered = null;
     this.hide();
@@ -532,18 +522,16 @@ Tooltip.prototype = {
     imgObj.src = imageUrl;
     imgObj.onload = () => {
       imgObj.onload = null;
 
       // Display dimensions
       let w = options.naturalWidth || imgObj.naturalWidth;
       let h = options.naturalHeight || imgObj.naturalHeight;
       label.textContent = w + " x " + h;
-
-      this.setSize(vbox.width, vbox.height);
     }
   },
 
   /**
    * Exactly the same as the `image` function but takes a css background image
    * value instead : url(....)
    */
   setCssBackgroundImageContent: function(cssBackground, sheetHref, maxDim=400) {
@@ -591,64 +579,16 @@ Tooltip.prototype = {
     }
     iframe.addEventListener("load", onLoad, true);
     iframe.setAttribute("src", SPECTRUM_FRAME);
 
     // Put the iframe in the tooltip
     this.content = iframe;
 
     return def.promise;
-  },
-
-  /**
-   * Set the content of the tooltip to be the result of CSSTransformPreviewer.
-   * Meaning a canvas previewing a css transformation.
-   *
-   * @param {String} transform
-   *        The CSS transform value (e.g. "rotate(45deg) translateX(50px)")
-   * @param {PageStyleActor} pageStyle
-   *        An instance of the PageStyleActor that will be used to retrieve
-   *        computed styles
-   * @param {NodeActor} node
-   *        The NodeActor for the currently selected node
-   * @return A promise that resolves when the tooltip content is ready, or
-   *         rejects if no transform is provided or is invalid
-   */
-  setCssTransformContent: function(transform, pageStyle, node) {
-    let def = promise.defer();
-
-    if (transform) {
-      // Look into the computed styles to find the width and height and possibly
-      // the origin if it hadn't been provided
-      pageStyle.getComputed(node, {
-        filter: "user",
-        markMatched: false,
-        onlyMatched: false
-      }).then(styles => {
-        let origin = styles["transform-origin"].value;
-        let width = parseInt(styles["width"].value);
-        let height = parseInt(styles["height"].value);
-
-        let root = this.doc.createElementNS(XHTML_NS, "div");
-        let previewer = new CSSTransformPreviewer(root);
-        this.content = root;
-        if (!previewer.preview(transform, origin, width, height)) {
-          // If the preview didn't work, reject the promise
-          def.reject();
-        } else {
-          // Else, make sure the tooltip has the right size and resolve
-          this.setSize(previewer.canvas.width, previewer.canvas.height);
-          def.resolve();
-        }
-      });
-    } else {
-      def.reject();
-    }
-
-    return def.promise;
   }
 };
 
 /**
  * Base class for all (color, gradient, ...)-swatch based value editors inside
  * tooltips
  *
  * @param {XULDocument} doc
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -500,37 +500,31 @@ CssHtmlTree.prototype = {
   },
 
   /**
    * Verify that target is indeed a css value we want a tooltip on, and if yes
    * prepare some content for the tooltip
    */
   _buildTooltipContent: function(target)
   {
-    // Test for image url
-    if (target.classList.contains("theme-link")) {
-      let propValue = target.parentNode;
-      let propName = propValue.parentNode.querySelector(".property-name");
-      if (propName.textContent === "background-image") {
-        this.tooltip.setCssBackgroundImageContent(propValue.textContent);
-        return true;
-      }
+    // If the hovered element is not a property view and is not a background
+    // image, then don't show a tooltip
+    let isPropertyValue = target.classList.contains("property-value");
+    if (!isPropertyValue) {
+      return false;
+    }
+    let propName = target.parentNode.querySelector(".property-name");
+    let isBackgroundImage = propName.textContent === "background-image";
+    if (!isBackgroundImage) {
+      return false;
     }
 
-    // Test for css transform
-    if (target.classList.contains("property-value")) {
-      let def = promise.defer();
-      let propValue = target;
-      let propName = target.parentNode.querySelector(".property-name");
-      if (propName.textContent === "transform") {
-        this.tooltip.setCssTransformContent(propValue.textContent,
-          this.pageStyle, this.viewedElement).then(def.resolve);
-        return def.promise;
-      }
-    }
+    // Fill some content
+    this.tooltip.setCssBackgroundImageContent(target.textContent);
+    return true;
   },
 
   /**
    * Create a context menu.
    */
   _buildContextMenu: function()
   {
     let doc = this.styleDocument.defaultView.parent.document;
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1120,42 +1120,37 @@ CssRuleView.prototype = {
     popupset.appendChild(this._contextmenu);
   },
 
   /**
    * Verify that target is indeed a css value we want a tooltip on, and if yes
    * prepare some content for the tooltip
    */
   _buildTooltipContent: function(target) {
-    let property = target.textProperty, def = promise.defer(), hasTooltip = false;
-
-    // Test for css transform
-    if (property && property.name === "transform") {
-      this.previewTooltip.setCssTransformContent(property.value, this.pageStyle,
-        this._viewedElement).then(def.resolve);
-      hasTooltip = true;
-    }
-
-    // Test for image
     let isImageHref = target.classList.contains("theme-link") &&
       target.parentNode.classList.contains("ruleview-propertyvalue");
-    if (isImageHref) {
-      property = target.parentNode.textProperty;
-      this.previewTooltip.setCssBackgroundImageContent(property.value,
-        property.rule.domRule.href);
-      def.resolve();
-      hasTooltip = true;
+
+    // If the inplace-editor is visible or if this is not a background image
+    // don't show the tooltip
+    if (!isImageHref) {
+      return false;
     }
 
-    if (hasTooltip) {
-      this.colorPicker.revert();
-      this.colorPicker.hide();
-    }
+    // Retrieve the TextProperty for the hovered element
+    let property = target.parentNode.textProperty;
+    let href = property.rule.domRule.href;
 
-    return def.promise;
+    // Fill some content
+    this.previewTooltip.setCssBackgroundImageContent(property.value, href);
+
+    // Hide the color picker tooltip if shown and revert changes
+    this.colorPicker.revert();
+    this.colorPicker.hide();
+
+    return true;
   },
 
   /**
    * Update the context menu. This means enabling or disabling menuitems as
    * appropriate.
    */
   _contextMenuUpdate: function() {
     let win = this.doc.defaultView;
@@ -1301,17 +1296,17 @@ CssRuleView.prototype = {
     }
 
     this.popup.destroy();
   },
 
   /**
    * Update the highlighted element.
    *
-   * @param {NodeActor} aElement
+   * @param {nsIDOMElement} aElement
    *        The node whose style rules we'll inspect.
    */
   highlight: function CssRuleView_highlight(aElement)
   {
     if (this._viewedElement === aElement) {
       return promise.resolve(undefined);
     }
 
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -48,11 +48,9 @@ support-files =
 [browser_bug894376_css_value_completion_existing_property_value_pair.js]
 [browser_ruleview_bug_902966_revert_value_on_ESC.js]
 [browser_ruleview_pseudoelement.js]
 support-files = browser_ruleview_pseudoelement.html
 [browser_computedview_bug835808_keyboard_nav.js]
 [browser_bug913014_matched_expand.js]
 [browser_bug765105_background_image_tooltip.js]
 [browser_bug889638_rule_view_color_picker.js]
-[browser_bug726427_csstransform_tooltip.js]
-
 [browser_bug940500_rule_view_pick_gradient_color.js]
deleted file mode 100644
--- a/browser/devtools/styleinspector/test/browser_bug726427_csstransform_tooltip.js
+++ /dev/null
@@ -1,224 +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/ */
-
-let contentDoc;
-let inspector;
-let ruleView;
-let computedView;
-
-const PAGE_CONTENT = [
-  '<style type="text/css">',
-  '  #testElement {',
-  '    width: 500px;',
-  '    height: 300px;',
-  '    background: red;',
-  '    transform: skew(16deg);',
-  '  }',
-  '  .test-element {',
-  '    transform-origin: top left;',
-  '    transform: rotate(45deg);',
-  '  }',
-  '  div {',
-  '    transform: scaleX(1.5);',
-  '    transform-origin: bottom right;',
-  '  }',
-  '  [attr] {',
-  '  }',
-  '</style>',
-  '<div id="testElement" class="test-element" attr="value">transformed element</div>'
-].join("\n");
-
-function test() {
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
-    contentDoc = content.document;
-    waitForFocus(createDocument, content);
-  }, true);
-
-  content.location = "data:text/html,rule view css transform tooltip test";
-}
-
-function createDocument() {
-  contentDoc.body.innerHTML = PAGE_CONTENT;
-
-  openRuleView((aInspector, aRuleView) => {
-    inspector = aInspector;
-    ruleView = aRuleView;
-    startTests();
-  });
-}
-
-function startTests() {
-  inspector.selection.setNode(contentDoc.querySelector("#testElement"));
-  inspector.once("inspector-updated", testTransformTooltipOnIDSelector);
-}
-
-function endTests() {
-  contentDoc = inspector = ruleView = computedView = null;
-  gBrowser.removeCurrentTab();
-  finish();
-}
-
-function testTransformTooltipOnIDSelector() {
-  info("Testing that a transform tooltip appears on the #ID rule");
-
-  let panel = ruleView.previewTooltip.panel;
-  ok(panel, "The XUL panel exists for the rule-view preview tooltips");
-
-  let {valueSpan} = getRuleViewProperty("#testElement", "transform");
-  assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
-    // The transform preview is canvas, so there's not much we can test, so for
-    // now, let's just be happy with the fact that the tooltips is shown!
-    ok(true, "Tooltip shown on the transform property of the #ID rule");
-    ruleView.previewTooltip.hide();
-    executeSoon(testTransformTooltipOnClassSelector);
-  });
-}
-
-function testTransformTooltipOnClassSelector() {
-  info("Testing that a transform tooltip appears on the .class rule");
-
-  let {valueSpan} = getRuleViewProperty(".test-element", "transform");
-  assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
-    // The transform preview is canvas, so there's not much we can test, so for
-    // now, let's just be happy with the fact that the tooltips is shown!
-    ok(true, "Tooltip shown on the transform property of the .class rule");
-    ruleView.previewTooltip.hide();
-    executeSoon(testTransformTooltipOnTagSelector);
-  });
-}
-
-function testTransformTooltipOnTagSelector() {
-  info("Testing that a transform tooltip appears on the tag rule");
-
-  let {valueSpan} = getRuleViewProperty("div", "transform");
-  assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
-    // The transform preview is canvas, so there's not much we can test, so for
-    // now, let's just be happy with the fact that the tooltips is shown!
-    ok(true, "Tooltip shown on the transform property of the tag rule");
-    ruleView.previewTooltip.hide();
-    executeSoon(testTransformTooltipNotShownOnInvalidTransform);
-  });
-}
-
-function testTransformTooltipNotShownOnInvalidTransform() {
-  info("Testing that a transform tooltip does not appear for invalid values");
-
-  // This is the list of keys to type in the inplace-editor
-  let keyData = "transform".split("");
-  keyData.push("VK_TAB");
-  keyData = keyData.concat("muchTransform(suchAngle)".split(""));
-  keyData.push("VK_RETURN");
-
-  // Focus the inplace editor
-  let rule = getRule("[attr]");
-  let brace = rule.querySelector(".ruleview-ruleclose");
-  waitForEditorFocus(brace.parentNode, editor => {
-    // Enter an invalid value
-    typeKeySequence(keyData, () => {
-      let {valueSpan} = getRuleViewProperty("[attr]", "transform");
-      assertTooltipNotShownOn(ruleView.previewTooltip, valueSpan, () => {
-        executeSoon(testTransformTooltipOnComputedView);
-      });
-    });
-  });
-  brace.click();
-}
-
-function testTransformTooltipOnComputedView() {
-  info("Testing that a transform tooltip appears in the computed view too");
-
-  inspector.sidebar.select("computedview");
-  computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
-  let doc = computedView.styleDocument;
-
-  let panel = computedView.tooltip.panel;
-  let {valueSpan} = getComputedViewProperty("transform");
-
-  assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
-    // The transform preview is canvas, so there's not much we can test, so for
-    // now, let's just be happy with the fact that the tooltips is shown!
-    ok(true, "Tooltip shown on the computed transform property");
-    computedView.tooltip.hide();
-    executeSoon(endTests);
-  });
-}
-
-function assertTooltipShownOn(tooltip, element, cb) {
-  // If there is indeed a show-on-hover on element, the xul panel will be shown
-  tooltip.panel.addEventListener("popupshown", function shown() {
-    tooltip.panel.removeEventListener("popupshown", shown, true);
-    cb();
-  }, true);
-  tooltip._showOnHover(element);
-}
-
-function assertTooltipNotShownOn(tooltip, element, cb) {
-  // The only way to make sure the tooltip is not shown is try and show it, wait
-  // for a given amount of time, and then check if it's shown or not
-  tooltip._showOnHover(element);
-  setTimeout(() => {
-    ok(!tooltip.isShown(), "The tooltip did not appear on hover of the element");
-    cb();
-  }, tooltip.defaultShowDelay + 100);
-}
-
-function typeKeySequence(sequence, cb, index=0) {
-  if (index === sequence.length) {
-    return cb();
-  }
-
-  EventUtils.synthesizeKey(sequence[index], {}, ruleView.doc.defaultView);
-  executeSoon(() => {
-    typeKeySequence(sequence, cb, index + 1);
-  });
-}
-
-function getRule(selectorText) {
-  let rule;
-
-  [].forEach.call(ruleView.doc.querySelectorAll(".ruleview-rule"), aRule => {
-    let selector = aRule.querySelector(".ruleview-selector-matched");
-    if (selector && selector.textContent === selectorText) {
-      rule = aRule;
-    }
-  });
-
-  return rule;
-}
-
-function getRuleViewProperty(selectorText, propertyName) {
-  let prop;
-
-  let rule = getRule(selectorText);
-  if (rule) {
-    // Look for the propertyName in that rule element
-    [].forEach.call(rule.querySelectorAll(".ruleview-property"), property => {
-      let nameSpan = property.querySelector(".ruleview-propertyname");
-      let valueSpan = property.querySelector(".ruleview-propertyvalue");
-
-      if (nameSpan.textContent === propertyName) {
-        prop = {nameSpan: nameSpan, valueSpan: valueSpan};
-      }
-    });
-  }
-
-  return prop;
-}
-
-function getComputedViewProperty(name) {
-  let prop;
-  [].forEach.call(computedView.styleDocument.querySelectorAll(".property-view"), property => {
-    let nameSpan = property.querySelector(".property-name");
-    let valueSpan = property.querySelector(".property-value");
-
-    if (nameSpan.textContent === name) {
-      prop = {nameSpan: nameSpan, valueSpan: valueSpan};
-    }
-  });
-  return prop;
-}
--- a/browser/devtools/styleinspector/test/browser_bug765105_background_image_tooltip.js
+++ b/browser/devtools/styleinspector/test/browser_bug765105_background_image_tooltip.js
@@ -137,19 +137,18 @@ function testComputedView() {
   info("Testing tooltips in the computed view");
 
   inspector.sidebar.select("computedview");
   computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
   let doc = computedView.styleDocument;
 
   let panel = computedView.tooltip.panel;
   let {valueSpan} = getComputedViewProperty("background-image");
-  let uriSpan = valueSpan.querySelector(".theme-link");
 
-  assertTooltipShownOn(computedView.tooltip, uriSpan, () => {
+  assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
     let images = panel.getElementsByTagName("image");
     is(images.length, 1, "Tooltip contains an image");
     ok(images[0].src === "chrome://global/skin/icons/warning-64.png");
 
     computedView.tooltip.hide();
 
     endTests();
   });
--- a/browser/metro/base/content/contenthandlers/FormHelper.js
+++ b/browser/metro/base/content/contenthandlers/FormHelper.js
@@ -124,17 +124,17 @@ FormAssistant.prototype = {
 
     // We only work with choice lists or elements with autocomplete suggestions
     if (!this._isSelectElement(aElement) &&
         !this._isAutocomplete(aElement)) {
       return this.close();
     }
 
     // Don't re-open when navigating to avoid repopulating list when changing selection.
-    if (this._isAutocomplete(aElement) && this._open && this._isNavigationKey(aEvent)) {
+    if (this._isAutocomplete(aElement) && this._open && Util.isNavigationKey(aEvent.keyCode)) {
       return false;
     }
 
     // Enable the assistant
     this.currentElement = aElement;
     return this._open = true;
   },
 
@@ -288,32 +288,16 @@ FormAssistant.prototype = {
       case "text":
         if (this._isAutocomplete(aEvent.target)) {
           this._sendJsonMsgWrapper("FormAssist:AutoComplete");
         }
         break;
     }
   },
 
-  _isNavigationKey: function (aEvent) {
-    // Ignore navigation keys
-    if (aEvent.keyCode) {
-      let navigationKeys = [
-        aEvent.DOM_VK_DOWN,
-        aEvent.DOM_VK_UP,
-        aEvent.DOM_VK_LEFT,
-        aEvent.DOM_VK_RIGHT,
-        aEvent.DOM_VK_PAGE_UP,
-        aEvent.DOM_VK_PAGE_DOWN];
-
-      return navigationKeys.indexOf(aEvent.keyCode) != -1;
-    }
-    return false;
-  },
-
   _executeDelayed: function formHelperExecuteSoon(aCallback) {
     let self = this;
     let timer = new Util.Timeout(function() {
       aCallback(self);
     });
     timer.once(0);
   },
 
--- a/browser/metro/base/content/helperui/MenuUI.js
+++ b/browser/metro/base/content/helperui/MenuUI.js
@@ -45,17 +45,16 @@ var AutofillMenuUI = {
       maxWidth: this._anchorRect.width,
       maxHeight: 350,
       source: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
     };
   },
 
   show: function show(aAnchorRect, aSuggestionsList) {
     this.commands.addEventListener("select", this, true);
-    window.addEventListener("keypress", this, true);
 
     this._anchorRect = aAnchorRect;
     this._emptyCommands();
     for (let idx = 0; idx < aSuggestionsList.length; idx++) {
       let item = document.createElement("richlistitem");
       let label = document.createElement("label");
       label.setAttribute("value", aSuggestionsList[idx].label);
       item.setAttribute("value", aSuggestionsList[idx].value);
@@ -67,48 +66,23 @@ var AutofillMenuUI = {
   },
 
   selectByIndex: function mn_selectByIndex(aIndex) {
     this._menuPopup.hide();
     FormHelperUI.doAutoComplete(this.commands.childNodes[aIndex].getAttribute("data"));
   },
 
   hide: function hide () {
-    window.removeEventListener("keypress", this, true);
     this.commands.removeEventListener("select", this, true);
 
     this._menuPopup.hide();
   },
 
   handleEvent: function (aEvent) {
     switch (aEvent.type) {
-      case "keypress":
-        switch (aEvent.keyCode) {
-          case aEvent.DOM_VK_ESCAPE:
-            this.hide();
-            break;
-
-          case aEvent.DOM_VK_DOWN:
-            this.commands.moveByOffset(1, true, false);
-            break;
-
-          case aEvent.DOM_VK_UP:
-            this.commands.moveByOffset(-1, true, false);
-            break;
-
-          case aEvent.DOM_VK_PAGE_DOWN:
-            this.commands.moveByOffset(this.commands.scrollOnePage(1), true, false);
-            break;
-
-          case aEvent.DOM_VK_PAGE_UP:
-            this.commands.moveByOffset(this.commands.scrollOnePage(-1), true, false);
-            break;
-        }
-        break;
-
       case "select":
         FormHelperUI.doAutoComplete(this.commands.value);
         break;
     }
   }
 };
 
 var ContextMenuUI = {
@@ -522,22 +496,57 @@ MenuPopup.prototype = {
     let event = document.createEvent("Events");
     event.initEvent(aName, true, false);
     this._panel.dispatchEvent(event);
   },
 
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
       case "keypress":
-        if (!this._wantTypeBehind) {
+        // this.commands is not holding focus and not processing key events.
+        // Proxying events so that they're handled properly.
+
+        // Avoid recursion
+        if (aEvent.mine)
+          break;
+
+        let ev = document.createEvent("KeyboardEvent");
+        ev.initKeyEvent(
+          "keypress",        //  in DOMString typeArg,
+          false,             //  in boolean canBubbleArg,
+          true,              //  in boolean cancelableArg,
+          null,              //  in nsIDOMAbstractView viewArg,  Specifies UIEvent.view. This value may be null.
+          aEvent.ctrlKey,    //  in boolean ctrlKeyArg,
+          aEvent.altKey,     //  in boolean altKeyArg,
+          aEvent.shiftKey,   //  in boolean shiftKeyArg,
+          aEvent.metaKey,    //  in boolean metaKeyArg,
+          aEvent.keyCode,    //  in unsigned long keyCodeArg,
+          aEvent.charCode);  //  in unsigned long charCodeArg);
+
+        ev.mine = true;
+        this.commands.dispatchEvent(ev);
+
+        switch (aEvent.keyCode) {
+          case aEvent.DOM_VK_ESCAPE:
+            this.hide();
+            break;
+
+          case aEvent.DOM_VK_RETURN:
+            this.commands.currentItem.click();
+            break;
+        }
+
+        if (Util.isNavigationKey(aEvent.keyCode)) {
+          aEvent.stopPropagation();
+          aEvent.preventDefault();
+        } else if (!this._wantTypeBehind) {
           // Hide the context menu so you can't type behind it.
           aEvent.stopPropagation();
           aEvent.preventDefault();
-          if (aEvent.keyCode != aEvent.DOM_VK_ESCAPE)
-            this.hide();
+          this.hide();
         }
         break;
       case "blur":
       case "mousedown":
       case "touchstart":
       case "scroll":
         if (!this._popup.contains(aEvent.target)) {
           aEvent.stopPropagation();
--- a/browser/metro/modules/ContentUtil.jsm
+++ b/browser/metro/modules/ContentUtil.jsm
@@ -1,16 +1,17 @@
 /* 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";
 
 this.EXPORTED_SYMBOLS = ["ContentUtil"];
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent;
 
 this.ContentUtil = {
   populateFragmentFromString: function populateFragmentFromString(fragment, str) {
     let re = /^([^#]*)#(\d+)\b([^#]*)/,
         document = fragment.ownerDocument,
         // the remaining arguments are our {text, className} values
         replacements = Array.slice(arguments, 2),
         match;
@@ -82,11 +83,24 @@ this.ContentUtil = {
           if (copy !== undefined)
             target[name] = copy;
         }
       }
     }
 
     // Return the modified object
     return target;
-  }
+  },
 
+  // Checks if a keycode is used for list navigation.
+  isNavigationKey: function (keyCode) {
+    let navigationKeys = [
+      nsIDOMKeyEvent.DOM_VK_DOWN,
+      nsIDOMKeyEvent.DOM_VK_UP,
+      nsIDOMKeyEvent.DOM_VK_LEFT,
+      nsIDOMKeyEvent.DOM_VK_RIGHT,
+      nsIDOMKeyEvent.DOM_VK_PAGE_UP,
+      nsIDOMKeyEvent.DOM_VK_PAGE_DOWN,
+      nsIDOMKeyEvent.DOM_VK_ESCAPE];
+
+    return navigationKeys.indexOf(keyCode) != -1;
+  }
 };
--- a/mobile/android/base/animation/AnimationUtils.java
+++ b/mobile/android/base/animation/AnimationUtils.java
@@ -4,17 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 package org.mozilla.gecko.animation;
 
 import android.content.Context;
 
 public class AnimationUtils {
-    private static long mShortDuration;
+    private static long mShortDuration = -1;
 
     public static long getShortDuration(Context context) {
         if (mShortDuration < 0) {
             mShortDuration = context.getResources().getInteger(android.R.integer.config_shortAnimTime);
         }
         return mShortDuration;
     }
 }
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -522,22 +522,23 @@ ThreadActor.prototype = {
     eventLoop.enter();
   },
   _popThreadPause: function () {
     const eventLoop = this._threadPauseEventLoops.pop();
     dbg_assert(eventLoop, "Should have an event loop.");
     eventLoop.resolve();
   },
 
+  /**
+   * Remove all debuggees and clear out the thread's sources.
+   */
   clearDebuggees: function () {
     if (this.dbg) {
       this.dbg.removeAllDebuggees();
     }
-    this.conn.removeActorPool(this._threadLifetimePool || undefined);
-    this._threadLifetimePool = null;
     this._sources = null;
   },
 
   /**
    * Add a debuggee global to the Debugger object.
    *
    * @returns the Debugger.Object that corresponds to the global.
    */
@@ -631,16 +632,18 @@ ThreadActor.prototype = {
     dumpn("in ThreadActor.prototype.disconnect");
     if (this._state == "paused") {
       this.onResume();
     }
 
     this._state = "exited";
 
     this.clearDebuggees();
+    this.conn.removeActorPool(this._threadLifetimePool);
+    this._threadLifetimePool = null;
 
     if (this._prettyPrintWorker) {
       this._prettyPrintWorker.removeEventListener(
         "error", this._onPrettyPrintError, false);
       this._prettyPrintWorker.removeEventListener(
         "message", this._onPrettyPrintMsg, false);
       this._prettyPrintWorker.terminate();
       this._prettyPrintWorker = null;