Bug 890156 - patch 4 - Update widget/windows code for per-monitor DPI support. r=emk
authorJonathan Kew <jkew@mozilla.com>
Fri, 04 Dec 2015 16:59:14 +0000
changeset 279792 68d1b9a375b0b03f976d254aee2012a8d2b0ddb6
parent 279791 443207a1d886ce2d13bc7328e51dbca2300f8fcd
child 279793 c35bafe04d6f6c8e75b9435097edd11d323bfb97
push id17004
push userryanvm@gmail.com
push dateThu, 14 Jan 2016 01:28:16 +0000
treeherderfx-team@8e354cea3ecd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs890156
milestone46.0a1
Bug 890156 - patch 4 - Update widget/windows code for per-monitor DPI support. r=emk
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowBase.cpp
widget/windows/nsWindowBase.h
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1354,68 +1354,77 @@ NS_METHOD nsBaseWidget::SetWindowClass(c
 }
 
 NS_METHOD nsBaseWidget::MoveClient(double aX, double aY)
 {
   LayoutDeviceIntPoint clientOffset(GetClientOffset());
 
   // GetClientOffset returns device pixels; scale back to desktop pixels
   // if that's what this widget uses for the Move/Resize APIs
-  CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels()
-                                    ? GetDefaultScale()
-                                    : CSSToLayoutDeviceScale(1.0);
-  aX -= clientOffset.x * 1.0 / scale.scale;
-  aY -= clientOffset.y * 1.0 / scale.scale;
-
-  return Move(aX, aY);
+  if (BoundsUseDesktopPixels()) {
+    DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale();
+    return Move(aX - desktopOffset.x, aY - desktopOffset.y);
+  } else {
+    return Move(aX - clientOffset.x, aY - clientOffset.y);
+  }
 }
 
 NS_METHOD nsBaseWidget::ResizeClient(double aWidth,
                                      double aHeight,
                                      bool aRepaint)
 {
   NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
   NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
 
   LayoutDeviceIntRect clientBounds;
   GetClientBounds(clientBounds);
 
   // GetClientBounds and mBounds are device pixels; scale back to desktop pixels
   // if that's what this widget uses for the Move/Resize APIs
-  CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels()
-                                    ? GetDefaultScale()
-                                    : CSSToLayoutDeviceScale(1.0);
-  double invScale = 1.0 / scale.scale;
-  aWidth = mBounds.width * invScale + (aWidth - clientBounds.width * invScale);
-  aHeight = mBounds.height * invScale + (aHeight - clientBounds.height * invScale);
-
-  return Resize(aWidth, aHeight, aRepaint);
+  if (BoundsUseDesktopPixels()) {
+    DesktopSize desktopDelta =
+      (LayoutDeviceIntSize(mBounds.width, mBounds.height) -
+       clientBounds.Size()) / GetDesktopToDeviceScale();
+    return Resize(aWidth + desktopDelta.width, aHeight + desktopDelta.height,
+                  aRepaint);
+  } else {
+    return Resize(mBounds.width + (aWidth - clientBounds.width),
+                  mBounds.height + (aHeight - clientBounds.height), aRepaint);
+  }
 }
 
 NS_METHOD nsBaseWidget::ResizeClient(double aX,
                                      double aY,
                                      double aWidth,
                                      double aHeight,
                                      bool aRepaint)
 {
   NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
   NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
 
   LayoutDeviceIntRect clientBounds;
   GetClientBounds(clientBounds);
 
-  double scale = BoundsUseDesktopPixels() ? 1.0 / GetDefaultScale().scale : 1.0;
-  aWidth = mBounds.width * scale + (aWidth - clientBounds.width * scale);
-  aHeight = mBounds.height * scale + (aHeight - clientBounds.height * scale);
-
   LayoutDeviceIntPoint clientOffset(GetClientOffset());
-  aX -= clientOffset.x * scale;
-  aY -= clientOffset.y * scale;
-
-  return Resize(aX, aY, aWidth, aHeight, aRepaint);
+
+  if (BoundsUseDesktopPixels()) {
+    DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale();
+    DesktopPoint desktopOffset = clientOffset / scale;
+    DesktopSize desktopDelta =
+      (LayoutDeviceIntSize(mBounds.width, mBounds.height) -
+       clientBounds.Size()) / scale;
+    return Resize(aX - desktopOffset.x, aY - desktopOffset.y,
+                  aWidth + desktopDelta.width, aHeight + desktopDelta.height,
+                  aRepaint);
+  } else {
+    return Resize(aX - clientOffset.x, aY - clientOffset.y,
+                  aWidth + mBounds.width - clientBounds.width,
+                  aHeight + mBounds.height - clientBounds.height,
+                  aRepaint);
+  }
 }
 
 //-------------------------------------------------------------------------
 //
 // Bounds
 //
 //-------------------------------------------------------------------------
 
