Bug 890156 - patch 6 - Update window placement code to work with desktop pixels, for per-monitor DPI support on Windows. r=emk
authorJonathan Kew <jkew@mozilla.com>
Thu, 03 Dec 2015 13:19:50 +0000
changeset 279797 153848bbb30d26dacc0972a379417dcd4f02249f
parent 279796 c35bafe04d6f6c8e75b9435097edd11d323bfb97
child 279798 1be1d936e2a2a2d83f446285a8cd65ccdf632906
push id29889
push userryanvm@gmail.com
push dateThu, 14 Jan 2016 01:25:48 +0000
treeherdermozilla-central@3c473ad89a25 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs890156
milestone46.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 890156 - patch 6 - Update window placement code to work with desktop pixels, for per-monitor DPI support on Windows. r=emk
browser/components/sessionstore/SessionStore.jsm
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
gfx/layers/apz/util/APZEventState.cpp
gfx/src/nsDeviceContext.cpp
layout/base/Units.h
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsMenuPopupFrame.h
layout/xul/nsResizerFrame.cpp
view/nsView.cpp
xpfe/appshell/nsXULWindow.cpp
xpfe/appshell/nsXULWindow.h
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3471,26 +3471,26 @@ var SessionStoreInternal = {
       if (aTop < screenTop.value) {
         aTop = screenTop.value;
       } else if (aTop + aHeight > screenTop.value + screenHeight.value) {
         aTop = screenTop.value + screenHeight.value - aHeight;
       }
     }
 
     // only modify those aspects which aren't correct yet
+    if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
+      aWindow.moveTo(aLeft, aTop);
+    }
     if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height"))) {
       // Don't resize the window if it's currently maximized and we would
       // maximize it again shortly after.
       if (aSizeMode != "maximized" || win_("sizemode") != "maximized") {
         aWindow.resizeTo(aWidth, aHeight);
       }
     }
-    if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
-      aWindow.moveTo(aLeft, aTop);
-    }
     if (aSizeMode && win_("sizemode") != aSizeMode)
     {
       switch (aSizeMode)
       {
       case "maximized":
         aWindow.maximize();
         break;
       case "minimized":
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5024,43 +5024,48 @@ nsGlobalWindow::SetOuterHeight(int32_t a
 void
 nsGlobalWindow::SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
                                ErrorResult& aError)
 {
   SetReplaceableWindowCoord(aCx, &nsGlobalWindow::SetOuterHeight,
                             aValue, "outerHeight", aError);
 }
 
-nsIntPoint
+DesktopIntPoint
 nsGlobalWindow::GetScreenXY(ErrorResult& aError)
 {
   MOZ_ASSERT(IsOuterWindow());
 
   // When resisting fingerprinting, always return (0,0)
   if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
-    return nsIntPoint(0, 0);
+    return DesktopIntPoint(0, 0);
   }
 
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
-    return nsIntPoint(0, 0);
+    return DesktopIntPoint(0, 0);
   }
 
   int32_t x = 0, y = 0;
   aError = treeOwnerAsWin->GetPosition(&x, &y);
-  return nsIntPoint(x, y);
+
+  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));
 }
 
 int32_t
 nsGlobalWindow::GetScreenXOuter(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
-  return DevToCSSIntPixels(GetScreenXY(aError).x);
+  return GetScreenXY(aError).x;
 }
 
 int32_t
 nsGlobalWindow::GetScreenX(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetScreenXOuter, (aError), aError, 0);
 }
 
@@ -5294,17 +5299,17 @@ nsGlobalWindow::SetScreenX(JSContext* aC
                             aValue, "screenX", aError);
 }
 
 int32_t
 nsGlobalWindow::GetScreenYOuter(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
-  return DevToCSSIntPixels(GetScreenXY(aError).y);
+  return GetScreenXY(aError).y;
 }
 
 int32_t
 nsGlobalWindow::GetScreenY(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aError), aError, 0);
 }
 
@@ -6997,23 +7002,25 @@ nsGlobalWindow::MoveToOuter(int32_t aXPo
   }
 
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  // Mild abuse of a "size" object so we don't need more helper functions.
-  nsIntSize cssPos(aXPos, aYPos);
-  CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerIsChrome);
-
-  nsIntSize devPos = CSSToDevIntPixels(cssPos);
-
-  aError = treeOwnerAsWin->SetPosition(devPos.width, devPos.height);
+  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;
+
+  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
@@ -1601,18 +1601,18 @@ protected:
   nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
                                   const nsAString& aPseudoElt,
                                   bool aDefaultStylesOnly,
                                   nsIDOMCSSStyleDeclaration** aReturn);
 
   // Outer windows only.
   void PreloadLocalStorage();
 
