bug 674373 pt 5 - provide a fullZoom API in nsIDOMWindowUtils, and use this rather than inferring zoom from CSS to device pixel ratio. r=roc
authorJonathan Kew <jkew@mozilla.com>
Sat, 29 Sep 2012 12:35:19 +0100
changeset 108735 550641381dfaf0e8144e4aec9e458dfd8c125aeb
parent 108734 4e8929729b4caefbccbd752c27a34be60f8b71f3
child 108736 1d3de8da2508e3acdaa3ddf1e0769472d3704f0b
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersroc
bugs674373
milestone18.0a1
bug 674373 pt 5 - provide a fullZoom API in nsIDOMWindowUtils, and use this rather than inferring zoom from CSS to device pixel ratio. r=roc
browser/base/content/browser.js
browser/devtools/highlighter/highlighter.jsm
browser/devtools/shared/LayoutHelpers.jsm
content/events/test/test_bug574663.html
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
gfx/src/nsDeviceContext.cpp
gfx/src/nsDeviceContext.h
layout/style/test/test_moz_device_pixel_ratio.html
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -728,17 +728,17 @@ const gFormSubmitObserver = {
                      .getInterface(Components.interfaces.nsIDOMWindowUtils);
 
       if (style.direction == 'rtl') {
         offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
       } else {
         offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
       }
 
-      offset = Math.round(offset * utils.screenPixelsPerCSSPixel);
+      offset = Math.round(offset * utils.fullZoom);
 
       position = "after_start";
     }
 
     this.panel.openPopup(element, position, offset, 0);
   }
 };
 