@@ -1652,17 +1661,17 @@ nsBaseWidget::GetGLFrameBufferFormat()
 
 void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints)
 {
   mSizeConstraints = aConstraints;
   // We can't ensure that the size is honored at this point because we're
   // probably in the middle of a reflow.
 }
 
-const widget::SizeConstraints& nsBaseWidget::GetSizeConstraints() const
+const widget::SizeConstraints nsBaseWidget::GetSizeConstraints()
 {
   return mSizeConstraints;
 }
 
 // static
 nsIRollupListener*
 nsBaseWidget::GetActiveRollupListener()
 {
@@ -1836,38 +1845,27 @@ nsBaseWidget::StartAsyncScrollbarDrag(co
 
   int layersId = mCompositorParent->RootLayerTreeId();;
   ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, aDragMetrics.mViewId);
 
   APZThreadUtils::RunOnControllerThread(
     NewRunnableMethod(mAPZC.get(), &APZCTreeManager::StartScrollbarDrag, guid, aDragMetrics));
 }
 
-CSSIntRect
-nsBaseWidget::GetScaledScreenBounds()
-{
-  LayoutDeviceIntRect bounds;
-  GetScreenBounds(bounds);
-
-  // *Dividing* a LayoutDeviceIntRect by a CSSToLayoutDeviceScale gives a
-  // CSSIntRect.
-  CSSToLayoutDeviceScale scale = GetDefaultScale();
-  return RoundedToInt(bounds / scale);
-}
-
 already_AddRefed<nsIScreen>
 nsBaseWidget::GetWidgetScreen()
 {
   nsCOMPtr<nsIScreenManager> screenManager;
   screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
   if (!screenManager) {
     return nullptr;
   }
 
-  CSSIntRect bounds = GetScaledScreenBounds();
+  LayoutDeviceIntRect bounds;
+  GetScreenBounds(bounds);
   nsCOMPtr<nsIScreen> screen;
   screenManager->ScreenForRect(bounds.x, bounds.y,
                                bounds.width, bounds.height,
                                getter_AddRefs(screen));
   return screen.forget();
 }
 
 nsresult
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -293,36 +293,32 @@ public:
   nsPopupLevel PopupLevel() { return mPopupLevel; }
 
   virtual LayoutDeviceIntSize
   ClientToWindowSize(const LayoutDeviceIntSize& aClientSize) override
   {
     return aClientSize;
   }
 
-  // return the widget's outside dimensions
-  // in global coordinates in display pixel.
-  CSSIntRect GetScaledScreenBounds();
-
   // return the screen the widget is in.
   already_AddRefed<nsIScreen> GetWidgetScreen();
 
   // return true if this is a popup widget with a native titlebar
   bool IsPopupWithTitleBar() const
   {
     return (mWindowType == eWindowType_popup && 
             mBorderStyle != eBorderStyle_default &&
             mBorderStyle & eBorderStyle_title);
   }
 
   NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) override = 0;
 
   virtual uint32_t GetGLFrameBufferFormat() override;
 
-  virtual const SizeConstraints& GetSizeConstraints() const override;
+  virtual const SizeConstraints GetSizeConstraints() override;
   virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override;
 
   virtual bool CaptureWidgetOnScreen(RefPtr<mozilla::gfx::DrawTarget> aDT) override {
     return false;
   }
 
   virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override;
 
@@ -466,22 +462,23 @@ protected:
   }
 
   /**
    * Apply the current size constraints to the given size.
    *
    * @param aWidth width to constrain
    * @param aHeight height to constrain
    */
