Bug 1240085 - Revert to CSS-pixel units for screenX, screenY, moveTo() APIs, and adjust the origin for secondary displays with differing resolution to avoid overlapping coordinate spaces. r=emk
authorJonathan Kew <jkew@mozilla.com>
Fri, 05 Feb 2016 16:29:33 +0000
changeset 283396 7d6e0141de4fab594b48d54663a4cc90fc78e248
parent 283395 69f7ba132363aa783954f185a5c88b16582da924
child 283397 d1e05b9116cc9725951440e05c65b2e57fea881f
push id71511
push userjkew@mozilla.com
push dateSun, 07 Feb 2016 10:22:25 +0000
treeherdermozilla-inbound@7d6e0141de4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs1240085
milestone47.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 1240085 - Revert to CSS-pixel units for screenX, screenY, moveTo() APIs, and adjust the origin for secondary displays with differing resolution to avoid overlapping coordinate spaces. r=emk
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
gfx/vr/VRDeviceProxy.cpp
widget/cocoa/nsScreenCocoa.h
widget/nsBaseScreen.cpp
widget/nsBaseScreen.h
widget/nsIScreen.idl
widget/uikit/nsScreenManager.h
widget/windows/nsScreenWin.cpp
widget/windows/nsScreenWin.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -143,16 +143,17 @@
 #include "nsITimedChannel.h"
 #include "nsServiceManagerUtils.h"
 #ifdef MOZ_XUL
 #include "nsIDOMXULControlElement.h"
 #include "nsMenuPopupFrame.h"
 #endif
 #include "mozilla/dom/CustomEvent.h"
 #include "nsIJARChannel.h"
+#include "nsIScreenManager.h"
 
 #include "xpcprivate.h"
 
 #ifdef NS_PRINTING
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
 #endif
@@ -5040,40 +5041,64 @@ nsGlobalWindow::SetOuterHeight(int32_t a
 void
 nsGlobalWindow::SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
                                ErrorResult& aError)
 {
   SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetOuterHeight,
                             aValue, "outerHeight", aError);
 }
 
-DesktopIntPoint
+CSSIntPoint
 nsGlobalWindow::GetScreenXY(ErrorResult& aError)
 {
   MOZ_ASSERT(IsOuterWindow());
 
   // When resisting fingerprinting, always return (0,0)
   if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
-    return DesktopIntPoint(0, 0);
+    return CSSIntPoint(0, 0);
   }
 
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
-    return DesktopIntPoint(0, 0);
+    return CSSIntPoint(0, 0);
   }
 
   int32_t x = 0, y = 0;
-  aError = treeOwnerAsWin->GetPosition(&x, &y);
+  aError = treeOwnerAsWin->GetPosition(&x, &y); // LayoutDevice px values
+
+  RefPtr<nsPresContext> presContext;
+  mDocShell->GetPresContext(getter_AddRefs(presContext));
+  if (!presContext) {
+    return CSSIntPoint(x, y);
+  }
+
+  // Find the global desktop coordinate of the top-left of the screen.
+  // We'll use this as a "fake origin" when converting to CSS px units,
+  // to avoid overlapping coordinates in cases such as a hi-dpi screen
+  // placed to the right of a lo-dpi screen on Windows. (Instead, there
+  // may be "gaps" in the resulting CSS px coordinates in some cases.)
+  nsDeviceContext *dc = presContext->DeviceContext();
+  nsRect screenRect;
+  dc->GetRect(screenRect);
+  LayoutDeviceRect screenRectDev =
+    LayoutDevicePixel::FromAppUnits(screenRect, dc->AppUnitsPerDevPixel());
 
   nsCOMPtr<nsIWidget> widget = GetMainWidget();
   DesktopToLayoutDeviceScale scale = widget ? widget->GetDesktopToDeviceScale()
                                             : DesktopToLayoutDeviceScale(1.0);