@@ -7546,19 +7546,19 @@ var MousePosTracker = {
     var index = this._listeners.indexOf(listener);
     if (index < 0)
       return;
 
     this._listeners.splice(index, 1);
   },
 
   handleEvent: function (event) {
-    var screenPixelsPerCSSPixel = this._windowUtils.screenPixelsPerCSSPixel;
-    this._x = event.screenX / screenPixelsPerCSSPixel - window.mozInnerScreenX;
-    this._y = event.screenY / screenPixelsPerCSSPixel - window.mozInnerScreenY;
+    var fullZoom = this._windowUtils.fullZoom;
+    this._x = event.screenX / fullZoom - window.mozInnerScreenX;
+    this._y = event.screenY / fullZoom - window.mozInnerScreenY;
 
     this._listeners.forEach(function (listener) {
       try {
         this._callListener(listener);
       } catch (e) {
         Cu.reportError(e);
       }
     }, this);
--- a/browser/devtools/highlighter/highlighter.jsm
+++ b/browser/devtools/highlighter/highlighter.jsm
@@ -689,17 +689,17 @@ Highlighter.prototype = {
 
   /**
    * Store page zoom factor.
    */
   computeZoomFactor: function Highlighter_computeZoomFactor() {
     this.zoom =
       this.win.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils)
-      .screenPixelsPerCSSPixel;
+      .fullZoom;
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Emitter Mechanism
 
   addListener: function Highlighter_addListener(aEvent, aListener)
   {
     if (!(aEvent in this.events))
--- a/browser/devtools/shared/LayoutHelpers.jsm
+++ b/browser/devtools/shared/LayoutHelpers.jsm
@@ -170,17 +170,17 @@ LayoutHelpers = {
   /**
    * Apply the page zoom factor.
    */
   getZoomedRect: function LH_getZoomedRect(aWin, aRect) {
     // get page zoom factor, if any
     let zoom =
       aWin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
         .getInterface(Components.interfaces.nsIDOMWindowUtils)
-        .screenPixelsPerCSSPixel;
+        .fullZoom;
 
     // adjust rect for zoom scaling
     let aRectScaled = {};
     for (let prop in aRect) {
       aRectScaled[prop] = aRect[prop] * zoom;
     }
 
     return aRectScaled;
--- a/content/events/test/test_bug574663.html
+++ b/content/events/test/test_bug574663.html
@@ -57,32 +57,32 @@ function runTest() {
         win.close();
         clearPrefs();
         SimpleTest.finish();
         return;
       }
 
       let [ctrlKey, isMomentum] = outstandingTests.shift();
       let scrollTopBefore = scrollbox.scrollTop;
-      let zoomFactorBefore = winUtils.screenPixelsPerCSSPixel;
+      let zoomFactorBefore = winUtils.fullZoom;
       sendTouchpadScrollMotion(scrollbox, 1, ctrlKey, isMomentum);
 
       setTimeout(function () {
         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         if (!ctrlKey) {
           let postfix = isMomentum ? ", even after releasing the touchpad" : "";
           // Normal scroll: scroll
-          is(winUtils.screenPixelsPerCSSPixel, zoomFactorBefore, "Normal scrolling shouldn't change zoom" + postfix);
+          is(winUtils.fullZoom, zoomFactorBefore, "Normal scrolling shouldn't change zoom" + postfix);
           isnot(scrollbox.scrollTop, scrollTopBefore, "Normal scrolling should scroll" + postfix);
         } else {
           if (!isMomentum) {
-            isnot(winUtils.screenPixelsPerCSSPixel, zoomFactorBefore, "Ctrl-scrolling should zoom while the user is touching the touchpad");
+            isnot(winUtils.fullZoom, zoomFactorBefore, "Ctrl-scrolling should zoom while the user is touching the touchpad");
             is(scrollbox.scrollTop, scrollTopBefore, "Ctrl-scrolling shouldn't scroll while the user is touching the touchpad");
           } else {
-            is(winUtils.screenPixelsPerCSSPixel, zoomFactorBefore, "Momentum scrolling shouldn't zoom, even when pressing Ctrl");
+            is(winUtils.fullZoom, zoomFactorBefore, "Momentum scrolling shouldn't zoom, even when pressing Ctrl");
             isnot(scrollbox.scrollTop, scrollTopBefore, "Momentum scrolling should scroll, even when pressing Ctrl");
           }
         }
         // Revert the effect.
         sendTouchpadScrollMotion(scrollbox, -1, ctrlKey, isMomentum);
         setTimeout(nextTest, 20);
       }, 20);
     }
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1506,16 +1506,35 @@ NS_IMETHODIMP
 nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   return window->GetDevicePixelRatio(aScreenPixels);
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetFullZoom(float* aFullZoom)
+{
+  *aFullZoom = 1.0f;
+
+  if (!nsContentUtils::IsCallerTrustedForRead()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsPresContext* presContext = GetPresContext();
+  if (!presContext) {
+    return NS_OK;
+  }
+
+  *aFullZoom = presContext->DeviceContext()->GetPixelScale();
+
+  return NS_OK;
+}
+ 
+NS_IMETHODIMP
 nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget,
                                                nsIDOMEvent* aEvent,
                                                bool aTrusted,
                                                bool* aRetVal)
 {
   if (!nsContentUtils::IsCallerTrustedForRead()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -35,17 +35,17 @@ interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 
-[scriptable, uuid(90d8e97b-2c61-4c05-9f1c-e568d22f5bdc)]
+[scriptable, uuid(47405734-D827-44AE-AC01-EF10E80F5D04)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -693,16 +693,24 @@ interface nsIDOMWindowUtils : nsISupport
   readonly attribute unsigned long IMEStatus;
 
   /**
    * Get the number of screen pixels per CSS pixel.
    */
   readonly attribute float screenPixelsPerCSSPixel;
 
   /**
+   * Get the current zoom factor.
+   * This is _approximately_ the same as nsIMarkupDocumentViewer.fullZoom,
+   * but takes into account Gecko's quantization of the zoom factor, which is
+   * implemented by adjusting the (integer) number of appUnits per devPixel.
+   */
+  readonly attribute float fullZoom;
+
+  /**
    * Dispatches aEvent via the nsIPresShell object of the window's document.
    * The event is dispatched to aTarget, which should be an object
    * which implements nsIContent interface (#element, #text, etc).
    *
    * Cannot be accessed from unprivileged context (not
    * content-accessible) Will throw a DOM security error if called
    * without UniversalXPConnect privileges.
    *
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -729,9 +729,11 @@ nsDeviceContext::SetPixelScale(float aSc
     return oldAppUnitsPerDevPixel != mAppUnitsPerDevPixel;
 }
 
 void
 nsDeviceContext::UpdateScaledAppUnits()
 {
     mAppUnitsPerDevPixel =
         NS_MAX(1, NSToIntRound(float(mAppUnitsPerDevNotScaledPixel) / mPixelScale));
+    // adjust mPixelScale to reflect appunit rounding
+    mPixelScale = float(mAppUnitsPerDevNotScaledPixel) / mAppUnitsPerDevPixel;
 }
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -218,16 +218,21 @@ public:
     /**
      * Set the pixel scaling factor: all lengths are multiplied by this factor
      * when we convert them to device pixels. Returns whether the ratio of
      * app units to dev pixels changed because of the scale factor.
      */
     bool SetPixelScale(float aScale);
 
     /**
+     * Returns the pixel scaling factor (page zoom factor) applied.
+     */
+    float GetPixelScale() const { return mPixelScale; }
+
+    /**
      * True if this device context was created for printing.
      */
     bool IsPrinterSurface();
 
 protected:
     void SetDPI();
     void ComputeClientRectUsingScreen(nsRect *outRect);
     void ComputeFullAreaUsingScreen(nsRect *outRect);
--- a/layout/style/test/test_moz_device_pixel_ratio.html
+++ b/layout/style/test/test_moz_device_pixel_ratio.html
@@ -33,21 +33,21 @@ function run() {
     SpecialPowers.setFullZoom(window, factor);
     return previous;
   }
 
   function isVisible(divName) {
     return window.getComputedStyle(document.getElementById(divName), null).visibility == "visible";
   }
 
-  function getZoomRatio() {
+  function getScreenPixelsPerCSSPixel() {
     return SpecialPowers.DOMWindowUtils.screenPixelsPerCSSPixel;
   }
 
-  var screenPixelsPerCSSPixel = getZoomRatio();
+  var screenPixelsPerCSSPixel = getScreenPixelsPerCSSPixel();
   var baseRatio = 1.0 * screenPixelsPerCSSPixel;
   var doubleRatio = 2.0 * screenPixelsPerCSSPixel;
   var halfRatio = 0.5 * screenPixelsPerCSSPixel;
   var styleElem = document.getElementsByTagName("style")[1];
   styleElem.textContent = 
       ["@media all and (-moz-device-pixel-ratio: " + baseRatio + ") {",
          "#zoom1 { visibility: visible; }",
        "}",
@@ -56,20 +56,20 @@ function run() {
        "}",
        "@media all and (-moz-device-pixel-ratio: " + halfRatio + ") {",
          "#zoom3 { visibility: visible; }",
        "}"
       ].join("\n");
 
   ok(isVisible("zoom1"), "Base ratio rule should apply at base zoom level");
   ok(!isVisible("zoom2") && !isVisible("zoom3"), "no other rules should apply");
-  var origZoom = zoom(2 * screenPixelsPerCSSPixel);
+  var origZoom = zoom(2);
   ok(isVisible("zoom2"), "Double ratio rule should apply at double zoom level");
   ok(!isVisible("zoom1") && !isVisible("zoom3"), "no other rules should apply");
-  zoom(0.5 * screenPixelsPerCSSPixel);
+  zoom(0.5);
   ok(isVisible("zoom3"), "Half ratio rule should apply at half zoom level");
   ok(!isVisible("zoom1") && !isVisible("zoom2"), "no other rules should apply");
   zoom(origZoom);
 
   SimpleTest.finish();
 }
 </script>
 </pre>