-  void ConstrainSize(int32_t* aWidth, int32_t* aHeight) const
+  void ConstrainSize(int32_t* aWidth, int32_t* aHeight)
   {
-    *aWidth = std::max(mSizeConstraints.mMinSize.width,
-                     std::min(mSizeConstraints.mMaxSize.width, *aWidth));
-    *aHeight = std::max(mSizeConstraints.mMinSize.height,
-                      std::min(mSizeConstraints.mMaxSize.height, *aHeight));
+    SizeConstraints c = GetSizeConstraints();
+    *aWidth = std::max(c.mMinSize.width,
+                       std::min(c.mMaxSize.width, *aWidth));
+    *aHeight = std::max(c.mMinSize.height,
+                        std::min(c.mMaxSize.height, *aHeight));
   }
 
   virtual CompositorChild* GetRemoteRenderer() override;
 
   /**
    * Notify the widget that this window is being used with OMTC.
    */
   virtual void WindowUsesOMTC() {}
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -2022,17 +2022,17 @@ public:
      */
     virtual void SetSizeConstraints(const SizeConstraints& aConstraints) = 0;
 
     /**
      * Return the size constraints currently observed by the widget.
      *
      * @return the constraints in device pixels
      */
-    virtual const SizeConstraints& GetSizeConstraints() const = 0;
+    virtual const SizeConstraints GetSizeConstraints() = 0;
 
     /**
      * If this is owned by a TabChild, return that.  Otherwise return
      * null.
      */
     virtual TabChild* GetOwningTabChild() { return nullptr; }
 
     /**
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -119,17 +119,16 @@
 #include "nsWindowGfx.h"
 #include "gfxWindowsPlatform.h"
 #include "Layers.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Preferences.h"
 #include "nsISound.h"
 #include "SystemTimeConverter.h"
 #include "WinTaskbar.h"
-#include "WinUtils.h"
 #include "WidgetUtils.h"
 #include "nsIWidgetListener.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/gfx/2D.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIAppStartup.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
@@ -179,16 +178,20 @@
 #include <d3d11.h>
 
 #include "InkCollector.h"
 
 #if !defined(SM_CONVERTIBLESLATEMODE)
 #define SM_CONVERTIBLESLATEMODE 0x2003
 #endif
 
+#if !defined(WM_DPICHANGED)
+#define WM_DPICHANGED 0x02E0
+#endif
+
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "InputData.h"
 
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -421,16 +424,17 @@ nsWindow::nsWindow()
   mTransparentSurface   = nullptr;
   mMemoryDC             = nullptr;
   mTransparencyMode     = eTransparencyOpaque;
   memset(&mGlassMargins, 0, sizeof mGlassMargins);
 #endif
   DWORD background      = ::GetSysColor(COLOR_BTNFACE);
   mBrush                = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
   mSendingSetText       = false;
+  mDefaultScale         = -1.0; // not yet set, will be calculated on first use
 
   mTaskbarPreview = nullptr;
 
   // Global initialization
   if (!sInstanceCount) {
     // Global app registration id for Win7 and up. See
     // WinTaskbar.cpp for details.
     mozilla::widget::WinTaskbar::RegisterAppUserModelID();
@@ -451,16 +455,17 @@ nsWindow::nsWindow()
     Preferences::AddBoolVarCache(&gIsPointerEventsEnabled,
                                  "dom.w3c_pointer_events.enabled",
                                  gIsPointerEventsEnabled);
   } // !sInstanceCount
 
   mIdleService = nullptr;
 
   ::InitializeCriticalSection(&mPresentLock);
+  mSizeConstraintsScale = GetDefaultScale().scale;
 
   sInstanceCount++;
 }
 
 nsWindow::~nsWindow()
 {
   mInDtor = true;
 
@@ -1074,16 +1079,19 @@ float nsWindow::GetDPI()
     // Something's broken
     return 96.0f;
   }
   return float(heightPx/heightInches);
 }
 
 double nsWindow::GetDefaultScaleInternal()
 {
+  if (mDefaultScale <= 0.0) {
+    mDefaultScale = WinUtils::LogToPhysFactor(mWnd);
+  }
   return WinUtils::LogToPhysFactor(mWnd);
 }
 
 int32_t nsWindow::LogToPhys(double aValue)
 {
   return WinUtils::LogToPhys(::MonitorFromWindow(mWnd,
                                                  MONITOR_DEFAULTTOPRIMARY),
                              aValue);
@@ -1397,34 +1405,58 @@ BOOL CALLBACK nsWindow::RegisterTouchFor
 void
 nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
 {
   SizeConstraints c = aConstraints;
   if (mWindowType != eWindowType_popup) {
     c.mMinSize.width = std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
     c.mMinSize.height = std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
   }
+  mSizeConstraintsScale = GetDefaultScale().scale;
 
   nsBaseWidget::SetSizeConstraints(c);
 }
 
+const SizeConstraints
+nsWindow::GetSizeConstraints()
+{
+  double scale = GetDefaultScale().scale;
+  if (mSizeConstraintsScale == scale || mSizeConstraintsScale == 0.0) {
+    return mSizeConstraints;
+  }
+  scale /= mSizeConstraintsScale;
+  SizeConstraints c = mSizeConstraints;
+  if (c.mMinSize.width != NS_UNCONSTRAINEDSIZE) {
+    c.mMinSize.width = NSToIntRound(c.mMinSize.width * scale);
+  }
+  if (c.mMinSize.height != NS_UNCONSTRAINEDSIZE) {
+    c.mMinSize.height = NSToIntRound(c.mMinSize.height * scale);
+  }
+  if (c.mMaxSize.width != NS_UNCONSTRAINEDSIZE) {
+    c.mMaxSize.width = NSToIntRound(c.mMaxSize.width * scale);
+  }
+  if (c.mMaxSize.height != NS_UNCONSTRAINEDSIZE) {
+    c.mMaxSize.height = NSToIntRound(c.mMaxSize.height * scale);
+  }
+  return c;
+}
+
 // Move this component
 NS_METHOD nsWindow::Move(double aX, double aY)
 {
   if (mWindowType == eWindowType_toplevel ||
       mWindowType == eWindowType_dialog) {
     SetSizeMode(nsSizeMode_Normal);
   }
 
   // for top-level windows only, convert coordinates from desktop pixels
   // (the "parent" coordinate space) to the window's device pixel space
-  CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale()
-                                    : CSSToLayoutDeviceScale(1.0);
-  int32_t x = NSToIntRound(aX * scale.scale);
-  int32_t y = NSToIntRound(aY * scale.scale);
+  double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
+  int32_t x = NSToIntRound(aX * scale);
+  int32_t y = NSToIntRound(aY * scale);
 
   // Check to see if window needs to be moved first
   // to avoid a costly call to SetWindowPos. This check
   // can not be moved to the calling code in nsView, because
   // some platforms do not position child windows correctly
 
   // Only perform this check for non-popup windows, since the positioning can
   // in fact change even when the x/y do not.  We always need to perform the
@@ -1466,33 +1498,38 @@ NS_METHOD nsWindow::Move(double aX, doub
     // region, some drivers or OSes may incorrectly copy into the clipped-out
     // area.
     if (IsPlugin() &&
         (!mLayerManager || mLayerManager->GetBackendType() == LayersBackend::LAYERS_D3D9) &&
         mClipRects &&
         (mClipRectCount != 1 || !mClipRects[0].IsEqualInterior(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height)))) {
       flags |= SWP_NOCOPYBITS;
     }
+    double oldScale = mDefaultScale;
+    mResizeState = IN_SIZEMOVE;
     VERIFY(::SetWindowPos(mWnd, nullptr, x, y, 0, 0, flags));
+    mResizeState = NOT_RESIZING;
+    if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
+      ChangedDPI();
+    }
 
     SetThemeRegion();
   }
   NotifyRollupGeometryChange();
   return NS_OK;
 }
 
 // Resize this component
 NS_METHOD nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
 {
   // for top-level windows only, convert coordinates from desktop pixels
   // (the "parent" coordinate space) to the window's device pixel space
-  CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale()
-                                    : CSSToLayoutDeviceScale(1.0);
-  int32_t width = NSToIntRound(aWidth * scale.scale);
-  int32_t height = NSToIntRound(aHeight * scale.scale);
+  double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
+  int32_t width = NSToIntRound(aWidth * scale);
+  int32_t height = NSToIntRound(aHeight * scale);
 
   NS_ASSERTION((width >= 0) , "Negative width passed to nsWindow::Resize");
   NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
 
   ConstrainSize(&width, &height);
 
   // Avoid unnecessary resizing calls
   if (mBounds.width == width && mBounds.height == height) {
@@ -1514,39 +1551,42 @@ NS_METHOD nsWindow::Resize(double aWidth
   if (mWnd) {
     UINT  flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE;
 
     if (!aRepaint) {
       flags |= SWP_NOREDRAW;
     }
 
     ClearThemeRegion();
+    double oldScale = mDefaultScale;
     VERIFY(::SetWindowPos(mWnd, nullptr, 0, 0,
                           width, GetHeight(height), flags));
+    if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
+      ChangedDPI();
+    }
     SetThemeRegion();
   }
 
   if (aRepaint)
     Invalidate();
 
   NotifyRollupGeometryChange();
   return NS_OK;
 }
 
 // Resize this component
 NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint)
 {
   // for top-level windows only, convert coordinates from desktop pixels
   // (the "parent" coordinate space) to the window's device pixel space
-  CSSToLayoutDeviceScale scale = BoundsUseDesktopPixels() ? GetDefaultScale()
-                                    : CSSToLayoutDeviceScale(1.0);
-  int32_t x = NSToIntRound(aX * scale.scale);
-  int32_t y = NSToIntRound(aY * scale.scale);
-  int32_t width = NSToIntRound(aWidth * scale.scale);
-  int32_t height = NSToIntRound(aHeight * scale.scale);
+  double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
+  int32_t x = NSToIntRound(aX * scale);
+  int32_t y = NSToIntRound(aY * scale);
+  int32_t width = NSToIntRound(aWidth * scale);
+  int32_t height = NSToIntRound(aHeight * scale);
 
   NS_ASSERTION((width >= 0),  "Negative width passed to nsWindow::Resize");
   NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
 
   ConstrainSize(&width, &height);
 
   // Avoid unnecessary resizing calls
   if (mBounds.x == x && mBounds.y == y &&
@@ -1570,18 +1610,22 @@ NS_METHOD nsWindow::Resize(double aX, do
 
   if (mWnd) {
     UINT  flags = SWP_NOZORDER | SWP_NOACTIVATE;
     if (!aRepaint) {
       flags |= SWP_NOREDRAW;
     }
 
     ClearThemeRegion();
+    double oldScale = mDefaultScale;
     VERIFY(::SetWindowPos(mWnd, nullptr, x, y,
                           width, GetHeight(height), flags));
+    if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
+      ChangedDPI();
+    }
     if (mTransitionWnd) {
       // If we have a fullscreen transition window, we need to make
       // it topmost again, otherwise the taskbar may be raised by
       // the system unexpectedly when we leave fullscreen state.
       ::SetWindowPos(mTransitionWnd, HWND_TOPMOST, 0, 0, 0, 0,
                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
       // Every transition window is only used once.
       mTransitionWnd = nullptr;
@@ -1748,31 +1792,33 @@ nsWindow::SetSizeMode(nsSizeMode aMode) 
     // we activate here to ensure that the right child window is focused
     if (mode == SW_MAXIMIZE || mode == SW_SHOW)
       DispatchFocusToTopLevelWindow(true);
   }
   return rv;
 }
 
 // Constrain a potential move to fit onscreen
-// Position (aX, aY) is specified in Windows screen (logical) pixels
+// Position (aX, aY) is specified in Windows screen (logical) pixels,
+// except when using per-monitor DPI, in which case it's device pixels.
 NS_METHOD nsWindow::ConstrainPosition(bool aAllowSlop,
                                       int32_t *aX, int32_t *aY)
 {
   if (!mIsTopWidgetWindow) // only a problem for top-level windows
     return NS_OK;
 
-  double dpiScale = GetDefaultScale().scale;
-
-  // we need to use the window size in logical screen pixels
+  double dpiScale = GetDesktopToDeviceScale().scale;
+
+  // We need to use the window size in the kind of pixels used for window-
+  // manipulation APIs.
   int32_t logWidth = std::max<int32_t>(NSToIntRound(mBounds.width / dpiScale), 1);
   int32_t logHeight = std::max<int32_t>(NSToIntRound(mBounds.height / dpiScale), 1);
 
   /* get our playing field. use the current screen, or failing that
-    for any reason, use device caps for the default screen. */
+  for any reason, use device caps for the default screen. */
   RECT screenRect;
 
   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
   if (screenmgr) {
     nsCOMPtr<nsIScreen> screen;
     int32_t left, top, width, height;
 
     screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
@@ -3050,21 +3096,21 @@ nsWindow::PrepareForFullscreenTransition
   }
 
   FullscreenTransitionInitData initData;
   nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
   int32_t x, y, width, height;
   screen->GetRectDisplayPix(&x, &y, &width, &height);
   MOZ_ASSERT(BoundsUseDesktopPixels(),
              "Should only be called on top-level window");