-  DesktopPoint pt = LayoutDeviceIntPoint(x, y) / scale;
-  return DesktopIntPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
+  DesktopRect screenRectDesk = screenRectDev / scale;
+
+  CSSPoint cssPt =
+    LayoutDevicePoint(x - screenRectDev.x, y - screenRectDev.y) /
+    presContext->CSSToDevPixelScale();
+  cssPt.x += screenRectDesk.x;
+  cssPt.y += screenRectDesk.y;
+
+  return CSSIntPoint(NSToIntRound(cssPt.x), NSToIntRound(cssPt.y));
 }
 
 int32_t
 nsGlobalWindow::GetScreenXOuter(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   return GetScreenXY(aError).x;
@@ -7023,23 +7048,47 @@ nsGlobalWindow::MoveToOuter(int32_t aXPo
   }
 
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  DesktopIntPoint pt(aXPos, aYPos);
-  CheckSecurityLeftAndTop(&pt.x, &pt.y, aCallerIsChrome);
-
-  nsCOMPtr<nsIWidget> widget = GetMainWidget();
-  DesktopToLayoutDeviceScale scale = widget ? widget->GetDesktopToDeviceScale()
-                                            : DesktopToLayoutDeviceScale(1.0);
-  LayoutDevicePoint devPos = pt * scale;
+  nsCOMPtr<nsIScreenManager> screenMgr =
+    do_GetService("@mozilla.org/gfx/screenmanager;1");
+  nsCOMPtr<nsIScreen> screen;
+  if (screenMgr) {
+    CSSIntSize size;
+    GetInnerSize(size);
+    screenMgr->ScreenForRect(aXPos, aYPos, size.width, size.height,
+                             getter_AddRefs(screen));
+  }
+
+  LayoutDevicePoint devPos;
+  if (screen) {
+    int32_t screenLeftDeskPx, screenTopDeskPx, w, h;
+    screen->GetRectDisplayPix(&screenLeftDeskPx, &screenTopDeskPx, &w, &h);
+    CSSIntPoint cssPos(aXPos - screenLeftDeskPx, aYPos - screenTopDeskPx);
+    CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerIsChrome);
+
+    double scale;
+    screen->GetDefaultCSSScaleFactor(&scale);
+    devPos = cssPos * CSSToLayoutDeviceScale(scale);
+
+    int32_t screenLeftDevPx, screenTopDevPx;
+    screen->GetRect(&screenLeftDevPx, &screenTopDevPx, &w, &h);
+    devPos.x += screenLeftDevPx;
+    devPos.y += screenTopDevPx;
+  } else {
+    // We couldn't find a screen? Just assume a 1:1 mapping.
+    CSSIntPoint cssPos(aXPos, aXPos);
+    CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerIsChrome);
+    devPos = cssPos * CSSToLayoutDeviceScale(1.0);
+  }
 
   aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
 }
 
 void
 nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(MoveToOuter, (aXPos, aYPos, aError, nsContentUtils::IsCallerChrome()), aError, );
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1620,18 +1620,18 @@ protected:
   nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
                                   const nsAString& aPseudoElt,
                                   bool aDefaultStylesOnly,
                                   nsIDOMCSSStyleDeclaration** aReturn);
 
   // Outer windows only.
   void PreloadLocalStorage();
 
-  // Returns desktop pixels.  Outer windows only.
-  mozilla::DesktopIntPoint GetScreenXY(mozilla::ErrorResult& aError);
+  // Returns CSS pixels based on primary screen.  Outer windows only.
+  mozilla::CSSIntPoint GetScreenXY(mozilla::ErrorResult& aError);
 
   nsGlobalWindow* InnerForSetTimeoutOrInterval(mozilla::ErrorResult& aError);
 
   void PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                            const nsAString& aTargetOrigin,
                            JS::Handle<JS::Value> aTransfer,
                            mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
--- a/gfx/vr/VRDeviceProxy.cpp
+++ b/gfx/vr/VRDeviceProxy.cpp
@@ -129,16 +129,20 @@ public:
     *aRotation = nsIScreen::ROTATION_0_DEG;
     return NS_OK;
   }
   NS_IMETHOD SetRotation(uint32_t aRotation) override { return NS_ERROR_NOT_AVAILABLE; }
   NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override {
     *aContentsScaleFactor = 1.0;
     return NS_OK;
   }
+  NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) override {
+    *aScaleFactor = 1.0;
+    return NS_OK;
+  }
 
 protected:
   virtual ~FakeScreen() {}
 
   IntRect mScreenRect;
 };
 
 NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen)
--- a/widget/cocoa/nsScreenCocoa.h
+++ b/widget/cocoa/nsScreenCocoa.h
@@ -19,16 +19,20 @@ public:
     NS_IMETHOD GetId(uint32_t* outId);
     NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth);
     NS_IMETHOD GetColorDepth(int32_t* aColorDepth);
     NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor);
+    NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor)
+    {
+      return GetContentsScaleFactor(aScaleFactor);
+    }
 
     NSScreen *CocoaScreen() { return mScreen; }
 
 private:
     CGFloat BackingScaleFactor();
 
     NSScreen *mScreen;
     uint32_t mId;
