Bug 712472 - click-and-drag in Tilt becomes wonky after you do full-page-zoom (Ctrl +). r=robcee
authorVictor Porof <vporof@mozilla.com>
Wed, 21 Dec 2011 14:39:29 +0200
changeset 84892 8be4e3d2105e8155d7a29b5f87ff072027c6d414
parent 84865 31b7a15bc54de48552146a1718f3e4eca4c5117f
child 84893 a60b890c6936cd4d31b23d6824c70a1d4db8dd53
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrobcee
bugs712472
milestone12.0a1
Bug 712472 - click-and-drag in Tilt becomes wonky after you do full-page-zoom (Ctrl +). r=robcee
browser/devtools/tilt/TiltGL.jsm
browser/devtools/tilt/TiltUtils.jsm
browser/devtools/tilt/TiltVisualizer.jsm
browser/devtools/tilt/test/Makefile.in
browser/devtools/tilt/test/browser_tilt_controller.js
browser/devtools/tilt/test/browser_tilt_gl04.js
browser/devtools/tilt/test/browser_tilt_visualizer.js
browser/devtools/tilt/test/browser_tilt_zoom.js
browser/devtools/tilt/test/head.js
--- a/browser/devtools/tilt/TiltGL.jsm
+++ b/browser/devtools/tilt/TiltGL.jsm
@@ -509,17 +509,17 @@ TiltGL.Renderer.prototype = {
    *                 the x amount of scaling
    * @param {Number} y
    *                 the y amount of scaling
    * @param {Number} z
    *                 optional, the z amount of scaling
    */
   scale: function TGLR_scale(x, y, z)
   {
-    mat4.scale(this.mvMatrix, [x, y, z || 0]);
+    mat4.scale(this.mvMatrix, [x, y, z || 1]);
   },
 
   /**
    * Performs a custom interpolation between two matrices.
    * The result is saved in the first operand.
    *
    * @param {Array} aMat
    *                the first matrix
--- a/browser/devtools/tilt/TiltUtils.jsm
+++ b/browser/devtools/tilt/TiltUtils.jsm
@@ -646,21 +646,34 @@ TiltUtils.getWindowId = function TU_getW
   }
 
   return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIDOMWindowUtils)
                 .currentInnerWindowID;
 };
 
 /**
+ * Gets the markup document viewer zoom for the currently selected browser.
+ *
+ * @return {Number} the zoom ammount
+ */
+TiltUtils.getDocumentZoom = function TU_getDocumentZoom() {
+  let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
+    .getService(Ci.nsIWindowMediator)
+    .getMostRecentWindow("navigator:browser");
+
+  return browserWindow.gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
+};
+
+/**
  * Performs a garbage collection.
  */
 TiltUtils.gc = function TU_gc()
 {
-  var browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
+  let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
     .getService(Ci.nsIWindowMediator)
     .getMostRecentWindow("navigator:browser");
 
   browserWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindowUtils)
                .garbageCollect();
 };
 
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -216,16 +216,17 @@ TiltVisualizer.Presenter = function TV_P
     v3: vec3.create()
   };
 
   /**
    * Scene transformations, exposing offset, translation and rotation.
    * Modified by events in the controller through delegate functions.
    */
   this.transforms = {
+    zoom: TiltUtils.getDocumentZoom(),
     offset: vec3.create(),      // mesh offset, aligned to the viewport center
     translation: vec3.create(), // scene translation, on the [x, y, z] axis
     rotation: quat4.create()    // scene rotation, expressed as a quaternion
   };
 
   /**
    * Variables holding information about the initial and current node selected.
    */
@@ -333,16 +334,18 @@ TiltVisualizer.Presenter.prototype = {
                        transforms.translation[2]);
 
     renderer.transform(quat4.toMat4(transforms.rotation));
 
     // offset the visualization mesh to center
     renderer.translate(transforms.offset[0],
                        transforms.offset[1] + transforms.translation[1], 0);
 
+    renderer.scale(transforms.zoom, transforms.zoom);
+
     // draw the visualization mesh
     renderer.strokeWeight(2);
     renderer.depthTest(true);
     this.drawMeshStacks();
     this.drawMeshWireframe();
     this.drawHighlight();
 
     // make sure the initial transition is drawn until finished
