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 283403 7d6e0141de4fab594b48d54663a4cc90fc78e248
parent 283402 69f7ba132363aa783954f185a5c88b16582da924
child 283404 d1e05b9116cc9725951440e05c65b2e57fea881f
push id17482
push userphilringnalda@gmail.com
push dateSun, 07 Feb 2016 23:32:05 +0000
treeherderfx-team@a0d0344ed47a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs1240085
milestone47.0a1
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;
 };