-  // Returns device pixels.  Outer windows only.
-  nsIntPoint GetScreenXY(mozilla::ErrorResult& aError);
+  // Returns desktop pixels.  Outer windows only.
+  mozilla::DesktopIntPoint 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/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -176,17 +176,17 @@ APZEventState::ProcessSingleTap(const CS
   }
 
   if (mTouchEndCancelled) {
     return;
   }
 
   LayoutDevicePoint currentPoint =
       APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid)
-    * widget->GetDefaultScale();;
+    * widget->GetDefaultScale();
   if (!mActiveElementManager->ActiveElementUsesStyle()) {
     // If the active element isn't visually affected by the :active style, we
     // have no need to wait the extra sActiveDurationMs to make the activation
     // visually obvious to the user.
     APZCCallbackHelper::FireSingleTapEvent(currentPoint, aModifiers, widget);
     return;
   }
 
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -653,16 +653,18 @@ nsDeviceContext::ComputeFullAreaUsingScr
 //
 void
 nsDeviceContext::FindScreen(nsIScreen** outScreen)
 {
     if (!mWidget || !mScreenManager) {
         return;
     }
 
+    CheckDPIChange();
+
     if (mWidget->GetOwningTabChild()) {
         mScreenManager->ScreenForNativeWidget((void *)mWidget->GetOwningTabChild(),
                                               outScreen);
     }
     else if (mWidget->GetNativeData(NS_NATIVE_WINDOW)) {
         mScreenManager->ScreenForNativeWidget(mWidget->GetNativeData(NS_NATIVE_WINDOW),
                                               outScreen);
     }
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -297,16 +297,20 @@ struct LayoutDevicePixel {
   static LayoutDeviceIntPoint FromAppUnitsToNearest(const nsPoint& aPoint, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntPoint::FromUnknownPoint(aPoint.ToNearestPixels(aAppUnitsPerDevPixel));
   }
 
   static LayoutDeviceIntRect FromAppUnitsToNearest(const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntRect::FromUnknownRect(aRect.ToNearestPixels(aAppUnitsPerDevPixel));
   }
 
+  static LayoutDeviceIntRect FromAppUnitsToInside(const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
+    return LayoutDeviceIntRect::FromUnknownRect(aRect.ToInsidePixels(aAppUnitsPerDevPixel));
+  }
+
   static LayoutDeviceIntSize FromAppUnitsRounded(const nsSize& aSize, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntSize(
       NSAppUnitsToIntPixels(aSize.width, aAppUnitsPerDevPixel),
       NSAppUnitsToIntPixels(aSize.height, aAppUnitsPerDevPixel));
   }
 
   static nsPoint ToAppUnits(const LayoutDeviceIntPoint& aPoint, nscoord aAppUnitsPerDevPixel) {
     return nsPoint(aPoint.x * aAppUnitsPerDevPixel,
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1423,18 +1423,27 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
 #else
     // Other OS screen positioned popups can be flipped vertically but never horizontally
     vFlip = FlipStyle_Outside;
 #endif // #ifdef XP_MACOSX
   }
 
   // If a panel is being moved or has flip="none", don't constrain or flip it. But always do this for
   // content shells, so that the popup doesn't extend outside the containing frame.
-  if (mInContentShell || (mFlip != FlipType_None && (!aIsMove || mPopupType != ePopupTypePanel))) {
-    nsRect screenRect = GetConstraintRect(anchorRect, rootScreenRect, popupLevel);
+  if (mInContentShell || (mFlip != FlipType_None &&
+                          (!aIsMove || mPopupType != ePopupTypePanel))) {
+    int32_t appPerDev = presContext->AppUnitsPerDevPixel();
+    LayoutDeviceIntRect anchorRectDevPix =
+      LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
+    LayoutDeviceIntRect rootScreenRectDevPix =
+      LayoutDeviceIntRect::FromAppUnitsToNearest(rootScreenRect, appPerDev);
+    LayoutDeviceIntRect screenRectDevPix =
+      GetConstraintRect(anchorRectDevPix, rootScreenRectDevPix, popupLevel);
+    nsRect screenRect =
+      LayoutDeviceIntRect::ToAppUnits(screenRectDevPix, appPerDev);
 
     // Ensure that anchorRect is on screen.
     anchorRect = anchorRect.Intersect(screenRect);
 
     // shrink the the popup down if it is larger than the screen size
     if (mRect.width > screenRect.width)
       mRect.width = screenRect.width;
     if (mRect.height > screenRect.height)
@@ -1522,62 +1531,57 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
 }
 
 /* virtual */ nsMenuFrame*
 nsMenuPopupFrame::GetCurrentMenuItem()
 {
   return mCurrentMenu;
 }
 
-nsRect
-nsMenuPopupFrame::GetConstraintRect(const nsRect& aAnchorRect,
-                                    const nsRect& aRootScreenRect,
+LayoutDeviceIntRect
+nsMenuPopupFrame::GetConstraintRect(const LayoutDeviceIntRect& aAnchorRect,
+                                    const LayoutDeviceIntRect& aRootScreenRect,
                                     nsPopupLevel aPopupLevel)
 {
-  nsIntRect screenRectPixels;
-  nsPresContext* presContext = PresContext();
+  LayoutDeviceIntRect screenRectPixels;
 
   // determine the available screen space. It will be reduced by the OS chrome
   // such as menubars. It addition, for content shells, it will be the area of
   // the content rather than the screen.
   nsCOMPtr<nsIScreen> screen;
   nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
   if (sm) {
     // for content shells, get the screen where the root frame is located.
     // This is because we need to constrain the content to this content area,
     // so we should use the same screen. Otherwise, use the screen where the
     // anchor is located.
-    nsRect rect = mInContentShell ? aRootScreenRect : aAnchorRect;
-    // nsIScreenManager::ScreenForRect wants the coordinates in CSS pixels
-    int32_t width = std::max(1, nsPresContext::AppUnitsToIntCSSPixels(rect.width));
-    int32_t height = std::max(1, nsPresContext::AppUnitsToIntCSSPixels(rect.height));
-    sm->ScreenForRect(nsPresContext::AppUnitsToIntCSSPixels(rect.x),
-                      nsPresContext::AppUnitsToIntCSSPixels(rect.y),
-                      width, height, getter_AddRefs(screen));
+    LayoutDeviceIntRect rect = mInContentShell ? aRootScreenRect : aAnchorRect;
+    int32_t width = std::max(1, rect.width);
+    int32_t height = std::max(1, rect.height);
+    sm->ScreenForRect(rect.x, rect.y, width, height, getter_AddRefs(screen));
     if (screen) {
       // Non-top-level popups (which will always be panels)
       // should never overlap the OS bar:
       bool dontOverlapOSBar = aPopupLevel != ePopupLevelTop;
       // get the total screen area if the popup is allowed to overlap it.
       if (!dontOverlapOSBar && mMenuCanOverlapOSBar && !mInContentShell)
         screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
-                        &screenRectPixels.width, &screenRectPixels.height);
+          &screenRectPixels.width, &screenRectPixels.height);
       else
         screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
-                             &screenRectPixels.width, &screenRectPixels.height);
+          &screenRectPixels.width, &screenRectPixels.height);
     }
   }
 
-  nsRect screenRect = ToAppUnits(screenRectPixels, presContext->AppUnitsPerDevPixel());
   if (mInContentShell) {
     // for content shells, clip to the client area rather than the screen area
-    screenRect.IntersectRect(screenRect, aRootScreenRect);
+    screenRectPixels.IntersectRect(screenRectPixels, aRootScreenRect);
   }
 
-  return screenRect;
+  return screenRectPixels;
 }
 
 void nsMenuPopupFrame::CanAdjustEdges(int8_t aHorizontalSide,
                                       int8_t aVerticalSide,
                                       LayoutDeviceIntPoint& aChange)
 {
   int8_t popupAlign(mPopupAlignment);
   if (IsDirectionRTL()) {
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -357,18 +357,21 @@ public:
   // For a popup that should appear anchored at the given rect, determine
   // the screen area that it is constrained by. This will be the available
   // area of the screen the popup should be displayed on. Content popups,
   // however, will also be constrained by the content area, given by
   // aRootScreenRect. All coordinates are in app units.
   // For non-toplevel popups (which will always be panels), we will also
   // constrain them to the available screen rect, ie they will not fall
   // underneath the taskbar, dock or other fixed OS elements.
-  nsRect GetConstraintRect(const nsRect& aAnchorRect, const nsRect& aRootScreenRect,
-                           nsPopupLevel aPopupLevel);
+  // This operates in device pixels.
+  mozilla::LayoutDeviceIntRect
+  GetConstraintRect(const mozilla::LayoutDeviceIntRect& aAnchorRect,
+                    const mozilla::LayoutDeviceIntRect& aRootScreenRect,
+                    nsPopupLevel aPopupLevel);
 
   // Determines whether the given edges of the popup may be moved, where
   // aHorizontalSide and aVerticalSide are one of the NS_SIDE_* constants, or
   // 0 for no movement in that direction. aChange is the distance to move on
   // those sides. If will be reset to 0 if the side cannot be adjusted at all
   // in that direction. For example, a popup cannot be moved if it is anchored
   // on a particular side.
   //
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -223,22 +223,25 @@ nsResizerFrame::HandleEvent(nsPresContex
         }
       }
       else if (menuPopupFrame) {
         nsRect frameRect = menuPopupFrame->GetScreenRectInAppUnits();
         nsIFrame* rootFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame();
         nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();
 
         nsPopupLevel popupLevel = menuPopupFrame->PopupLevel();
-        nsRect screenRect = menuPopupFrame->GetConstraintRect(frameRect, rootScreenRect, popupLevel);
-        // round using ToInsidePixels as it's better to be a pixel too small
-        // than be too large. If the popup is too large it could get flipped
-        // to the opposite side of the anchor point while resizing.
-        nsIntRect screenRectPixels = screenRect.ToInsidePixels(aPresContext->AppUnitsPerDevPixel());
-        rect.IntersectRect(rect, LayoutDeviceIntRect::FromUnknownRect(screenRectPixels));
+        int32_t appPerDev = aPresContext->AppUnitsPerDevPixel();
+        LayoutDeviceIntRect screenRect = menuPopupFrame->GetConstraintRect
+          (LayoutDeviceIntRect::FromAppUnitsToNearest(frameRect, appPerDev),
+           // round using ...ToInside as it's better to be a pixel too small
+           // than be too large. If the popup is too large it could get flipped
+           // to the opposite side of the anchor point while resizing.
+           LayoutDeviceIntRect::FromAppUnitsToInside(rootScreenRect, appPerDev),
+           popupLevel);
+        rect.IntersectRect(rect, screenRect);
       }
 
       if (contentToResize) {
         // convert the rectangle into css pixels. When changing the size in a
         // direction, don't allow the new size to be less that the resizer's
         // size. This ensures that content isn't resized too small as to make
         // the resizer invisible.
         nsRect appUnitsRect = ToAppUnits(rect.ToUnknownRect(), aPresContext->AppUnitsPerDevPixel());
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -331,54 +331,53 @@ void nsView::DoResetWidgetBounds(bool aM
     return;
   }
 
   bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
   bool changedSize = curBounds.Size() != newBounds.Size();
 
   // Child views are never attached to top level widgets, this is safe.
 
-  // Coordinates are converted to display pixels for window Move/Resize APIs,
+  // Coordinates are converted to desktop pixels for window Move/Resize APIs,
   // because of the potential for device-pixel coordinate spaces for mixed
   // hidpi/lodpi screens to overlap each other and result in bad placement
   // (bug 814434).
-  double invScale;
+  DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScale();
 
-  // Bug 861270: for correct widget manipulation at arbitrary scale factors,
-  // prefer to base scaling on widget->GetDefaultScale(). But only do this if
-  // it matches the view manager's device context scale after allowing for the
-  // quantization to app units, because of OS X multiscreen issues (where the
-  // only two scales are 1.0 or 2.0, and so the quantization doesn't actually
-  // cause problems anyhow).
-  // In the case of a mismatch, fall back to scaling based on the dev context's
-  // AppUnitsPerDevPixelAtUnitFullZoom value. On platforms where the device-pixel
-  // scale is uniform across all displays (currently all except OS X), we'll
-  // always use the precise value from mWindow->GetDefaultScale here.
-  CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
-  if (NSToIntRound(60.0 / scale.scale) == dx->AppUnitsPerDevPixelAtUnitFullZoom()) {
-    invScale = 1.0 / scale.scale;
-  } else {
-    invScale = dx->AppUnitsPerDevPixelAtUnitFullZoom() / 60.0;
+#ifdef XP_MACOSX
+  // On OS X, this can be called before Cocoa has updated the backing scale
+  // factor of our widget, in which case |scale| is wrong here. To work
+  // around this, we check the device context and override |scale| if it
+  // doesn't match. (This happens when a popup window that has previously
+  // been created and hidden is being moved between hi- and lo-dpi screens,
+  // but is not currently visible; Cocoa doesn't notify it of the scale
+  // factor change until it gets shown on the new screen, which is too late
+  // for us because we'll have already done the computations involving scale
+  // here to move/size it.)
+  // It might be better to avoid this by keeping calculations such as
+  // CalcWidgetBounds entirely in appUnits, rather than using device pixels,
+  // but that seems like a more extensive and potentially risky change.
+  int32_t appPerDev = dx->AppUnitsPerDevPixelAtUnitFullZoom();
+  if (NSToIntRound(60.0 / scale.scale) != appPerDev) {
+    scale = DesktopToLayoutDeviceScale(60.0 / appPerDev);
   }
+#endif
 
+  DesktopRect deskRect = newBounds / scale;
   if (changedPos) {
     if (changedSize && !aMoveOnly) {
-      widget->ResizeClient(newBounds.x * invScale,
-                           newBounds.y * invScale,
-                           newBounds.width * invScale,
-                           newBounds.height * invScale,
+      widget->ResizeClient(deskRect.x, deskRect.y,
+                           deskRect.width, deskRect.height,
                            aInvalidateChangedSize);
     } else {
-      widget->MoveClient(newBounds.x * invScale,
-                         newBounds.y * invScale);
+      widget->MoveClient(deskRect.x, deskRect.y);
     }
   } else {
     if (changedSize && !aMoveOnly) {
-      widget->ResizeClient(newBounds.width * invScale,
-                           newBounds.height * invScale,
+      widget->ResizeClient(deskRect.width, deskRect.height,
                            aInvalidateChangedSize);
     } // else do nothing!
   }
 
   if (!curVisibility && newVisibility) {
     widget->Show(true);
   }
 }
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -564,23 +564,61 @@ NS_IMETHODIMP nsXULWindow::GetDevicePixe
 }
 
 NS_IMETHODIMP nsXULWindow::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
 {
   *aScale = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
   return NS_OK;
 }
 
+DesktopToLayoutDeviceScale
+nsXULWindow::GetScaleForDestinationPosition(int32_t aX, int32_t aY)
+{
+  DesktopToLayoutDeviceScale scale(nsIWidget::DefaultScaleOverride());
+  if (scale.scale <= 0.0) {
+    // The destination monitor may have the different dpi from the source.
+    nsCOMPtr<nsIScreenManager> screenMgr(do_GetService(
+                                         "@mozilla.org/gfx/screenmanager;1"));
+    if (screenMgr) {
+      int32_t width, height;
+      // Use current size, converted to desktop pixels
+      GetSize(&width, &height);
+      DesktopToLayoutDeviceScale curr = mWindow->GetDesktopToDeviceScale();
+      width /= curr.scale;
+      height /= curr.scale;
+      width = std::max(1, width);
+      height = std::max(1, height);
+      nsCOMPtr<nsIScreen> screen;
+      screenMgr->ScreenForRect(aX, aY, width, height,
+                               getter_AddRefs(screen));
+      if (screen) {
+        double contentsScaleFactor;
+        if (NS_SUCCEEDED(screen->GetContentsScaleFactor(
+                         &contentsScaleFactor))) {
+          scale = DesktopToLayoutDeviceScale(contentsScaleFactor);
+        } else {
+          // Fallback to the scale from the widget.
+          scale = mWindow->GetDesktopToDeviceScale();
+        }
+      }
+    } else {
+      // this fallback should never actually be needed
+      scale = mWindow->GetDesktopToDeviceScale();
+    }
+  }
+  return scale;
+}
+
 NS_IMETHODIMP nsXULWindow::SetPosition(int32_t aX, int32_t aY)
 {
   // Don't reset the window's size mode here - platforms that don't want to move
   // maximized windows should reset it in their respective Move implementation.
-  CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
-  double invScale = 1.0 / scale.scale;
-  nsresult rv = mWindow->Move(aX * invScale, aY * invScale);
+  DesktopToLayoutDeviceScale scale = GetScaleForDestinationPosition(aX, aY);
+  DesktopPoint pos = LayoutDeviceIntPoint(aX, aY) / scale;
+  nsresult rv = mWindow->Move(pos.x, pos.y);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   if (!mChromeLoaded) {
     // If we're called before the chrome is loaded someone obviously wants this
     // window at this position. We don't persist this one-time position.
     mIgnoreXULPosition = true;
     return NS_OK;
   }
   PersistentAttributesDirty(PAD_POSITION);
@@ -597,19 +635,19 @@ NS_IMETHODIMP nsXULWindow::SetSize(int32
 {
   /* any attempt to set the window's size or position overrides the window's
      zoom state. this is important when these two states are competing while
      the window is being opened. but it should probably just always be so. */
   mWindow->SetSizeMode(nsSizeMode_Normal);
 
   mIntrinsicallySized = false;
 
-  CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
-  double invScale = 1.0 / scale.scale;
-  nsresult rv = mWindow->Resize(aCX * invScale, aCY * invScale, aRepaint);
+  DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
+  DesktopSize size = LayoutDeviceIntSize(aCX, aCY) / scale;
+  nsresult rv = mWindow->Resize(size.width, size.height, aRepaint);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   if (!mChromeLoaded) {
     // If we're called before the chrome is loaded someone obviously wants this
     // window at this size & in the normal size mode (since it is the only mode
     // in which setting dimensions makes sense). We don't persist this one-time
     // size.
     mIgnoreXULSize = true;
     mIgnoreXULSizeMode = true;
@@ -630,20 +668,19 @@ NS_IMETHODIMP nsXULWindow::SetPositionAn
 {
   /* any attempt to set the window's size or position overrides the window's
      zoom state. this is important when these two states are competing while
      the window is being opened. but it should probably just always be so. */
   mWindow->SetSizeMode(nsSizeMode_Normal);
 
   mIntrinsicallySized = false;
 
-  CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
-  double invScale = 1.0 / scale.scale;
-  nsresult rv = mWindow->Resize(aX * invScale, aY * invScale,
-                                aCX * invScale, aCY * invScale,
+  DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
+  DesktopRect rect = LayoutDeviceIntRect(aX, aY, aCX, aCY) / scale;
+  nsresult rv = mWindow->Resize(rect.x, rect.y, rect.width, rect.height,
                                 aRepaint);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   if (!mChromeLoaded) {
     // If we're called before the chrome is loaded someone obviously wants this
     // window at this size and position. We don't persist this one-time setting.
     mIgnoreXULPosition = true;
     mIgnoreXULSize = true;
     mIgnoreXULSizeMode = true;
@@ -701,18 +738,17 @@ NS_IMETHODIMP nsXULWindow::Center(nsIXUL
 
   if (aRelative) {
     nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative, &result));
     if (base) {
       // get window rect
       result = base->GetPositionAndSize(&left, &top, &width, &height);
       if (NS_SUCCEEDED(result)) {
         double scale;
-        if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
-          // convert device-pixel coordinates to global display pixels
+        if (NS_SUCCEEDED(base->GetDevicePixelsPerDesktopPixel(&scale))) {
           left = NSToIntRound(left / scale);
           top = NSToIntRound(top / scale);
           width = NSToIntRound(width / scale);
           height = NSToIntRound(height / scale);
         }
         // if centering on screen, convert that to the corresponding screen
         if (aScreen)
           screenmgr->ScreenForRect(left, top, width, height, getter_AddRefs(screen));
@@ -739,28 +775,38 @@ NS_IMETHODIMP nsXULWindow::Center(nsIXUL
 
   if (aScreen && screen) {
     screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
     screenCoordinates = true;
   }
 
   if (screenCoordinates || windowCoordinates) {
     NS_ASSERTION(mWindow, "what, no window?");
-    CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
+    double scale = mWindow->GetDesktopToDeviceScale().scale;
     GetSize(&ourWidth, &ourHeight);
-    ourWidth = NSToIntRound(ourWidth / scale.scale);
-    ourHeight = NSToIntRound(ourHeight / scale.scale);
-    left += (width - ourWidth) / 2;
-    top += (height - ourHeight) / (aAlert ? 3 : 2);
+    int32_t scaledWidth, scaledHeight;
+    scaledWidth = NSToIntRound(ourWidth / scale);
+    scaledHeight = NSToIntRound(ourHeight / scale);
+    left += (width - scaledWidth) / 2;
+    top += (height - scaledHeight) / (aAlert ? 3 : 2);
     if (windowCoordinates) {
       mWindow->ConstrainPosition(false, &left, &top);
     }
-    SetPosition(left * scale.scale, top * scale.scale);
+    SetPosition(left * scale, top * scale);
+
+    // If moving the window caused it to change size,
+    // re-do the centering.
+    int32_t newWidth, newHeight;
+    GetSize(&newWidth, &newHeight);
+    if (newWidth != ourWidth || newHeight != ourHeight) {
+      return Center(aRelative, aScreen, aAlert);
+    }
     return NS_OK;
   }
+
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP nsXULWindow::Repaint(bool aForce)
 {
   //XXX First Check In
   NS_ASSERTION(false, "Not Yet Implemented");
   return NS_OK;
@@ -1015,16 +1061,29 @@ NS_IMETHODIMP nsXULWindow::EnsureAuthPro
 void nsXULWindow::OnChromeLoaded()
 {
   nsresult rv = EnsureContentTreeOwner();
 
   if (NS_SUCCEEDED(rv)) {
     mChromeLoaded = true;
     ApplyChromeFlags();
     SyncAttributesToWidget();
+
+    bool positionSet = !mIgnoreXULPosition;
+    nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+    // don't override WM placement on unix for independent, top-level windows
+    // (however, we think the benefits of intelligent dependent window placement
+    // trump that override.)
+    if (!parentWindow)
+      positionSet = false;
+#endif
+    if (positionSet)
+      positionSet = LoadPositionFromXUL();
+
     if (!mIgnoreXULSize)
       LoadSizeFromXUL();
     if (mIntrinsicallySized) {
       // (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false)
       nsCOMPtr<nsIContentViewer> cv;
       mDocShell->GetContentViewer(getter_AddRefs(cv));
       if (cv) {
         nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = do_QueryInterface(mDocShell);
@@ -1036,27 +1095,16 @@ void nsXULWindow::OnChromeLoaded()
           int32_t width = 0, height = 0;
           if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) {
             treeOwner->SizeShellTo(docShellAsItem, width, height);
           }
         }
       }
     }
 
-    bool positionSet = !mIgnoreXULPosition;
-    nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
-#if defined(XP_UNIX) && !defined(XP_MACOSX)
-    // don't override WM placement on unix for independent, top-level windows
-    // (however, we think the benefits of intelligent dependent window placement
-    // trump that override.)
-    if (!parentWindow)
-      positionSet = false;
-#endif
-    if (positionSet)
-      positionSet = LoadPositionFromXUL();
     LoadMiscPersistentAttributesFromXUL();
 
     if (mCenterAfterLoad && !positionSet)
       Center(parentWindow, parentWindow ? false : true, false);
 
     if (mShowAfterLoad) {
       SetVisibility(true);
       // At this point the window may have been closed during Show(), so
@@ -1084,21 +1132,21 @@ bool nsXULWindow::LoadPositionFromXUL()
   int32_t currHeight = 0;
   nsresult errorCode;
   int32_t temp;
 
   GetPositionAndSize(&currX, &currY, &currWidth, &currHeight);
 
   // Convert to global display pixels for consistent window management across
   // screens with diverse resolutions
-  CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
-  currX = NSToIntRound(currX / scale.scale);
-  currY = NSToIntRound(currY / scale.scale);
-  currWidth = NSToIntRound(currWidth / scale.scale);
-  currHeight = NSToIntRound(currHeight / scale.scale);
+  double scale = mWindow->GetDesktopToDeviceScale().scale;
+  currX = NSToIntRound(currX / scale);
+  currY = NSToIntRound(currY / scale);
+  currWidth = NSToIntRound(currWidth / scale);
+  currHeight = NSToIntRound(currHeight / scale);
 
   // Obtain the position information from the <xul:window> element.
   int32_t specX = currX;
   int32_t specY = currY;
   nsAutoString posString;
 
   windowElement->GetAttribute(SCREENX_ATTRIBUTE, posString);
   temp = posString.ToInteger(&errorCode);
@@ -1115,32 +1163,34 @@ bool nsXULWindow::LoadPositionFromXUL()
 
   if (gotPosition) {
     // our position will be relative to our parent, if any
     nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
     if (parent) {
       int32_t parentX, parentY;
       if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
         double scale;
-        if (NS_SUCCEEDED(parent->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
+        if (NS_SUCCEEDED(parent->GetDevicePixelsPerDesktopPixel(&scale))) {
           parentX = NSToIntRound(parentX / scale);
           parentY = NSToIntRound(parentY / scale);
         }
         specX += parentX;
         specY += parentY;
       }
     }
     else {
       StaggerPosition(specX, specY, currWidth, currHeight);
     }
   }
   mWindow->ConstrainPosition(false, &specX, &specY);
   if (specX != currX || specY != currY) {
-    CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
-    SetPosition(specX * scale.scale, specY * scale.scale);
+    DesktopToLayoutDeviceScale destScale =
+      GetScaleForDestinationPosition(specX, specY);
+    LayoutDevicePoint devPos = DesktopIntPoint(specX, specY) * destScale;
+    SetPosition(devPos.x, devPos.y);
   }
 
   return gotPosition;
 }
 
 bool nsXULWindow::LoadSizeFromXUL()
 {
   bool     gotSize = false;
@@ -1155,21 +1205,22 @@ bool nsXULWindow::LoadSizeFromXUL()
 
   int32_t currWidth = 0;
   int32_t currHeight = 0;
   nsresult errorCode;
   int32_t temp;
 
   NS_ASSERTION(mWindow, "we expected to have a window already");
 
-  CSSToLayoutDeviceScale scale = mWindow ? mWindow->GetDefaultScale()
-                                         : CSSToLayoutDeviceScale(1.0);
   GetSize(&currWidth, &currHeight);
-  currWidth = NSToIntRound(currWidth / scale.scale);
-  currHeight = NSToIntRound(currHeight / scale.scale);
+  double displayToDevPx =
+    mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
+  double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
+  currWidth = NSToIntRound(currWidth * displayToDevPx / cssToDevPx);
+  currHeight = NSToIntRound(currHeight * displayToDevPx / cssToDevPx);
 
   // Obtain the position and sizing information from the <xul:window> element.
   int32_t specWidth = currWidth;
   int32_t specHeight = currHeight;
   nsAutoString sizeString;
 
   windowElement->GetAttribute(WIDTH_ATTRIBUTE, sizeString);
   temp = sizeString.ToInteger(&errorCode);
@@ -1188,29 +1239,28 @@ bool nsXULWindow::LoadSizeFromXUL()
     // constrain to screen size
     nsCOMPtr<nsIDOMWindow> domWindow;
     GetWindowDOMWindow(getter_AddRefs(domWindow));
     if (nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(domWindow)) {
       nsCOMPtr<nsIDOMScreen> screen = window->GetScreen();
       if (screen) {
         int32_t screenWidth;
         int32_t screenHeight;
-        screen->GetAvailWidth(&screenWidth);
+        screen->GetAvailWidth(&screenWidth); // CSS pixels
         screen->GetAvailHeight(&screenHeight);
         if (specWidth > screenWidth)
           specWidth = screenWidth;
         if (specHeight > screenHeight)
           specHeight = screenHeight;
       }
     }
 
     mIntrinsicallySized = false;
     if (specWidth != currWidth || specHeight != currHeight) {
-      CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
-      SetSize(specWidth * scale.scale, specHeight * scale.scale, false);
+      SetSize(specWidth * cssToDevPx, specHeight * cssToDevPx, false);
     }
   }
 
   return gotSize;
 }
 
 /* Miscellaneous persistent attributes are attributes named in the
    |persist| attribute, other than size and position. Those are special
@@ -1492,17 +1542,19 @@ NS_IMETHODIMP nsXULWindow::SavePersisten
   if (nsPIDOMWindow* domWindow = mDocShell->GetWindow()) {
     isFullscreen = domWindow->GetFullScreen();
   }
 
   // get our size, position and mode to persist
   LayoutDeviceIntRect rect;
   bool gotRestoredBounds = NS_SUCCEEDED(mWindow->GetRestoredBounds(rect));
 
-  CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
+  // we use CSS pixels for size, but desktop pixels for position
+  CSSToLayoutDeviceScale sizeScale = mWindow->GetDefaultScale();
+  DesktopToLayoutDeviceScale posScale = mWindow->GetDesktopToDeviceScale();
 
   // make our position relative to our parent, if any
   nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
   if (parent && gotRestoredBounds) {
     int32_t parentX, parentY;
     if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
       rect.x -= parentX;
       rect.y -= parentY;
@@ -1520,44 +1572,48 @@ NS_IMETHODIMP nsXULWindow::SavePersisten
     docShellElement->GetId(windowElementId);
   }
 
   bool shouldPersist = !isFullscreen && ownerXULDoc;
   ErrorResult rv;
   // (only for size elements which are persisted)
   if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {
     if (persistString.Find("screenX") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.x / scale.scale));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d",
+                  NSToIntRound(rect.x / posScale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv);
       if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, SCREENX_ATTRIBUTE);
       }
     }
     if (persistString.Find("screenY") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.y / scale.scale));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d",
+                  NSToIntRound(rect.y / posScale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv);
       if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, SCREENY_ATTRIBUTE);
       }
     }
   }
 
   if ((mPersistentAttributesDirty & PAD_SIZE) && gotRestoredBounds) {
     if (persistString.Find("width") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.width / scale.scale));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d",
+                  NSToIntRound(rect.width / sizeScale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv);
       if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, WIDTH_ATTRIBUTE);
       }
     }
     if (persistString.Find("height") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.height / scale.scale));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d",
+                  NSToIntRound(rect.height / sizeScale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv);
       if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, HEIGHT_ATTRIBUTE);
       }
     }
   }
 
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -13,16 +13,17 @@
 
 // Helper classes
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
 #include "nsCOMArray.h"
 #include "nsRect.h"
+#include "Units.h"
 
 // Interfaces needed
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMWindow.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -84,16 +85,19 @@ protected:
    virtual ~nsXULWindow();
 
    NS_IMETHOD EnsureChromeTreeOwner();
    NS_IMETHOD EnsureContentTreeOwner();
    NS_IMETHOD EnsurePrimaryContentTreeOwner();
    NS_IMETHOD EnsurePrompter();
    NS_IMETHOD EnsureAuthPrompter();
 
+   mozilla::DesktopToLayoutDeviceScale
+   GetScaleForDestinationPosition(int32_t aX, int32_t aY);
+
    void OnChromeLoaded();
    void StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
                         int32_t aSpecWidth, int32_t aSpecHeight);
    bool       LoadPositionFromXUL();
    bool       LoadSizeFromXUL();
    bool       LoadMiscPersistentAttributesFromXUL();
    void       SyncAttributesToWidget();
    NS_IMETHOD SavePersistentAttributes();