@@ -487,22 +490,23 @@ TiltVisualizer.Presenter.prototype = {
     };
 
     // if there's no initial selection made, highlight the required node
     if (!this._initialSelection) {
       this._initialSelection = true;
       this.highlightNode(this.inspectorUI.selection);
     }
 
-    let width = renderer.width;
-    let height = renderer.height;
+    let zoom = TiltUtils.getDocumentZoom();
+    let width = Math.min(aData.meshWidth * zoom, renderer.width);
+    let height = Math.min(aData.meshHeight * zoom, renderer.height);
 
     // set the necessary mesh offsets
-    this.transforms.offset[0] = -Math.min(aData.meshWidth, width) * 0.5;
-    this.transforms.offset[1] = -Math.min(aData.meshHeight, height) * 0.5;
+    this.transforms.offset[0] = -width * 0.5;
+    this.transforms.offset[1] = -height * 0.5;
 
     // make sure the canvas is opaque now that the initialization is finished
     this.canvas.style.background = TiltVisualizerStyle.canvas.background;
 
     this.drawVisualization();
     this.redraw = true;
   },
 
@@ -554,18 +558,19 @@ TiltVisualizer.Presenter.prototype = {
     this.contentWindow.addEventListener("resize", this.onResize, false);
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
   onResize: function TVP_onResize(e)
   {
-    let width = e.target.innerWidth;
-    let height = e.target.innerHeight;
+    let zoom = TiltUtils.getDocumentZoom();
+    let width = e.target.innerWidth * zoom;
+    let height = e.target.innerHeight * zoom;
 
     // handle aspect ratio changes to update the projection matrix
     this.renderer.width = width;
     this.renderer.height = height;
 
     this.redraw = true;
   },
 
@@ -698,19 +703,22 @@ TiltVisualizer.Presenter.prototype = {
         }
       } else {
         if ("function" === typeof aProperties.onfail) {
           aProperties.onfail();
         }
       }
     }, false);
 
-    let width = this.renderer.width;
-    let height = this.renderer.height;
+    let zoom = TiltUtils.getDocumentZoom();
+    let width = this.renderer.width * zoom;
+    let height = this.renderer.height * zoom;
     let mesh = this.meshStacks;