--- a/widget/nsBaseScreen.cpp
+++ b/widget/nsBaseScreen.cpp
@@ -76,8 +76,15 @@ nsBaseScreen::CheckMinimumBrightness()
 }
 
 NS_IMETHODIMP
 nsBaseScreen::GetContentsScaleFactor(double* aContentsScaleFactor)
 {
   *aContentsScaleFactor = 1.0;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsBaseScreen::GetDefaultCSSScaleFactor(double* aScaleFactor)
+{
+  *aScaleFactor = 1.0;
+  return NS_OK;
+}
--- a/widget/nsBaseScreen.h
+++ b/widget/nsBaseScreen.h
@@ -38,16 +38,18 @@ public:
   NS_IMETHOD GetRotation(uint32_t* aRotation) override {
     *aRotation = nsIScreen::ROTATION_0_DEG;
     return NS_OK;
   }
   NS_IMETHOD SetRotation(uint32_t aRotation) override { return NS_ERROR_NOT_AVAILABLE; }
 
   NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override;
 
+  NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) override;
+
 protected:
   virtual ~nsBaseScreen();
 
   /**
    * Manually set the current level of brightness locking. This is called after
    * we determine, based on the current active locks, what the strongest
    * lock is. You should normally not call this function - it will be
    * called automatically by this class.
--- a/widget/nsIScreen.idl
+++ b/widget/nsIScreen.idl
@@ -69,13 +69,30 @@ interface nsIScreen : nsISupports
   readonly attribute long colorDepth;
   /**
    * Get/set the screen rotation, on platforms that support changing
    * screen rotation.
    */
   attribute unsigned long rotation;
 
   /**
-   * The number of device pixels per screen point in HiDPI mode.
-   * Returns 1.0 if HiDPI mode is disabled or unsupported.
+   * The number of device pixels per desktop pixel for this screen (for
+   * hidpi configurations where there may be multiple device pixels per
+   * desktop px and/or per CSS px).
+   *
+   * This seems poorly named (something like devicePixelsPerDesktopPixel
+   * would be more accurate/explicit), but given that it is exposed to
+   * front-end code and may also be used by add-ons, it's probably not
+   * worth the disruption of changing it.
+   *
+   * Returns 1.0 if HiDPI mode is disabled or unsupported, or if the
+   * host OS uses device pixels as its desktop pixel units (as in Win8.1
+   * per-monitor dpi support).
    */
   readonly attribute double contentsScaleFactor;
+
+  /**
+   * The default number of device pixels per unscaled CSS pixel for this
+   * screen. This is probably what contentsScaleFactor originally meant
+   * to be, prior to confusion between CSS pixels and desktop pixel units.
+   */
+  readonly attribute double defaultCSSScaleFactor;
 };
--- a/widget/uikit/nsScreenManager.h
+++ b/widget/uikit/nsScreenManager.h
@@ -26,16 +26,20 @@ public:
 
     NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight);
     NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth);
     NS_IMETHOD GetColorDepth(int32_t* aColorDepth);
     NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor);
+    NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor)
+    {
+      return GetContentsScaleFactor(aScaleFactor);
+    }
 
 private:
     UIScreen* mScreen;
 };
 
 class UIKitScreenManager : public nsIScreenManager
 {
 public:
--- a/widget/windows/nsScreenWin.cpp
+++ b/widget/windows/nsScreenWin.cpp
@@ -191,8 +191,14 @@ nsScreenWin::GetContentsScaleFactor(doub
   if (widget::WinUtils::IsPerMonitorDPIAware()) {
     *aContentsScaleFactor = 1.0;
   } else {
     *aContentsScaleFactor = widget::WinUtils::LogToPhysFactor(mScreen);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsScreenWin::GetDefaultCSSScaleFactor(double* aScaleFactor)
+{
+  *aScaleFactor = widget::WinUtils::LogToPhysFactor(mScreen);
+  return NS_OK;
+}
--- a/widget/windows/nsScreenWin.h
+++ b/widget/windows/nsScreenWin.h
@@ -28,17 +28,19 @@ public:
   NS_IMETHOD GetRectDisplayPix(int32_t *outLeft,  int32_t *outTop,
                                int32_t *outWidth, int32_t *outHeight);
   NS_IMETHOD GetAvailRectDisplayPix(int32_t *outLeft,  int32_t *outTop,
                                     int32_t *outWidth, int32_t *outHeight);
 
   NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth);
   NS_IMETHOD GetColorDepth(int32_t* aColorDepth);
 
-  NS_IMETHOD GetContentsScaleFactor(double *aContentsScaleFactor) override;
+  NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override;
+
+  NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) override;
 
 private:
   double GetDPIScale();
 
   HMONITOR mScreen;
   uint32_t mId;
 };