-  CSSToLayoutDeviceScale scale = GetDefaultScale();
-  initData.mBounds.x = NSToIntRound(x * scale.scale);
-  initData.mBounds.y = NSToIntRound(y * scale.scale);
-  initData.mBounds.width = NSToIntRound(width * scale.scale);
-  initData.mBounds.height = NSToIntRound(height * scale.scale);
+  double scale = GetDesktopToDeviceScale().scale; // XXX or GetDefaultScale() ?
+  initData.mBounds.x = NSToIntRound(x * scale);
+  initData.mBounds.y = NSToIntRound(y * scale);
+  initData.mBounds.width = NSToIntRound(width * scale);
+  initData.mBounds.height = NSToIntRound(height * scale);
 
   // Create a semaphore for synchronizing the window handle which will
   // be created by the transition thread and used by the main thread for
   // posting the transition messages.
   initData.mSemaphore = ::CreateSemaphore(nullptr, 0, 1, nullptr);
   if (initData.mSemaphore) {
     initData.mThread = ::CreateThread(
       nullptr, 0, FullscreenTransitionThreadProc, &initData, 0, nullptr);
@@ -3796,17 +3842,17 @@ nsWindow::UpdateThemeGeometries(const ns
     return;
   }
 
   // On Win10, force show the top border:
   if (IsWin10OrLater() && mCustomNonClient && mSizeMode == nsSizeMode_Normal) {
     RECT rect;
     ::GetWindowRect(mWnd, &rect);
     // We want 1 pixel of border for every whole 100% of scaling
-    double borderSize = RoundDown(GetDefaultScale().scale);
+    double borderSize = std::min(1, RoundDown(GetDesktopToDeviceScale().scale));
     clearRegion.Or(clearRegion, nsIntRect(0, 0, rect.right - rect.left, borderSize));
   }
 
   if (!IsWin10OrLater()) {
     for (size_t i = 0; i < aThemeGeometries.Length(); i++) {
       if (aThemeGeometries[i].mType == nsNativeThemeWin::eThemeGeometryTypeWindowButtons) {
         LayoutDeviceIntRect bounds = aThemeGeometries[i].mRect;
         clearRegion.Or(clearRegion, nsIntRect(bounds.X(), bounds.Y(), bounds.Width(), bounds.Height() - 2.0));
@@ -5380,23 +5426,22 @@ nsWindow::ProcessMessage(UINT msg, WPARA
         mResizeState = IN_SIZEMOVE;
       }
       break;
     }
 
     case WM_EXITSIZEMOVE:
     {
       if (mResizeState == RESIZING) {
-        mResizeState = NOT_RESIZING;
         nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
-
         if (observerService) {
           observerService->NotifyObservers(nullptr, "live-resize-end", nullptr);
         }
       }
+      mResizeState = NOT_RESIZING;
 
       if (!sIsInMouseCapture) {
         NotifySizeMoveDone();
       }
 
       break;
     }
 
@@ -5592,16 +5637,24 @@ nsWindow::ProcessMessage(UINT msg, WPARA
 
     UpdateNonClientMargins();
     BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
     NotifyThemeChanged();
     UpdateGlass();
     Invalidate(true, true, true);
     break;
 
+  case WM_DPICHANGED:
+  {
+    LPRECT rect = (LPRECT) lParam;
+    OnDPIChanged(rect->left, rect->top, rect->right - rect->left,
+                 rect->bottom - rect->top);
+    break;
+  }
+
   case WM_UPDATEUISTATE:
   {
     // If the UI state has changed, fire an event so the UI updates the
     // keyboard cues based on the system setting and how the window was
     // opened. For example, a dialog opened via a keyboard press on a button
     // should enable cues, whereas the same dialog opened via a mouse click of
     // the button should not.
     int32_t action = LOWORD(wParam);
@@ -6867,16 +6920,47 @@ nsWindow::OnSysColorChanged()
     // The Win32 toolkit normally only sends these events to top-level windows.
     // But we cycle through all of the childwindows and send it to them as well
     // so all presentations get notified properly.
     // See nsWindow::GlobalMsgWindowProc.
     NotifySysColorChanged();
   }
 }
 
+void
+nsWindow::OnDPIChanged(int32_t x, int32_t y, int32_t width, int32_t height)
+{
+  if (DefaultScaleOverride() > 0.0) {
+    return;
+  }
+  double oldScale = mDefaultScale;
+  mDefaultScale = -1.0; // force recomputation of scale factor
+  double newScale = GetDefaultScaleInternal();
+  if (mResizeState != NOT_RESIZING) {
+    // We want to try and maintain the size of the client area, rather than
+    // the overall size of the window including non-client area, so we prefer
+    // to calculate the new size instead of using Windows' suggested values.
+    if (oldScale > 0.0) {
+      double ratio = newScale / oldScale;
+      LayoutDeviceIntRect cr, sr;
+      GetClientBounds(cr);
+      GetScreenBounds(sr);
+      int32_t w = sr.width - cr.width + NSToIntRound(cr.width * ratio);
+      int32_t h = sr.height - cr.height + NSToIntRound(cr.height * ratio);
+      // Adjust x and y to preserve the center point of the suggested rect.
+      x -= (w - width) / 2;
+      y -= (h - height) / 2;
+      width = w;
+      height = h;
+    }
+    Resize(x, y, width, height, true);
+  }
+  ChangedDPI();
+}
+
 /**************************************************************
  **************************************************************
  **
  ** BLOCK: IME management and accessibility
  **
  ** Handles managing IME input and accessibility.
  **
  **************************************************************
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -26,17 +26,17 @@
 #include "nsRegion.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TimeStamp.h"
 #include "nsMargin.h"
 #include "nsRegionFwd.h"
 
 #include "nsWinGesture.h"
-
+#include "WinUtils.h"
 #include "WindowHook.h"
 #include "TaskbarWindowPreview.h"
 
 #ifdef ACCESSIBILITY
 #include "oleacc.h"
 #include "mozilla/a11y/Accessible.h"
 #endif
 
@@ -99,22 +99,32 @@ public:
   NS_IMETHOD              Create(nsIWidget* aParent,
                                  nsNativeWidget aNativeParent,
                                  const LayoutDeviceIntRect& aRect,
                                  nsWidgetInitData* aInitData = nullptr) override;
   NS_IMETHOD              Destroy() override;
   NS_IMETHOD              SetParent(nsIWidget *aNewParent) override;
   virtual nsIWidget*      GetParent(void) override;
   virtual float           GetDPI() override;
-  virtual double          GetDefaultScaleInternal() override;
-  virtual int32_t         LogToPhys(double aValue) override final;
+  double                  GetDefaultScaleInternal() final;
+  int32_t                 LogToPhys(double aValue) final;
+  mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final
+  {
+    if (mozilla::widget::WinUtils::IsPerMonitorDPIAware()) {
+      return mozilla::DesktopToLayoutDeviceScale(1.0);
+    } else {
+      return mozilla::DesktopToLayoutDeviceScale(GetDefaultScaleInternal());
+    }
+  }
+
   NS_IMETHOD              Show(bool bState) override;
   virtual bool            IsVisible() const override;
   NS_IMETHOD              ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) override;
   virtual void            SetSizeConstraints(const SizeConstraints& aConstraints) override;
+  virtual const SizeConstraints GetSizeConstraints() override;
   NS_IMETHOD              Move(double aX, double aY) override;
   NS_IMETHOD              Resize(double aWidth, double aHeight, bool aRepaint) override;
   NS_IMETHOD              Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
   NS_IMETHOD              BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                           int32_t aHorizontal,
                                           int32_t aVertical) override;
   NS_IMETHOD              PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget *aWidget, bool aActivate) override;
   NS_IMETHOD              SetSizeMode(nsSizeMode aMode) override;
@@ -402,16 +412,18 @@ protected:
   virtual bool            OnResize(nsIntRect &aWindowRect);
   bool                    OnGesture(WPARAM wParam, LPARAM lParam);
   bool                    OnTouch(WPARAM wParam, LPARAM lParam);
   bool                    OnHotKey(WPARAM wParam, LPARAM lParam);
   bool                    OnPaint(HDC aDC, uint32_t aNestingLevel);
   void                    OnWindowPosChanged(WINDOWPOS* wp);
   void                    OnWindowPosChanging(LPWINDOWPOS& info);
   void                    OnSysColorChanged();
+  void                    OnDPIChanged(int32_t x, int32_t y,
+                                       int32_t width, int32_t height);
 
   /**
    * Function that registers when the user has been active (used for detecting
    * when the user is idle).
    */
   void                    UserActivity();
 
   int32_t                 GetHeight(int32_t aProposedHeight);
@@ -538,16 +550,18 @@ protected:
   // Indicates custom frames are enabled
   bool                  mCustomNonClient;
   // Cached copy of L&F's resize border  
   int32_t               mHorResizeMargin;
   int32_t               mVertResizeMargin;
   // Height of the caption plus border
   int32_t               mCaptionHeight;
 
+  double                mDefaultScale;
+
   nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
   // Draggable titlebar region maintained by UpdateWindowDraggingRegion
   LayoutDeviceIntRegion mDraggableRegion;
 
   // Hook Data Memebers for Dropdowns. sProcessHook Tells the
   // hook methods whether they should be processing the hook
   // messages.
@@ -615,16 +629,18 @@ protected:
   POINT mCachedHitTestPoint;
   TimeStamp mCachedHitTestTime;
   int32_t mCachedHitTestResult;
 
   static bool sNeedsToInitMouseWheelSettings;
   static void InitMouseWheelScrollData();
 
   CRITICAL_SECTION mPresentLock;
+
+  double mSizeConstraintsScale; // scale in effect when setting constraints
 };
 
 /**
  * A child window is a window with different style.
  */
 class ChildWindow : public nsWindow {
 
 public:
--- a/widget/windows/nsWindowBase.cpp
+++ b/widget/windows/nsWindowBase.cpp
@@ -100,16 +100,26 @@ nsWindowBase::InjectTouchPoint(uint32_t 
   
   if (!sInjectTouchFuncPtr(1, &info)) {
     WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError());
     return false;
   }
   return true;
 }
 
+void nsWindowBase::ChangedDPI()
+{
+  if (mWidgetListener) {
+    nsIPresShell* presShell = mWidgetListener->GetPresShell();
+    if (presShell) {
+      presShell->BackingScaleFactorChanged();
+    }
+  }
+}
+
 nsresult
 nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                          nsIWidget::TouchPointerState aPointerState,
                                          ScreenIntPoint aPointerScreenPoint,
                                          double aPointerPressure,
                                          uint32_t aPointerOrientation,
                                          nsIObserver* aObserver)
 {
--- a/widget/windows/nsWindowBase.h
+++ b/widget/windows/nsWindowBase.h
@@ -92,16 +92,17 @@ public:
    * WM_APPCOMMAND common handler.
    * Sends events via NativeKey::HandleAppCommandMessage().
    */
   virtual bool HandleAppCommandMsg(const MSG& aAppCommandMsg,
                                    LRESULT *aRetValue);
 
 protected:
   virtual int32_t LogToPhys(double aValue) = 0;
+  void ChangedDPI();
 
   static bool InitTouchInjection();
   bool InjectTouchPoint(uint32_t aId, ScreenIntPoint& aPointerScreenPoint,
                         POINTER_FLAGS aFlags, uint32_t aPressure = 1024,
                         uint32_t aOrientation = 90);
 
   class PointerInfo
   {