+    x *= zoom;
+    y *= zoom;
 
     // create a ray following the mouse direction from the near clipping plane
     // to the far clipping plane, to check for intersections with the mesh,
     // and do all the heavy lifting in a separate thread
     worker.postMessage({
       thickness: STACK_THICKNESS,
       vertices: mesh.vertices.components,
 
@@ -983,17 +991,21 @@ TiltVisualizer.Controller.prototype = {
   onKeyDown: function TVC_onKeyDown(e)
   {
     let code = e.keyCode || e.which;
 
     if (code >= e.DOM_VK_LEFT && code <= e.DOM_VK_DOWN) {
       e.preventDefault();
       e.stopPropagation();
     }
-    this.arcball.keyDown(code);
+    if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
+      this.arcball.cancelKeyEvents();
+    } else {
+      this.arcball.keyDown(code);
+    }
   },
 
   /**
    * Called when a key is released.
    */
   onKeyUp: function TVC_onKeyUp(e)
   {
     let code = e.keyCode || e.which;
@@ -1008,26 +1020,27 @@ TiltVisualizer.Controller.prototype = {
     }
     this.arcball.keyUp(code);
   },
 
   /**
    * Called when the canvas looses focus.
    */
   onBlur: function TVC_onBlur(e) {
-    this.arcball._keyCode = {};
+    this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
    */
   onResize: function TVC_onResize(e)
   {
-    let width = e.target.innerWidth;
-    let height = e.target.innerHeight;
+    let zoom = TiltUtils.getDocumentZoom();
+    let width = e.target.innerWidth * zoom;
+    let height = e.target.innerHeight * zoom;
 
     this.arcball.resize(width, height);
   },
 
   /**
    * Checks if this object was initialized properly.
    *
    * @return {Boolean} true if the object was initialized properly
@@ -1464,16 +1477,23 @@ TiltVisualizer.Arcball.prototype = {
       // set the vector to a point mapped inside the sphere
       aSphereVec[0] = x;
       aSphereVec[1] = y;
       aSphereVec[2] = Math.sqrt(1 - sqlength);
     }
   },
 
   /**
+   * Cancels all pending transformations caused by key events.
+   */
+  cancelKeyEvents: function TVA_cancelKeyEvents() {
+    this._keyCode = {};
+  },
+
+  /**
    * Resize this implementation to use different bounds.
    * This function is automatically called when the arcball is created.
    *
    * @param {Number} newWidth
    *                 the new width of canvas
    * @param {Number} newHeight
    *                 the new  height of canvas
    * @param {Number} newRadius
--- a/browser/devtools/tilt/test/Makefile.in
+++ b/browser/devtools/tilt/test/Makefile.in
@@ -71,12 +71,13 @@ include $(topsrcdir)/config/rules.mk
 	browser_tilt_math07.js \
 	browser_tilt_utils01.js \
 	browser_tilt_utils02.js \
 	browser_tilt_utils03.js \
 	browser_tilt_utils04.js \
 	browser_tilt_utils05.js \
 	browser_tilt_utils06.js \
 	browser_tilt_visualizer.js \
+	browser_tilt_zoom.js \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/tilt/test/browser_tilt_controller.js
+++ b/browser/devtools/tilt/test/browser_tilt_controller.js
@@ -40,46 +40,65 @@ function test() {
         }
 
         ok(isEqualVec(tran(), prev_tran),
           "At init, the translation should be zero.");
         ok(isEqualVec(rot(), prev_rot),
           "At init, the rotation should be zero.");
 
 
-        EventUtils.synthesizeKey("VK_A", { type: "keydown" });
-        EventUtils.synthesizeKey("VK_LEFT", { type: "keydown" });
-        instance.controller.update();
+        function testEventCancel(cancellingEvent) {
+          EventUtils.synthesizeKey("VK_A", { type: "keydown" });
+          EventUtils.synthesizeKey("VK_LEFT", { type: "keydown" });
+          instance.controller.update();
+
+          ok(!isEqualVec(tran(), prev_tran),
+            "After a translation key is pressed, the vector should change.");
+          ok(!isEqualVec(rot(), prev_rot),
+            "After a rotation key is pressed, the quaternion should change.");
 
-        ok(!isEqualVec(tran(), prev_tran),
-          "After a translation key is pressed, the vector should change.");
-        ok(!isEqualVec(rot(), prev_rot),
-          "After a rotation key is pressed, the quaternion should change.");
+          save();
+
+
+          cancellingEvent();
+          instance.controller.update();
 
-        save();
+          ok(!isEqualVec(tran(), prev_tran),
+            "Even if the canvas lost focus, the vector has some inertia.");
+          ok(!isEqualVec(rot(), prev_rot),
+            "Even if the canvas lost focus, the quaternion has some inertia.");
+
+          save();
 
 
-        gBrowser.selectedBrowser.contentWindow.focus();
-        instance.controller.update();
+          while (!isEqualVec(tran(), prev_tran) ||
+                 !isEqualVec(rot(), prev_rot)) {
+            instance.controller.update();
+            save();
+          }
 
-        ok(!isEqualVec(tran(), prev_tran),
-          "Even if the canvas lost focus, the vector has some inertia.");
-        ok(!isEqualVec(rot(), prev_rot),
-          "Even if the canvas lost focus, the quaternion has some inertia.");
-
-        save();
-
-
-        while (!isEqualVec(tran(), prev_tran) || !isEqualVec(rot(), prev_rot)) {
-          instance.controller.update();
-          save();
+          ok(isEqualVec(tran(), prev_tran) && isEqualVec(rot(), prev_rot),
+            "After focus lost, the transforms inertia eventually stops.");
         }
 
-        ok(isEqualVec(tran(), prev_tran) && isEqualVec(rot(), prev_rot),
-          "After the focus is lost, the transforms inertia eventually stops.");
+        testEventCancel(function() {
+          EventUtils.synthesizeKey("T", { type: "keydown", altKey: 1 });
+        });
+        testEventCancel(function() {
+          EventUtils.synthesizeKey("I", { type: "keydown", ctrlKey: 1 });
+        });
+        testEventCancel(function() {
+          EventUtils.synthesizeKey("L", { type: "keydown", metaKey: 1 });
+        });
+        testEventCancel(function() {
+          EventUtils.synthesizeKey("T", { type: "keydown", shiftKey: 1 });
+        });
+        testEventCancel(function() {
+          gBrowser.selectedBrowser.contentWindow.focus();
+        });
       },
       onEnd: function()
       {
         gBrowser.removeCurrentTab();
         finish();
       }
     }, true);
   });
--- a/browser/devtools/tilt/test/browser_tilt_gl04.js
+++ b/browser/devtools/tilt/test/browser_tilt_gl04.js
@@ -108,9 +108,19 @@ function test() {
     -0.6889029145240784, 0.6511549949645996, 1.7610820531845093, 0,
     1.5683804750442505, -0.8317608833312988, 0.9210627675056458, 0, 1, 1, 1, 1
   ]), "The identity matrix transformation wasn't applied correctly.");
 
   renderer.origin(1, 1, 1);
   ok(isApproxVec(renderer.mvMatrix, [
     1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
   ]), "The origin wasn't reset to identity correctly.");
+
+  renderer.translate(1, 2);
+  ok(isApproxVec(renderer.mvMatrix, [
+    1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 0, 1
+  ]), "The second translation transformation wasn't applied correctly.");
+
+  renderer.scale(3, 4);
+  ok(isApproxVec(renderer.mvMatrix, [
+    3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1, 0, 1, 2, 0, 1
+  ]), "The second scale transformation wasn't applied correctly.");
 }
--- a/browser/devtools/tilt/test/browser_tilt_visualizer.js
+++ b/browser/devtools/tilt/test/browser_tilt_visualizer.js
@@ -94,16 +94,18 @@ function testPresenter(presenter) {
   ok(isApproxVec(presenter.highlight.v1, [0, 0, 0]),
     "The presenter highlight second vertex should be initially zeroed.");
   ok(isApproxVec(presenter.highlight.v2, [0, 0, 0]),
     "The presenter highlight third vertex should be initially zeroed.");
   ok(isApproxVec(presenter.highlight.v3, [0, 0, 0]),
     "The presenter highlight fourth vertex should be initially zeroed.");
   ok(presenter.transforms,
     "The presenter transforms wasn't initialized properly.");
+  ok(isApproxVec(presenter.transforms.zoom, 1),
+    "The presenter transforms zoom should be initially 1.");
   ok(isApproxVec(presenter.transforms.offset, [0, 0, 0]),
     "The presenter transforms offset should be initially zeroed.");
   ok(isApproxVec(presenter.transforms.translation, [0, 0, 0]),
     "The presenter transforms translation should be initially zeroed.");
   ok(isApproxVec(presenter.transforms.rotation, [0, 0, 0, 1]),
     "The presenter transforms rotation should be initially set to identity.");
 
   presenter.setTranslation([1, 2, 3]);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_zoom.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, executeSoon, gBrowser */
+/*global isApprox, isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, EventUtils, TiltUtils, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+const ZOOM = 2;
+const RESIZE = 50;
+
+function setZoom(value) {
+  gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = value;
+}
+
+function getZoom() {
+  return gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
+}
+
+function test() {
+  setZoom(Math.random());
+  is(getZoom(), TiltUtils.getDocumentZoom(),
+    "The getDocumentZoom utility function didn't return the expected results.");
+
+  if (!isTiltEnabled()) {
+    info("Skipping controller test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping controller test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onInspectorOpen: function()
+      {
+        setZoom(ZOOM);
+      },
+      onTiltOpen: function(instance)
+      {
+        ok(isApprox(instance.presenter.transforms.zoom, ZOOM),
+          "The presenter transforms zoom wasn't initially set correctly.");
+
+        let contentWindow = gBrowser.selectedBrowser.contentWindow;
+        let initialWidth = contentWindow.innerWidth;
+        let initialHeight = contentWindow.innerHeight;
+
+        let renderer = instance.presenter.renderer;
+        let arcball = instance.controller.arcball;
+
+        ok(isApprox(contentWindow.innerWidth * ZOOM, renderer.width, 1),
+          "The renderer width wasn't set correctly.");
+        ok(isApprox(contentWindow.innerHeight * ZOOM, renderer.height, 1),
+          "The renderer height wasn't set correctly.");
+
+        ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
+          "The arcball width wasn't set correctly.");
+        ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
+          "The arcball height wasn't set correctly.");
+
+
+        window.resizeBy(-RESIZE * ZOOM, -RESIZE * ZOOM);
+
+        executeSoon(function() {
+          ok(isApprox(contentWindow.innerWidth + RESIZE, initialWidth, 1),
+            "The content window width wasn't set correctly.");
+          ok(isApprox(contentWindow.innerHeight + RESIZE, initialHeight, 1),
+            "The content window height wasn't set correctly.");
+
+          ok(isApprox(contentWindow.innerWidth * ZOOM, renderer.width, 1),
+            "The renderer width wasn't set correctly.");
+          ok(isApprox(contentWindow.innerHeight * ZOOM, renderer.height, 1),
+            "The renderer height wasn't set correctly.");
+
+          ok(isApprox(contentWindow.innerWidth * ZOOM, arcball.width, 1),
+            "The arcball width wasn't set correctly.");
+          ok(isApprox(contentWindow.innerHeight * ZOOM, arcball.height, 1),
+            "The arcball height wasn't set correctly.");
+
+
+          window.resizeBy(RESIZE * ZOOM, RESIZE * ZOOM);
+
+          Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+          InspectorUI.closeInspectorUI();
+        });
+      },
+    });
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/*global Services, Components, gBrowser, executeSoon */
+/*global Services, Components, gBrowser, executeSoon, info */
 /*global InspectorUI, Tilt, TiltGL, EPSILON */
 "use strict";
 
 Components.utils.import("resource:///modules/devtools/TiltGL.jsm");
 Components.utils.import("resource:///modules/devtools/TiltMath.jsm");
 Components.utils.import("resource:///modules/devtools/TiltUtils.jsm");
 Components.utils.import("resource:///modules/devtools/TiltVisualizer.jsm");
 
@@ -48,38 +48,50 @@ const INSP_ENABLED = Services.prefs.getB
 function isTiltEnabled() {
   return TILT_ENABLED && INSP_ENABLED;
 }
 
 function isWebGLSupported() {
   return TiltGL.isWebGLSupported() && TiltGL.create3DContext(createCanvas());
 }
 
-function isApprox(num1, num2) {
-  return Math.abs(num1 - num2) < EPSILON;
+function isApprox(num1, num2, delta) {
+  if (Math.abs(num1 - num2) > (delta || EPSILON)) {
+    info("isApprox expected " + num1 + ", got " + num2 + " instead.");
+    return false;
+  }
+  return true;
 }
 
-function isApproxVec(vec1, vec2) {
+function isApproxVec(vec1, vec2, delta) {
+  vec1 = Array.prototype.slice.call(vec1);
+  vec2 = Array.prototype.slice.call(vec2);
+
   if (vec1.length !== vec2.length) {
     return false;
   }
   for (let i = 0, len = vec1.length; i < len; i++) {
-    if (!isApprox(vec1[i], vec2[i])) {
+    if (!isApprox(vec1[i], vec2[i], delta)) {
+      info("isApproxVec expected [" + vec1 + "], got [" + vec2 + "] instead.");
       return false;
     }
   }
   return true;
 }
 
 function isEqualVec(vec1, vec2) {
+  vec1 = Array.prototype.slice.call(vec1);
+  vec2 = Array.prototype.slice.call(vec2);
+
   if (vec1.length !== vec2.length) {
     return false;
   }
   for (let i = 0, len = vec1.length; i < len; i++) {
     if (vec1[i] !== vec2[i]) {
+      info("isEqualVec expected [" + vec1 + "], got [" + vec2 + "] instead.");
       return false;
     }
   }
   return true;
 }
 
 function createCanvas() {
   return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");