bug 794038 - rollup patch for Aurora 18. r=roc+smichaud+bgirard a=bajaj
authorJonathan Kew <jkew@mozilla.com>
Tue, 16 Oct 2012 20:41:19 +0100
changeset 117478 4e085ddddba1e82fb3a8d5edcd76ee566ea34af9
parent 117477 0cd34ac5cbd7d051516a7c3b32de2ccbe42d6a7b
child 117479 a31c54c978f92b04820efa9c889c2bef06bf65b9
push id239
push userakeybl@mozilla.com
push dateThu, 03 Jan 2013 21:54:43 +0000
treeherdermozilla-release@3a7b66445659 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, bajaj
bugs794038
milestone18.0a2
bug 794038 - rollup patch for Aurora 18. r=roc+smichaud+bgirard a=bajaj
docshell/base/nsDocShell.cpp
dom/plugins/base/PluginPRLibrary.cpp
dom/plugins/base/PluginPRLibrary.h
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
dom/plugins/ipc/PluginLibrary.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
dom/plugins/ipc/PluginUtilsOSX.h
dom/plugins/ipc/PluginUtilsOSX.mm
embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
embedding/browser/webBrowser/nsWebBrowser.cpp
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
embedding/test/test_window_open_units.html
gfx/2d/QuartzSupport.mm
layout/base/nsIPresShell.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.h
layout/generic/nsObjectFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsResizerFrame.cpp
modules/libpref/src/init/all.js
view/src/nsViewManager.cpp
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaUtils.mm
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/cocoa/nsScreenCocoa.h
widget/cocoa/nsScreenCocoa.mm
widget/nsGUIEvent.h
widget/nsIBaseWindow.idl
widget/nsIScreen.idl
widget/xpwidgets/nsBaseScreen.cpp
widget/xpwidgets/nsBaseScreen.h
widget/xpwidgets/nsBaseWidget.cpp
xpfe/appshell/src/nsChromeTreeOwner.cpp
xpfe/appshell/src/nsContentTreeOwner.cpp
xpfe/appshell/src/nsWebShellWindow.cpp
xpfe/appshell/src/nsXULWindow.cpp
xpfe/appshell/src/nsXULWindow.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4919,16 +4919,33 @@ nsDocShell::Destroy()
         mInPrivateBrowsing = false;
         DecreasePrivateDocShellCount();
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+    if (mParentWidget) {
+        *aScale = mParentWidget->GetDefaultScale();
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
+    if (ownerWindow) {
+        return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+    }
+
+    *aScale = 1.0;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::SetPosition(int32_t x, int32_t y)
 {
     mBounds.x = x;
     mBounds.y = y;
 
     if (mContentViewer)
         NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
 
@@ -4975,16 +4992,23 @@ nsDocShell::SetPositionAndSize(int32_t x
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx,
                                int32_t * cy)
 {
+    if (mParentWidget) {
+        // ensure size is up-to-date if window has changed resolution
+        nsIntRect r;
+        mParentWidget->GetClientBounds(r);
+        SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false);
+    }
+
     // We should really consider just getting this information from
     // our window instead of duplicating the storage and code...
     if (cx || cy) {
         // Caller wants to know our size; make sure to give them up to
         // date information.
         nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
         if (doc) {
             doc->FlushPendingNotifications(Flush_Layout);
--- a/dom/plugins/base/PluginPRLibrary.cpp
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -272,16 +272,23 @@ PluginPRLibrary::GetImageContainer(NPP i
 nsresult
 PluginPRLibrary::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
 {
   nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
   NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
   *aDrawing = false; 
   return NS_OK;
 }
+nsresult
+PluginPRLibrary::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
+{
+  nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+  NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+  return NS_OK;
+}
 #endif
 
 nsresult
 PluginPRLibrary::GetImageSize(NPP instance, nsIntSize* aSize)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/dom/plugins/base/PluginPRLibrary.h
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -109,16 +109,17 @@ public:
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
     virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window);
     virtual nsresult GetImageContainer(NPP instance, ImageContainer** aContainer);
     virtual nsresult GetImageSize(NPP instance, nsIntSize* aSize);
     virtual bool IsOOP() MOZ_OVERRIDE { return false; }
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
+    virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
 #endif
     virtual nsresult SetBackgroundUnknown(NPP instance) MOZ_OVERRIDE;
     virtual nsresult BeginUpdateBackground(NPP instance,
                                            const nsIntRect&, gfxContext** aCtx) MOZ_OVERRIDE;
     virtual nsresult EndUpdateBackground(NPP instance,
                                          gfxContext* aCtx, const nsIntRect&) MOZ_OVERRIDE;
 #if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
     virtual nsresult HandleGUIEvent(NPP instance,
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1076,16 +1076,36 @@ nsresult nsNPAPIPluginInstance::IsRemote
       return NS_ERROR_FAILURE;
   
   return library->IsRemoteDrawingCoreAnimation(&mNPP, aDrawing);
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
+nsresult nsNPAPIPluginInstance::ContentsScaleFactorChanged(double aContentsScaleFactor)
+{
+#ifdef XP_MACOSX
+  if (!mPlugin)
+      return NS_ERROR_FAILURE;
+
+  PluginLibrary* library = mPlugin->GetLibrary();
+  if (!library)
+      return NS_ERROR_FAILURE;
+
+  // We only need to call this if the plugin is running OOP.
+  if (!library->IsOOP())
+      return NS_OK;
+  
+  return library->ContentsScaleFactorChanged(&mNPP, aContentsScaleFactor);
+#else
+  return NS_ERROR_FAILURE;
+#endif
+}
+
 nsresult
 nsNPAPIPluginInstance::GetJSObject(JSContext *cx, JSObject** outObject)
 {
   if (mHaveJavaC2PJSObjectQuirk) {
     return NS_ERROR_FAILURE;
   }
 
   NPObject *npobj = nullptr;
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -73,16 +73,17 @@ public:
   nsresult Stop();
   nsresult SetWindow(NPWindow* window);
   nsresult NewStreamFromPlugin(const char* type, const char* target, nsIOutputStream* *result);
   nsresult Print(NPPrint* platformPrint);
   nsresult HandleEvent(void* event, int16_t* result);
   nsresult GetValueFromPlugin(NPPVariable variable, void* value);
   nsresult GetDrawingModel(int32_t* aModel);
   nsresult IsRemoteDrawingCoreAnimation(bool* aDrawing);
+  nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
   nsresult GetJSObject(JSContext *cx, JSObject** outObject);
   bool ShouldCache();
   nsresult IsWindowless(bool* isWindowless);
   nsresult AsyncSetWindow(NPWindow* window);
   nsresult GetImageContainer(ImageContainer **aContainer);
   nsresult GetImageSize(nsIntSize* aSize);
   nsresult NotifyPainted(void);
   nsresult GetIsOOP(bool* aIsOOP);
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1399,16 +1399,24 @@ bool nsPluginInstanceOwner::IsRemoteDraw
 
   bool coreAnimation;
   if (!NS_SUCCEEDED(mInstance->IsRemoteDrawingCoreAnimation(&coreAnimation)))
     return false;
 
   return coreAnimation;
 }
 
+nsresult nsPluginInstanceOwner::ContentsScaleFactorChanged(double aContentsScaleFactor)
+{
+  if (!mInstance) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  return mInstance->ContentsScaleFactorChanged(aContentsScaleFactor);
+}
+
 NPEventModel nsPluginInstanceOwner::GetEventModel()
 {
   return mEventModel;
 }
 
 #define DEFAULT_REFRESH_RATE 20 // 50 FPS
 
 nsCOMPtr<nsITimer>                *nsPluginInstanceOwner::sCATimer = NULL;
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -124,16 +124,17 @@ public:
 
   nsEventStatus ProcessEvent(const nsGUIEvent & anEvent);
   
 #ifdef XP_MACOSX
   enum { ePluginPaintEnable, ePluginPaintDisable };
   
   NPDrawingModel GetDrawingModel();
   bool IsRemoteDrawingCoreAnimation();
+  nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
   NPEventModel GetEventModel();
   static void CARefresh(nsITimer *aTimer, void *aClosure);
   void AddToCARefreshTimer();
   void RemoveFromCARefreshTimer();
   // This calls into the plugin (NPP_SetWindow) and can run script.
   void* FixUpPluginWindow(int32_t inPaintState);
   void HidePluginWindow();
   // Set a flag that (if true) indicates the plugin port info has changed and
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -113,16 +113,19 @@ child:
   // special cases where we need an iosurface
   rpc NPP_HandleEvent_IOSurface(NPRemoteEvent event, uint32_t surfaceid)
     returns (int16_t handled);
   // special cases of HandleEvent to make mediating races simpler
   rpc Paint(NPRemoteEvent event)
     returns (int16_t handled);
   // this is only used on windows to forward WM_WINDOWPOSCHANGE
   async WindowPosChanged(NPRemoteEvent event);
+  // used on OS X to tell the child the contents scale factor
+  // of its parent has changed
+  async ContentsScaleFactorChanged(double aContentsScaleFactor);
 
   // ********************** Async plugins rendering
   // see https://wiki.mozilla.org/Gecko:AsyncPluginPainting
   // **********************
 
   // Async version of SetWindow call
   // @param surfaceType - gfxASurface::gfxSurfaceType
   //        plugin child must create offscreen buffer
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -1008,16 +1008,34 @@ PluginInstanceChild::RecvWindowPosChange
     int16_t dontcare;
     return AnswerNPP_HandleEvent(event, &dontcare);
 #else
     NS_RUNTIMEABORT("WindowPosChanged is a windows-only message");
     return false;
 #endif
 }
 
+bool
+PluginInstanceChild::RecvContentsScaleFactorChanged(const double& aContentsScaleFactor)
+{
+#ifdef XP_MACOSX
+    mContentsScaleFactor = aContentsScaleFactor;
+    if (mShContext) {
+        // Release the shared context so that it is reallocated
+        // with the new size. 
+        ::CGContextRelease(mShContext);
+        mShContext = nullptr;
+    }
+    return true;
+#else
+    NS_RUNTIMEABORT("ContentsScaleFactorChanged is an OSX-only message");
+    return false;
+#endif
+}
+
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
 // Create a new window from NPWindow
 bool PluginInstanceChild::CreateWindow(const NPRemoteWindow& aWindow)
 { 
     PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
                       FULLFUNCTION,
                       aWindow.window,
                       aWindow.x, aWindow.y,
@@ -3952,17 +3970,19 @@ PluginInstanceChild::SwapSurfaces()
 
     // Outdated back surface... not usable anymore due to changed plugin size.
     // Dropping obsolete surface
     if (mDoubleBufferCARenderer.HasFrontSurface() && 
         mDoubleBufferCARenderer.HasBackSurface() &&
         (mDoubleBufferCARenderer.GetFrontSurfaceWidth() != 
             mDoubleBufferCARenderer.GetBackSurfaceWidth() ||
         mDoubleBufferCARenderer.GetFrontSurfaceHeight() != 
-            mDoubleBufferCARenderer.GetBackSurfaceHeight())) {
+            mDoubleBufferCARenderer.GetBackSurfaceHeight() ||
+        mDoubleBufferCARenderer.GetFrontSurfaceContentsScaleFactor() != 
+            mDoubleBufferCARenderer.GetBackSurfaceContentsScaleFactor())) {
 
         mDoubleBufferCARenderer.ClearFrontSurface();
     }
 #else
     if (mCurrentSurface && mBackSurface &&
         (mCurrentSurface->GetSize() != mBackSurface->GetSize() ||
          mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) {
         ClearCurrentSurface();
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -122,16 +122,19 @@ protected:
         PaintTracker pt;
         return AnswerNPP_HandleEvent(event, handled);
     }
 
     virtual bool
     RecvWindowPosChanged(const NPRemoteEvent& event) MOZ_OVERRIDE;
 
     virtual bool
+    RecvContentsScaleFactorChanged(const double& aContentsScaleFactor) MOZ_OVERRIDE;
+
+    virtual bool
     AnswerNPP_Destroy(NPError* result);
 
     virtual PPluginScriptableObjectChild*
     AllocPPluginScriptableObject();
 
     virtual bool
     DeallocPPluginScriptableObject(PPluginScriptableObjectChild* aObject);
 
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -794,17 +794,24 @@ PluginInstanceParent::GetImageSize(nsInt
 #ifdef XP_MACOSX
 nsresult
 PluginInstanceParent::IsRemoteDrawingCoreAnimation(bool *aDrawing)
 {
     *aDrawing = (NPDrawingModelCoreAnimation == (NPDrawingModel)mDrawingModel ||
                  NPDrawingModelInvalidatingCoreAnimation == (NPDrawingModel)mDrawingModel);
     return NS_OK;
 }
-#endif
+
+nsresult
+PluginInstanceParent::ContentsScaleFactorChanged(double aContentsScaleFactor)
+{
+    bool rv = SendContentsScaleFactorChanged(aContentsScaleFactor);
+    return rv ? NS_OK : NS_ERROR_FAILURE;
+}
+#endif // #ifdef XP_MACOSX
 
 nsresult
 PluginInstanceParent::SetBackgroundUnknown()
 {
     PLUGIN_LOG_DEBUG(("[InstanceParent][%p] SetBackgroundUnknown", this));
 
     if (mBackground) {
         DestroyBackground();
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -266,16 +266,17 @@ public:
     virtual bool
     AnswerPluginFocusChange(const bool& gotFocus);
 
     nsresult AsyncSetWindow(NPWindow* window);
     nsresult GetImageContainer(mozilla::layers::ImageContainer** aContainer);
     nsresult GetImageSize(nsIntSize* aSize);
 #ifdef XP_MACOSX
     nsresult IsRemoteDrawingCoreAnimation(bool *aDrawing);
+    nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
 #endif
     nsresult SetBackgroundUnknown();
     nsresult BeginUpdateBackground(const nsIntRect& aRect,
                                    gfxContext** aCtx);
     nsresult EndUpdateBackground(gfxContext* aCtx,
                                  const nsIntRect& aRect);
 #if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
     nsresult HandleGUIEvent(const nsGUIEvent& anEvent, bool* handled);
--- a/dom/plugins/ipc/PluginLibrary.h
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -68,16 +68,17 @@ public:
   virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& aResult) = 0;
 
   virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window) = 0;
   virtual nsresult GetImageContainer(NPP instance, ImageContainer** aContainer) = 0;
   virtual nsresult GetImageSize(NPP instance, nsIntSize* aSize) = 0;
   virtual bool IsOOP() = 0;
 #if defined(XP_MACOSX)
   virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) = 0;
+  virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) = 0;
 #endif
 
   /**
    * The next three methods are the third leg in the trip to
    * PluginInstanceParent.  They approximately follow the ReadbackSink
    * API.
    */
   virtual nsresult SetBackgroundUnknown(NPP instance) = 0;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -1180,17 +1180,27 @@ nsresult
 PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
 {
     PluginInstanceParent* i = InstCast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->IsRemoteDrawingCoreAnimation(aDrawing);
 }
-#endif
+
+nsresult
+PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
+{
+    PluginInstanceParent* i = InstCast(instance);
+    if (!i)
+        return NS_ERROR_FAILURE;
+
+    return i->ContentsScaleFactorChanged(aContentsScaleFactor);
+}
+#endif // #if defined(XP_MACOSX)
 
 bool
 PluginModuleParent::AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
                                                       NPError* aError,
                                                       bool* aBoolVal)
 {
     NPBool boolVal = false;
     *aError = mozilla::plugins::parent::_getvalue(nullptr, aVariable, &boolVal);
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -266,16 +266,17 @@ private:
                              char* argv[], NPSavedData* saved,
                              NPError* error);
     virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
                                        uint64_t maxAge);
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
+    virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
 #endif
 #if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
     virtual nsresult HandleGUIEvent(NPP instance, const nsGUIEvent& anEvent,
                                     bool* handled);
 #endif
 
 private:
     CrashReporterParent* CrashReporter();
--- a/dom/plugins/ipc/PluginUtilsOSX.h
+++ b/dom/plugins/ipc/PluginUtilsOSX.h
@@ -45,22 +45,24 @@ public:
   // Returns width in "display pixels".  A "display pixel" is the smallest
   // fully addressable part of a display.  But in HiDPI modes each "display
   // pixel" corresponds to more than one device pixel.  Multiply display pixels
   // by mContentsScaleFactor to get device pixels.
   size_t GetFrontSurfaceWidth();
   // Returns height in "display pixels".  Multiply by
   // mContentsScaleFactor to get device pixels.
   size_t GetFrontSurfaceHeight();
+  double GetFrontSurfaceContentsScaleFactor();
   // Returns width in "display pixels".  Multiply by
   // mContentsScaleFactor to get device pixels.
   size_t GetBackSurfaceWidth();
   // Returns height in "display pixels".  Multiply by
   // mContentsScaleFactor to get device pixels.
   size_t GetBackSurfaceHeight();
+  double GetBackSurfaceContentsScaleFactor();
   IOSurfaceID GetFrontSurfaceID();
 
   bool HasBackSurface();
   bool HasFrontSurface();
   bool HasCALayer();
 
   void SetCALayer(void *aCALayer);
   // aWidth and aHeight are in "display pixels".  Multiply by
--- a/dom/plugins/ipc/PluginUtilsOSX.mm
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -264,32 +264,48 @@ size_t nsDoubleBufferCARenderer::GetFron
 size_t nsDoubleBufferCARenderer::GetFrontSurfaceHeight() {
   if (!HasFrontSurface()) {
     return 0;
   }
 
   return mFrontSurface->GetHeight();
 }
 
+double nsDoubleBufferCARenderer::GetFrontSurfaceContentsScaleFactor() {
+  if (!HasFrontSurface()) {
+    return 1.0;
+  }
+
+  return mFrontSurface->GetContentsScaleFactor();
+}
+
 size_t nsDoubleBufferCARenderer::GetBackSurfaceWidth() {
   if (!HasBackSurface()) {
     return 0;
   }
 
   return mBackSurface->GetWidth();
 }
 
 size_t nsDoubleBufferCARenderer::GetBackSurfaceHeight() {
   if (!HasBackSurface()) {
     return 0;
   }
 
   return mBackSurface->GetHeight();
 }
 
+double nsDoubleBufferCARenderer::GetBackSurfaceContentsScaleFactor() {
+  if (!HasBackSurface()) {
+    return 1.0;
+  }
+
+  return mBackSurface->GetContentsScaleFactor();
+}
+
 IOSurfaceID nsDoubleBufferCARenderer::GetFrontSurfaceID() {
   if (!HasFrontSurface()) {
     return 0;
   }
 
   return mFrontSurface->GetIOSurfaceID();
 }
 
--- a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
@@ -466,16 +466,27 @@ nsDocShellTreeOwner::Destroy()
   {
     return webBrowserChrome->DestroyBrowserWindow();
   }
 
   return NS_ERROR_NULL_POINTER;
 }
 
 NS_IMETHODIMP
+nsDocShellTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+  if (mWebBrowser) {
+    return mWebBrowser->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+  }
+
+  *aScale = 1.0;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShellTreeOwner::SetPosition(int32_t aX, int32_t aY)
 {
   nsCOMPtr<nsIEmbeddingSiteWindow> ownerWin = GetOwnerWin();
   if (ownerWin)
   {
     return ownerWin->SetDimensions(nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION,
                                    aX, aY, 0, 0);
   }
--- a/embedding/browser/webBrowser/nsWebBrowser.cpp
+++ b/embedding/browser/webBrowser/nsWebBrowser.cpp
@@ -1234,16 +1234,22 @@ NS_IMETHODIMP nsWebBrowser::Destroy()
    InternalDestroy();
 
    if(!mInitInfo)
       mInitInfo = new nsWebBrowserInitInfo();
 
    return NS_OK;
 }
 
+NS_IMETHODIMP nsWebBrowser::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+  *aScale = mParentWidget ? mParentWidget->GetDefaultScale() : 1.0;
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsWebBrowser::SetPosition(int32_t aX, int32_t aY)
 {
    int32_t cx = 0;
    int32_t cy = 0;
 
    GetSize(&cx, &cy);
 
    return SetPositionAndSize(aX, aY, cx, cy, false);
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -1914,87 +1914,96 @@ nsWindowWatcher::SizeOpenedDocShellItem(
           sizeChromeHeight = true;
 
   // get various interfaces for aDocShellItem, used throughout this method
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   aDocShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(treeOwner));
   if (!treeOwnerAsWin) // we'll need this to actually size the docshell
     return;
-    
-  float devPixelsPerCSSPixel = 1.0;
+
+  double openerZoom = 1.0;
   if (aParent) {
     nsCOMPtr<nsIDOMDocument> openerDoc;
     aParent->GetDocument(getter_AddRefs(openerDoc));
     if (openerDoc) {
       nsCOMPtr<nsIDocument> doc = do_QueryInterface(openerDoc);
       nsIPresShell* shell = doc->GetShell();
       if (shell) {
         nsPresContext* presContext = shell->GetPresContext();
         if (presContext) {
-          devPixelsPerCSSPixel = presContext->CSSPixelsToDevPixels(1.0f);
+          openerZoom = presContext->GetFullZoom();
         }
       }
     }
   }
 
+  double scale;
+  treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
+
   /* The current position and size will be unchanged if not specified
      (and they fit entirely onscreen). Also, calculate the difference
      between chrome and content sizes on aDocShellItem's window.
      This latter point becomes important if chrome and content
      specifications are mixed in aFeatures, and when bringing the window
      back from too far off the right or bottom edges of the screen. */
 
   treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
+  left = NSToIntRound(left / scale);
+  top = NSToIntRound(top / scale);
+  width = NSToIntRound(width / scale);
+  height = NSToIntRound(height / scale);
   { // scope shellWindow why not
     nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(aDocShellItem));
     if (shellWindow) {
       int32_t cox, coy;
+      double shellScale;
       shellWindow->GetSize(&cox, &coy);
-      chromeWidth = width - cox;
-      chromeHeight = height - coy;
+      shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
+      chromeWidth = width - NSToIntRound(cox / shellScale);
+      chromeHeight = height - NSToIntRound(coy / shellScale);
     }
   }
 
   // Set up left/top
   if (aSizeSpec.mLeftSpecified) {
-    left = NSToIntRound(aSizeSpec.mLeft * devPixelsPerCSSPixel);
+    left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
   }
 
   if (aSizeSpec.mTopSpecified) {
-    top = NSToIntRound(aSizeSpec.mTop * devPixelsPerCSSPixel);
+    top = NSToIntRound(aSizeSpec.mTop * openerZoom);
   }
 
   // Set up width
   if (aSizeSpec.mOuterWidthSpecified) {
     if (!aSizeSpec.mUseDefaultWidth) {
-      width = NSToIntRound(aSizeSpec.mOuterWidth * devPixelsPerCSSPixel);
+      width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
     } // Else specified to default; just use our existing width
   }
   else if (aSizeSpec.mInnerWidthSpecified) {
     sizeChromeWidth = false;
     if (aSizeSpec.mUseDefaultWidth) {
       width = width - chromeWidth;
     } else {
-      width = NSToIntRound(aSizeSpec.mInnerWidth * devPixelsPerCSSPixel);
+      width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
     }
   }
 
   // Set up height
   if (aSizeSpec.mOuterHeightSpecified) {
     if (!aSizeSpec.mUseDefaultHeight) {
-      height = NSToIntRound(aSizeSpec.mOuterHeight * devPixelsPerCSSPixel);
+      height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
     } // Else specified to default; just use our existing height
   }
   else if (aSizeSpec.mInnerHeightSpecified) {
     sizeChromeHeight = false;
     if (aSizeSpec.mUseDefaultHeight) {
       height = height - chromeHeight;
     } else {
-      height = NSToIntRound(aSizeSpec.mInnerHeight * devPixelsPerCSSPixel);
+      height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
     }
   }
 
   bool positionSpecified = aSizeSpec.PositionSpecified();
 
   // Check security state for use in determing window dimensions
   bool enabled = false;
   if (nsContentUtils::IsCallerChrome()) {
@@ -2018,62 +2027,66 @@ nsWindowWatcher::SizeOpenedDocShellItem(
     if (screenMgr)
       screenMgr->ScreenForRect(left, top, width, height,
                                getter_AddRefs(screen));
     if (screen) {
       int32_t screenLeft, screenTop, screenWidth, screenHeight;
       int32_t winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
               winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
 
-      screen->GetAvailRect(&screenLeft, &screenTop,
-                           &screenWidth, &screenHeight);
+      screen->GetAvailRectDisplayPix(&screenLeft, &screenTop,
+                                     &screenWidth, &screenHeight);
 
       if (aSizeSpec.SizeSpecified()) {
         /* Unlike position, force size out-of-bounds check only if
            size actually was specified. Otherwise, intrinsically sized
            windows are broken. */
         if (height < 100)
           height = 100;
         if (winHeight > screenHeight)
           height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
         if (width < 100)
           width = 100;
         if (winWidth > screenWidth)
           width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
       }
 
-      if (left+winWidth > screenLeft+screenWidth)
-        left = screenLeft+screenWidth - winWidth;
+      if (left + winWidth > screenLeft + screenWidth)
+        left = screenLeft + screenWidth - winWidth;
       if (left < screenLeft)
         left = screenLeft;
-      if (top+winHeight > screenTop+screenHeight)
-        top = screenTop+screenHeight - winHeight;
+      if (top + winHeight > screenTop + screenHeight)
+        top = screenTop + screenHeight - winHeight;
       if (top < screenTop)
         top = screenTop;
       if (top != oldTop || left != oldLeft)
         positionSpecified = true;
     }
   }
 
   // size and position the window
 
-  if (positionSpecified)
-    treeOwnerAsWin->SetPosition(left, top);
+  if (positionSpecified) {
+    treeOwnerAsWin->SetPosition(left * scale, top * scale);
+    // moving the window may have changed its scale factor
+    treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
+  }
   if (aSizeSpec.SizeSpecified()) {
     /* Prefer to trust the interfaces, which think in terms of pure
        chrome or content sizes. If we have a mix, use the chrome size
        adjusted by the chrome/content differences calculated earlier. */
-    if (!sizeChromeWidth && !sizeChromeHeight)
-      treeOwner->SizeShellTo(aDocShellItem, width, height);
+    if (!sizeChromeWidth && !sizeChromeHeight) {
+      treeOwner->SizeShellTo(aDocShellItem, width * scale, height * scale);
+    }
     else {
       if (!sizeChromeWidth)
         width += chromeWidth;
       if (!sizeChromeHeight)
         height += chromeHeight;
-      treeOwnerAsWin->SetSize(width, height, false);
+      treeOwnerAsWin->SetSize(width * scale, height * scale, false);
     }
   }
   treeOwnerAsWin->SetVisibility(true);
 }
 
 void
 nsWindowWatcher::GetWindowTreeItem(nsIDOMWindow *inWindow,
                                    nsIDocShellTreeItem **outTreeItem)
--- a/embedding/test/test_window_open_units.html
+++ b/embedding/test/test_window_open_units.html
@@ -24,18 +24,20 @@ SimpleTest.waitForExplicitFinish();
 
 SimpleTest.waitForFocus(function() {
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
   viewer.fullZoom = 2;
 
   var p = window;
   var w = window.open("about:blank", "", "width=200,height=100");
   SimpleTest.waitForFocus(function() {
-    ok(w.innerWidth <= 402 && w.innerWidth >= 398, "width should be around twice what was requested");
-    ok(w.innerHeight <= 202 && w.innerWidth >= 198, "height should be around twice what was requested");
+    ok(w.innerWidth <= 402 && w.innerWidth >= 398,
+       "width (" + w.innerWidth + ") should be around twice what was requested (200)");
+    ok(w.innerHeight <= 202 && w.innerHeight >= 198,
+       "height (" + w.innerHeight + ") should be around twice what was requested (100)");
 
     netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
     viewer.fullZoom = 1;
     w.close();
     SimpleTest.finish();
   }, w, true);
 }, window, false);
 
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -738,16 +738,31 @@ void nsCARenderer::SetBounds(int aWidth,
     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
       Class CGBridgeLayerClass = ::NSClassFromString(@"CGBridgeLayer");
       if (!CGBridgeLayerClass || ![layer isKindOfClass:CGBridgeLayerClass])
 #endif
       {
         layer.contentsScale = mContentsScaleFactor;
       }
     }
+  } else {
+    // These settings are the default values.  But they might have been
+    // changed as above if we were previously running in a HiDPI mode
+    // (i.e. if we just switched from that to a non-HiDPI mode).
+    [layer setAffineTransform:CGAffineTransformIdentity];
+    if ([layer respondsToSelector:@selector(setContentsScale:)]) {
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+      Class CGBridgeLayerClass = ::NSClassFromString(@"CGBridgeLayer");
+      if (!CGBridgeLayerClass || ![layer isKindOfClass:CGBridgeLayerClass])
+#endif
+      {
+        layer.contentsScale = 1.0;
+      }
+    }
   }
   [CATransaction commit];
 
 }
 
 void nsCARenderer::SetViewport(int aWidth, int aHeight) {
   size_t intScaleFactor = ceil(mContentsScaleFactor);
   aWidth *= intScaleFactor;
@@ -833,21 +848,29 @@ nsresult nsCARenderer::Render(int aWidth
     return NS_ERROR_FAILURE;
   }
 
   CARenderer* caRenderer = (CARenderer*)mCARenderer;
   size_t intScaleFactor = ceil(aContentsScaleFactor);
   int renderer_width = caRenderer.bounds.size.width / intScaleFactor;
   int renderer_height = caRenderer.bounds.size.height / intScaleFactor;
 
-  if (renderer_width != aWidth || renderer_height != aHeight) {
+  if (renderer_width != aWidth || renderer_height != aHeight ||
+      mContentsScaleFactor != aContentsScaleFactor) {
     // XXX: This should be optimized to not rescale the buffer
     //      if we are resizing down.
+    // caLayer is the CALayer* provided by the plugin, so we need to preserve
+    // it across the call to Destroy().
     CALayer* caLayer = [caRenderer layer];
+    // mIOSurface is set by AttachIOSurface(), not by SetupRenderer().  So
+    // since it may have been set by a prior call to AttachIOSurface(), we
+    // need to preserve it across the call to Destroy().
+    mozilla::RefPtr<MacIOSurface> ioSurface = mIOSurface;
     Destroy();
+    mIOSurface = ioSurface;
     if (SetupRenderer(caLayer, aWidth, aHeight, aContentsScaleFactor,
                       mAllowOfflineRenderer) != NS_OK) {
       return NS_ERROR_FAILURE;
     }
 
     caRenderer = (CARenderer*)mCARenderer;
   }
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1337,16 +1337,17 @@ public:
   nsSize GetScrollPositionClampingScrollPortSize() {
     NS_ASSERTION(mScrollPositionClampingScrollPortSizeSet, "asking for scroll port when its not set?");
     return mScrollPositionClampingScrollPortSize;
   }
 
   virtual void WindowSizeMoveDone() = 0;
   virtual void SysColorChanged() = 0;
   virtual void ThemeChanged() = 0;
+  virtual void BackingScaleFactorChanged() = 0;
 
   nscoord MaxLineBoxWidth() {
     return mMaxLineBoxWidth;
   }
 
   void SetMaxLineBoxWidth(nscoord aMaxLineBoxWidth);
 
 protected:
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1601,16 +1601,55 @@ nsPresContext::SysColorChangedInternal()
   GetDocumentColorPreferences();
 
   // The system color values are computed to colors in the style data,
   // so normal style data comparison is sufficient here.
   RebuildAllStyleData(nsChangeHint(0));
 }
 
 void
+nsPresContext::UIResolutionChanged()
+{
+  if (!mPendingUIResolutionChanged) {
+    nsCOMPtr<nsIRunnable> ev =
+      NS_NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal);
+    if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
+      mPendingUIResolutionChanged = true;
+    }
+  }
+}
+
+/*static*/ bool
+nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument,
+                                                      void* aData)
+{
+  nsIPresShell* shell = aDocument->GetShell();
+  if (shell) {
+    nsPresContext* pc = shell->GetPresContext();
+    if (pc) {
+      pc->UIResolutionChangedInternal();
+    }
+  }
+  return true;
+}
+
+void
+nsPresContext::UIResolutionChangedInternal()
+{
+  mPendingUIResolutionChanged = false;
+
+  if (mDeviceContext->CheckDPIChange()) {
+    AppUnitsPerDevPixelChanged();
+  }
+
+  mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
+                                   nullptr);
+}
+
+void
 nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint)
 {
   if (!mShell) {
     // We must have been torn down. Nothing to do here.
     return;
   }
 
   RebuildUserFontSet();
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -718,16 +718,23 @@ public:
    * Notify the pres context that the theme has changed.  An internal switch
    * means it's one of our Mozilla themes that changed (e.g., Modern to Classic).
    * Otherwise, the OS is telling us that the native theme for the platform
    * has changed.
    */
   NS_HIDDEN_(void) ThemeChanged();
 
   /*
+   * Notify the pres context that the resolution of the user interface has
+   * changed. This happens if a window is moved between HiDPI and non-HiDPI
+   * displays, so that the ratio of points to device pixels changes.
+   */
+  NS_HIDDEN_(void) UIResolutionChanged();
+
+  /*
    * Notify the pres context that a system color has changed
    */
   NS_HIDDEN_(void) SysColorChanged();
 
   /** Printing methods below should only be used for Medium() == print **/
   NS_HIDDEN_(void) SetPrintSettings(nsIPrintSettings *aPrintSettings);
 
   nsIPrintSettings* GetPrintSettings() { return mPrintSettings; }
@@ -925,16 +932,20 @@ public:
   void SetIsGlyph(bool aValue) {
     mIsGlyph = aValue;
   }
 
 protected:
   friend class nsRunnableMethod<nsPresContext>;
   NS_HIDDEN_(void) ThemeChangedInternal();
   NS_HIDDEN_(void) SysColorChangedInternal();
+  NS_HIDDEN_(void) UIResolutionChangedInternal();
+
+  static NS_HIDDEN_(bool)
+  UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData);
 
   NS_HIDDEN_(void) SetImgAnimations(nsIContent *aParent, uint16_t aMode);
   NS_HIDDEN_(void) SetSMILAnimations(nsIDocument *aDoc, uint16_t aNewMode,
                                      uint16_t aOldMode);
   NS_HIDDEN_(void) GetDocumentColorPreferences();
 
   NS_HIDDEN_(void) PreferenceChanged(const char* aPrefName);
   static NS_HIDDEN_(int) PrefChangedCallback(const char*, void*);
@@ -1160,16 +1171,17 @@ protected:
   unsigned              mCanPaginatedScroll : 1;
   unsigned              mDoScaledTwips : 1;
   unsigned              mEnableJapaneseTransform : 1;
   unsigned              mIsRootPaginatedDocument : 1;
   unsigned              mPrefBidiDirection : 1;
   unsigned              mPrefScrollbarSide : 2;
   unsigned              mPendingSysColorChanged : 1;
   unsigned              mPendingThemeChanged : 1;
+  unsigned              mPendingUIResolutionChanged : 1;
   unsigned              mPendingMediaFeatureValuesChanged : 1;
   unsigned              mPrefChangePendingNeedsReflow : 1;
   unsigned              mMayHaveFixedBackgroundFrames : 1;
   unsigned              mAllInvalidated : 1;
 
   // Are we currently drawing an SVG glyph?
   unsigned              mIsGlyph : 1;
 
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -692,16 +692,17 @@ protected:
   // The callback for the mReflowContinueTimer timer.
   static void sReflowContinueCallback(nsITimer* aTimer, void* aPresShell);
   bool ScheduleReflowOffTimer();
 
   // Widget notificiations
   virtual void WindowSizeMoveDone();
   virtual void SysColorChanged() { mPresContext->SysColorChanged(); }
   virtual void ThemeChanged() { mPresContext->ThemeChanged(); }
+  virtual void BackingScaleFactorChanged() { mPresContext->UIResolutionChanged(); }
 
 #ifdef DEBUG
   // The reflow root under which we're currently reflowing.  Null when
   // not in reflow.
   nsIFrame*                 mCurrentReflowRoot;
   uint32_t                  mUpdateCount;
 #endif
 
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -2095,16 +2095,25 @@ nsObjectFrame::HandleEvent(nsPresContext
       return fm->SetFocus(elem, 0);
   }
   else if (anEvent->message == NS_PLUGIN_FOCUS) {
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm)
       return fm->FocusPlugin(GetContent());
   }
 
+#ifdef XP_MACOSX
+  if (anEvent->message == NS_PLUGIN_RESOLUTION_CHANGED) {
+    double scaleFactor = 1.0;
+    mInstanceOwner->GetContentsScaleFactor(&scaleFactor);
+    mInstanceOwner->ContentsScaleFactorChanged(scaleFactor);
+    return NS_OK;
+  }
+#endif
+
   if (mInstanceOwner->SendNativeEvents() &&
       NS_IS_PLUGIN_EVENT(anEvent)) {
     *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
     return rv;
   }
 
 #ifdef XP_WIN
   rv = nsObjectFrameSuper::HandleEvent(aPresContext, anEvent, anEventStatus);
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -1320,20 +1320,21 @@ nsMenuPopupFrame::GetConstraintRect(cons
   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;
-    int32_t width = rect.width > 0 ? presContext->AppUnitsToDevPixels(rect.width) : 1;
-    int32_t height = rect.height > 0 ? presContext->AppUnitsToDevPixels(rect.height) : 1;
-    sm->ScreenForRect(presContext->AppUnitsToDevPixels(rect.x),
-                      presContext->AppUnitsToDevPixels(rect.y),
+    // nsIScreenManager::ScreenForRect wants the coordinates in CSS pixels
+    int32_t width = NS_MAX(1, nsPresContext::AppUnitsToIntCSSPixels(rect.width));
+    int32_t height = NS_MAX(1, nsPresContext::AppUnitsToIntCSSPixels(rect.height));
+    sm->ScreenForRect(nsPresContext::AppUnitsToIntCSSPixels(rect.x),
+                      nsPresContext::AppUnitsToIntCSSPixels(rect.y),
                       width, height, getter_AddRefs(screen));
     if (screen) {
       // get the total screen area if the popup is allowed to overlap it.
       if (mMenuCanOverlapOSBar && !mInContentShell)
         screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
                         &screenRectPixels.width, &screenRectPixels.height);
       else
         screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
--- a/layout/xul/base/src/nsResizerFrame.cpp
+++ b/layout/xul/base/src/nsResizerFrame.cpp
@@ -203,17 +203,22 @@ nsResizerFrame::HandleEvent(nsPresContex
 
       // Don't allow resizing a window or a popup past the edge of the screen,
       // so adjust the rectangle to fit within the available screen area.
       if (window) {
         nsCOMPtr<nsIScreen> screen;
         nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
         if (sm) {
           nsIntRect frameRect = GetScreenRect();
-          sm->ScreenForRect(frameRect.x, frameRect.y, 1, 1, getter_AddRefs(screen));
+          // ScreenForRect requires display pixels, so scale from device pix
+          double scale;
+          window->GetUnscaledDevicePixelsPerCSSPixel(&scale);
+          sm->ScreenForRect(NSToIntRound(frameRect.x / scale),
+                            NSToIntRound(frameRect.y / scale), 1, 1,
+                            getter_AddRefs(screen));
           if (screen) {
             nsIntRect screenRect;
             screen->GetRect(&screenRect.x, &screenRect.y,
                             &screenRect.width, &screenRect.height);
             rect.IntersectRect(rect, screenRect);
           }
         }
       }
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -190,18 +190,18 @@ pref("media.autoplay.enabled", true);
 // The default number of decoded video frames that are enqueued in
 // nsBuiltinDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 10);
 
 #ifdef XP_MACOSX
 // Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display;
 //   <= 0 : hidpi mode disabled, display will just use pixel-based upscaling
 //   == 1 : hidpi supported if all screens share the same backingScaleFactor
-//   >= 2 : hidpi supported even with mixed backingScaleFactors (currently broken)
-pref("gfx.hidpi.enabled", 1);
+//   >= 2 : hidpi supported even with mixed backingScaleFactors (somewhat broken)
+pref("gfx.hidpi.enabled", 2);
 #endif
 
 // 0 = Off, 1 = Full, 2 = Tagged Images Only. 
 // See eCMSMode in gfx/thebes/gfxPlatform.h
 pref("gfx.color_management.mode", 2);
 pref("gfx.color_management.display_profile", "");
 pref("gfx.color_management.rendering_intent", 0);
 pref("gfx.color_management.enablev4", false);
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -746,17 +746,18 @@ nsresult nsViewManager::DispatchEvent(ns
 
   // If the view has no frame, look for a view that does.
   nsIFrame* frame = view->GetFrame();
   if (!frame &&
       (dispatchUsingCoordinates || NS_IS_KEY_EVENT(aEvent) ||
        NS_IS_IME_RELATED_EVENT(aEvent) ||
        NS_IS_NON_RETARGETED_PLUGIN_EVENT(aEvent) ||
        aEvent->message == NS_PLUGIN_ACTIVATE ||
-       aEvent->message == NS_PLUGIN_FOCUS)) {
+       aEvent->message == NS_PLUGIN_FOCUS ||
+       aEvent->message == NS_PLUGIN_RESOLUTION_CHANGED)) {
     while (view && !view->GetFrame()) {
       view = view->GetParent();
     }
 
     if (view) {
       frame = view->GetFrame();
     }
   }
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -414,16 +414,20 @@ public:
   // Returns the "backing scale factor" of the view's window, which is the
   // ratio of pixels in the window's backing store to Cocoa points. Prior to
   // HiDPI support in OS X 10.7, this was always 1.0, but in HiDPI mode it
   // will be 2.0 (and might potentially other values as screen resolutions
   // evolve). This gives the relationship between what Gecko calls "device
   // pixels" and the Cocoa "points" coordinate system.
   CGFloat                 BackingScaleFactor();
 
+  // Call if the window's backing scale factor changes - i.e., it is moved
+  // between HiDPI and non-HiDPI screens
+  void                    BackingScaleFactorChanged();
+
   virtual double          GetDefaultScale();
 
   NS_IMETHOD              Invalidate(const nsIntRect &aRect);
 
   virtual void*           GetNativeData(uint32_t aDataType);
   virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
   virtual nsIntPoint      WidgetToScreenOffset();
   virtual bool            ShowsResizeIndicator(nsIntRect* aResizerRect);
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -27,16 +27,17 @@
 #include "nsIInterfaceRequestor.h"
 #include "nsIFile.h"
 #include "nsILocalFileMac.h"
 #include "nsGfxCIID.h"
 #include "nsIDOMSimpleGestureEvent.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsThemeConstants.h"
 #include "nsIWidgetListener.h"
+#include "nsIPresShell.h"
 
 #include "nsDragService.h"
 #include "nsClipboard.h"
 #include "nsCursorManager.h"
 #include "nsWindowMap.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsMenuUtilsX.h"
@@ -774,16 +775,44 @@ nsChildView::BackingScaleFactor()
   }
   if (!mView) {
     return 1.0;
   }
   mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mView);
   return mBackingScaleFactor;
 }
 
+void
+nsChildView::BackingScaleFactorChanged()
+{
+  CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mView);
+
+  // ignore notification if it hasn't really changed (or maybe we have
+  // disabled HiDPI mode via prefs)
+  if (mBackingScaleFactor == newScale) {
+    return;
+  }
+
+  mBackingScaleFactor = newScale;
+
+  if (mWidgetListener && !mWidgetListener->GetXULWindow()) {
+    nsIPresShell* presShell = mWidgetListener->GetPresShell();
+    if (presShell) {
+      presShell->BackingScaleFactorChanged();
+    }
+  }
+
+  if (IsPluginView()) {
+    nsEventStatus status = nsEventStatus_eIgnore;
+    nsGUIEvent guiEvent(true, NS_PLUGIN_RESOLUTION_CHANGED, this);
+    guiEvent.time = PR_IntervalNow();
+    DispatchEvent(&guiEvent, status);
+  }
+}
+
 NS_IMETHODIMP nsChildView::ConstrainPosition(bool aAllowSlop,
                                              int32_t *aX, int32_t *aY)
 {
   return NS_OK;
 }
 
 // Move this component, aX and aY are in the parent widget coordinate system
 NS_IMETHODIMP nsChildView::Move(int32_t aX, int32_t aY)
@@ -2364,16 +2393,27 @@ NSEvent* gLastDragMouseDownEvent = nil;
    [self update];
 }
 
 - (BOOL)wantsBestResolutionOpenGLSurface
 {
   return nsCocoaUtils::HiDPIEnabled() ? YES : NO;
 }
 
+- (void)viewDidChangeBackingProperties
+{
+  [super viewDidChangeBackingProperties];
+  if (mGeckoChild) {
+    // actually, it could be the color space that's changed,
+    // but we can't tell the difference here except by retrieving
+    // the backing scale factor and comparing to the old value
+    mGeckoChild->BackingScaleFactorChanged();
+  }
+}
+
 // The display system has told us that a portion of our view is dirty. Tell
 // gecko to paint it
 - (void)drawRect:(NSRect)aRect
 {
   CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
   [self drawRect:aRect inContext:cgContext];
 
   // If we're a transparent window and our contents have changed, we need
--- a/widget/cocoa/nsCocoaUtils.mm
+++ b/widget/cocoa/nsCocoaUtils.mm
@@ -529,79 +529,23 @@ nsCocoaUtils::GetCurrentModifiers()
 //       1 : HiDPI enabled provided all screens have the same backing resolution
 //     > 1 : HiDPI enabled even if there are a mixture of screen modes
 
 // All the following code is to be removed once HiDPI work is more complete.
 
 static bool sHiDPIEnabled = false;
 static bool sHiDPIPrefInitialized = false;
 
-@interface ScreenParamChangeWatcher : NSObject
-- (id)init;
-@end
-
-@implementation ScreenParamChangeWatcher
-- (id)init
-{
-  [super init];
-  [[NSNotificationCenter defaultCenter]
-    addObserver:self
-       selector:@selector(applicationDidChangeScreenParameters:)
-           name:NSApplicationDidChangeScreenParametersNotification
-         object:NSApp];
-  return self;
-}
-
-- (void)applicationDidChangeScreenParameters:(NSNotification *)notification
-{
-  // reset flags so that the next call to HiDPIEnabled() will re-evaluate
-  sHiDPIEnabled = false;
-  sHiDPIPrefInitialized = false;
-}
-@end
-
-class HiDPIPrefObserver MOZ_FINAL : public nsIObserver {
-  public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIOBSERVER
-};
-
-NS_IMPL_ISUPPORTS1(HiDPIPrefObserver, nsIObserver)
-
-NS_IMETHODIMP
-HiDPIPrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
-                           const PRUnichar* aData)
-{
-  // reset flags so that the next call to HiDPIEnabled() will re-evaluate
-  sHiDPIEnabled = false;
-  sHiDPIPrefInitialized = false;
-  return NS_OK;
-}
-
 // static
 bool
 nsCocoaUtils::HiDPIEnabled()
 {
-  static ScreenParamChangeWatcher* sChangeWatcher = nil;
-
   if (!sHiDPIPrefInitialized) {
     sHiDPIPrefInitialized = true;
 
-    if (!sChangeWatcher) {
-      // Create an object to watch for changes in screen configuration.
-      // Note that we'll leak this object at shutdown;
-      // this is all a temporary hack until we have multi-screen HiDPI working
-      // properly and can dispense with this code.
-      sChangeWatcher = [[ScreenParamChangeWatcher alloc] init];
-
-      // And create an observer for changes to the preference.
-      nsCOMPtr<nsIObserver> obs(new HiDPIPrefObserver());
-      Preferences::AddStrongObserver(obs, "gfx.hidpi.enabled");
-    }
-
     int prefSetting = Preferences::GetInt("gfx.hidpi.enabled", 1);
     if (prefSetting <= 0) {
       return false;
     }
 
     // prefSetting is at least 1, need to check attached screens...
 
     int scaleFactors = 0; // used as a bitset to track the screen types found
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -238,16 +238,17 @@ public:
     NS_IMETHOD              GetClientBounds(nsIntRect &aRect);
     NS_IMETHOD              GetScreenBounds(nsIntRect &aRect);
     void                    ReportMoveEvent();
     void                    ReportSizeEvent();
     NS_IMETHOD              SetCursor(nsCursor aCursor);
     NS_IMETHOD              SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY);
 
     CGFloat                 BackingScaleFactor();
+    void                    BackingScaleFactorChanged();
     virtual double          GetDefaultScale();
 
     NS_IMETHOD              SetTitle(const nsAString& aTitle);
 
     NS_IMETHOD Invalidate(const nsIntRect &aRect);
     virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
     virtual LayerManager* GetLayerManager(PLayersChild* aShadowManager = nullptr,
                                           LayersBackend aBackendHint = mozilla::layers::LAYERS_NONE,
@@ -309,16 +310,19 @@ protected:
   nsresult             CreatePopupContentView(const nsIntRect &aRect,
                                               nsDeviceContext *aContext);
   void                 DestroyNativeWindow();
   void                 AdjustWindowShadow();
   void                 SetUpWindowFilter();
   void                 CleanUpWindowFilter();
   void                 UpdateBounds();
 
+  nsresult             DoResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
+                                bool aRepaint, bool aConstrainToCurrentScreen);
+
   virtual already_AddRefed<nsIWidget>
   AllocateChildPopupWidget()
   {
     static NS_DEFINE_IID(kCPopUpCID, NS_POPUP_CID);
     nsCOMPtr<nsIWidget> widget = do_CreateInstance(kCPopUpCID);
     return widget.forget();
   }
 
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -25,16 +25,17 @@
 #include "nsMenuBarX.h"
 #include "nsMenuUtilsX.h"
 #include "nsStyleConsts.h"
 #include "nsNativeThemeColors.h"
 #include "nsChildView.h"
 #include "nsCocoaFeatures.h"
 #include "nsIScreenManager.h"
 #include "nsIWidgetListener.h"
+#include "nsIPresShell.h"
 
 #include "gfxPlatform.h"
 #include "qcms.h"
 
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 namespace layers {
@@ -171,22 +172,42 @@ nsCocoaWindow::~nsCocoaWindow()
   if (mModal) {
     --gXULModalLevel;
     NS_ASSERTION(gXULModalLevel >= 0, "Wierdness setting modality!");
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-// fits the rect to the screen that contains the largest area of it
+// Find the screen that overlaps aRect the most,
+// if none are found default to the mainScreen.
+static NSScreen *FindTargetScreenForRect(const nsIntRect& aRect)
+{
+  NSScreen *targetScreen = [NSScreen mainScreen];
+  NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
+  int largestIntersectArea = 0;
+  while (NSScreen *screen = [screenEnum nextObject]) {
+    nsIntRect screenRect(nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]));
+    screenRect = screenRect.Intersect(aRect);
+    int area = screenRect.width * screenRect.height;
+    if (area > largestIntersectArea) {
+      largestIntersectArea = area;
+      targetScreen = screen;
+    }
+  }
+  return targetScreen;
+}
+
+// fits the rect to the screen that contains the largest area of it,
+// or to aScreen if a screen is passed in
 // NB: this operates with aRect in global CSS pixels
 static void FitRectToVisibleAreaForScreen(nsIntRect &aRect, NSScreen *aScreen)
 {
   if (!aScreen) {
-    return;
+    aScreen = FindTargetScreenForRect(aRect);
   }
 
   nsIntRect screenBounds(nsCocoaUtils::CocoaRectToGeckoRect([aScreen visibleFrame]));
 
   if (aRect.width > screenBounds.width) {
     aRect.width = screenBounds.width;
   }
   if (aRect.height > screenBounds.height) {
@@ -229,33 +250,18 @@ nsresult nsCocoaWindow::Create(nsIWidget
                                nsWidgetInitData *aInitData)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   // Because the hidden window is created outside of an event loop,
   // we have to provide an autorelease pool (see bug 559075).
   nsAutoreleasePool localPool;
 
-  // Find the screen that overlaps aRect the most,
-  // if none are found default to the mainScreen.
-  NSScreen *targetScreen = [NSScreen mainScreen];
-  NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
-  int largestIntersectArea = 0;
-  while (NSScreen *screen = [screenEnum nextObject]) {
-    nsIntRect screenRect(nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]));
-    screenRect = screenRect.Intersect(aRect);
-    int area = screenRect.width * screenRect.height;
-    if (area > largestIntersectArea) {
-      largestIntersectArea = area;
-      targetScreen = screen;
-    }
-  }
-
   nsIntRect newBounds = aRect;
-  FitRectToVisibleAreaForScreen(newBounds, targetScreen);
+  FitRectToVisibleAreaForScreen(newBounds, nullptr);
 
   // Set defaults which can be overriden from aInitData in BaseCreate
   mWindowType = eWindowType_toplevel;
   mBorderStyle = eBorderStyle_default;
 
   // Ensure that the toolkit is created.
   nsToolkit::GetToolkit();
 
@@ -415,16 +421,20 @@ nsresult nsCocoaWindow::CreateNativeWind
   // BorderlessWindow class.
   else if (features == NSBorderlessWindowMask)
     windowClass = [BorderlessWindow class];
 
   // Create the window
   mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
                                  backing:NSBackingStoreBuffered defer:YES];
 
+  // setup our notification delegate. Note that setDelegate: does NOT retain.
+  mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
+  [mWindow setDelegate:mDelegate];
+
   // Make sure that the content rect we gave has been honored.
   NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   if (!NSEqualRects([mWindow frame], wantedFrame)) {
     // This can happen when the window is not on the primary screen.
     [mWindow setFrame:wantedFrame display:NO];
   }
   UpdateBounds();
 
@@ -435,20 +445,16 @@ nsresult nsCocoaWindow::CreateNativeWind
     [mWindow setHasShadow:YES];
   }
 
   [mWindow setBackgroundColor:[NSColor clearColor]];
   [mWindow setOpaque:NO];
   [mWindow setContentMinSize:NSMakeSize(60, 60)];
   [mWindow disableCursorRects];
 
-  // setup our notification delegate. Note that setDelegate: does NOT retain.
-  mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
-  [mWindow setDelegate:mDelegate];
-
   [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
   mWindowMadeHere = true;
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
@@ -1018,55 +1024,58 @@ NS_IMETHODIMP nsCocoaWindow::ConstrainPo
                                                int32_t *aX, int32_t *aY)
 {
   if (!mWindow || ![mWindow screen]) {
     return NS_OK;
   }
 
   nsIntRect screenBounds;
 
+  int32_t width, height;
+
+  NSRect frame = [mWindow frame];
+
+  // zero size rects confuse the screen manager
+  width = NS_MAX<int32_t>(frame.size.width, 1);
+  height = NS_MAX<int32_t>(frame.size.height, 1);
+
   nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
   if (screenMgr) {
     nsCOMPtr<nsIScreen> screen;
-    int32_t width, height;
-
-    // zero size rects confuse the screen manager
-    width = mBounds.width > 0 ? mBounds.width : 1;
-    height = mBounds.height > 0 ? mBounds.height : 1;
     screenMgr->ScreenForRect(*aX, *aY, width, height, getter_AddRefs(screen));
 
     if (screen) {
-      screen->GetRect(&(screenBounds.x), &(screenBounds.y),
-                      &(screenBounds.width), &(screenBounds.height));
+      screen->GetRectDisplayPix(&(screenBounds.x), &(screenBounds.y),
+                                &(screenBounds.width), &(screenBounds.height));
     }
   }
 
   if (aAllowSlop) {
-    if (*aX < screenBounds.x - mBounds.width + kWindowPositionSlop) {
-      *aX = screenBounds.x - mBounds.width + kWindowPositionSlop;
+    if (*aX < screenBounds.x - width + kWindowPositionSlop) {
+      *aX = screenBounds.x - width + kWindowPositionSlop;
     } else if (*aX >= screenBounds.x + screenBounds.width - kWindowPositionSlop) {
       *aX = screenBounds.x + screenBounds.width - kWindowPositionSlop;
     }
 
-    if (*aY < screenBounds.y - mBounds.height + kWindowPositionSlop) {
-      *aY = screenBounds.y - mBounds.height + kWindowPositionSlop;
+    if (*aY < screenBounds.y - height + kWindowPositionSlop) {
+      *aY = screenBounds.y - height + kWindowPositionSlop;
     } else if (*aY >= screenBounds.y + screenBounds.height - kWindowPositionSlop) {
       *aY = screenBounds.y + screenBounds.height - kWindowPositionSlop;
     }
   } else {
     if (*aX < screenBounds.x) {
       *aX = screenBounds.x;
-    } else if (*aX >= screenBounds.x + screenBounds.width - mBounds.width) {
-      *aX = screenBounds.x + screenBounds.width - mBounds.width;
+    } else if (*aX >= screenBounds.x + screenBounds.width - width) {
+      *aX = screenBounds.x + screenBounds.width - width;
     }
 
     if (*aY < screenBounds.y) {
       *aY = screenBounds.y;
-    } else if (*aY >= screenBounds.y + screenBounds.height - mBounds.height) {
-      *aY = screenBounds.y + screenBounds.height - mBounds.height;
+    } else if (*aY >= screenBounds.y + screenBounds.height - height) {
+      *aY = screenBounds.y + screenBounds.height - height;
     }
   }
 
   return NS_OK;
 }
 
 void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
 {
@@ -1278,31 +1287,36 @@ NS_METHOD nsCocoaWindow::MakeFullScreen(
     EnteredFullScreen(aFullScreen);
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
-NS_IMETHODIMP nsCocoaWindow::Resize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool aRepaint)
+nsresult nsCocoaWindow::DoResize(int32_t aX, int32_t aY,
+                                 int32_t aWidth, int32_t aHeight,
+                                 bool aRepaint, bool aConstrainToCurrentScreen)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   ConstrainSize(&aWidth, &aHeight);
 
   nsIntRect newBounds(aX, aY, aWidth, aHeight);
 
   // convert requested size into Cocoa points
   CGFloat scaleFactor = BackingScaleFactor();
   NSRect cocoaBounds = nsCocoaUtils::DevPixelsToCocoaPoints(newBounds, scaleFactor);
 
-  // constrain to the visible area of the window's current screen
+  // constrain to the visible area of the window's current screen if requested,
+  // or to the screen that contains the largest area of the new rect
   nsCocoaUtils::NSRectToGeckoRect(cocoaBounds, newBounds);
-  FitRectToVisibleAreaForScreen(newBounds, [mWindow screen]);
+  FitRectToVisibleAreaForScreen(newBounds,
+                                aConstrainToCurrentScreen ?
+                                    [mWindow screen] : nullptr);
 
   // then convert back to device pixels
   nsCocoaUtils::GeckoRectToNSRect(newBounds, cocoaBounds);
   newBounds = nsCocoaUtils::CocoaPointsToDevPixels(cocoaBounds, scaleFactor);
 
   BOOL isMoving = (mBounds.x != newBounds.x || mBounds.y != newBounds.y);
   BOOL isResizing = (mBounds.width != newBounds.width || mBounds.height != newBounds.height);
 
@@ -1316,23 +1330,26 @@ NS_IMETHODIMP nsCocoaWindow::Resize(int3
   // the wrong place, leading to a visual jump.
   [mWindow setFrame:newFrame display:YES];
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+NS_IMETHODIMP nsCocoaWindow::Resize(int32_t aX, int32_t aY,
+                                    int32_t aWidth, int32_t aHeight,
+                                    bool aRepaint)
+{
+  return DoResize(aX, aY, aWidth, aHeight, aRepaint, false);
+}
+
 NS_IMETHODIMP nsCocoaWindow::Resize(int32_t aWidth, int32_t aHeight, bool aRepaint)
 {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-  
-  return Resize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint);
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+  return DoResize(mBounds.x, mBounds.y, aWidth, aHeight, aRepaint, true);
 }
 
 NS_IMETHODIMP nsCocoaWindow::GetClientBounds(nsIntRect &aRect)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   CGFloat scaleFactor = BackingScaleFactor();
   if (!mWindow) {
@@ -1394,16 +1411,58 @@ nsCocoaWindow::BackingScaleFactor()
   }
   if (!mWindow) {
     return 1.0;
   }
   mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mWindow);
   return mBackingScaleFactor;
 }
 
+void
+nsCocoaWindow::BackingScaleFactorChanged()
+{
+  CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mWindow);
+
+  // ignore notification if it hasn't really changed (or maybe we have
+  // disabled HiDPI mode via prefs)
+  if (mBackingScaleFactor == newScale) {
+    return;
+  }
+
+  if (mBackingScaleFactor > 0.0) {
+    // convert size constraints to the new device pixel coordinate space
+    double scaleFactor = newScale / mBackingScaleFactor;
+    mSizeConstraints.mMinSize.width =
+      NSToIntRound(mSizeConstraints.mMinSize.width * scaleFactor);
+    mSizeConstraints.mMinSize.height =
+      NSToIntRound(mSizeConstraints.mMinSize.height * scaleFactor);
+    if (mSizeConstraints.mMaxSize.width < NS_MAXSIZE) {
+      mSizeConstraints.mMaxSize.width =
+        NS_MIN(NS_MAXSIZE,
+               NSToIntRound(mSizeConstraints.mMaxSize.width * scaleFactor));
+    }
+    if (mSizeConstraints.mMaxSize.height < NS_MAXSIZE) {
+      mSizeConstraints.mMaxSize.height =
+        NS_MIN(NS_MAXSIZE,
+               NSToIntRound(mSizeConstraints.mMaxSize.height * scaleFactor));
+    }
+  }
+
+  mBackingScaleFactor = newScale;
+
+  if (!mWidgetListener || mWidgetListener->GetXULWindow()) {
+    return;
+  }
+
+  nsIPresShell* presShell = mWidgetListener->GetPresShell();
+  if (presShell) {
+    presShell->BackingScaleFactorChanged();
+  }
+}
+
 NS_IMETHODIMP nsCocoaWindow::SetCursor(nsCursor aCursor)
 {
   if (mPopupContentView)
     return mPopupContentView->SetCursor(aCursor);
 
   return NS_OK;
 }
 
@@ -2219,16 +2278,34 @@ bool nsCocoaWindow::ShouldFocusPlugin()
   [TopLevelWindowData deactivateInWindow:sheet];
   [sheet orderOut:self];
   if (contextInfo)
     [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+- (void)windowDidChangeBackingProperties:(NSNotification *)aNotification
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NSWindow *window = (NSWindow *)[aNotification object];
+
+  if ([window respondsToSelector:@selector(backingScaleFactor)]) {
+    CGFloat oldFactor =
+      [[[aNotification userInfo]
+         objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
+    if ([window backingScaleFactor] != oldFactor) {
+      mGeckoWindow->BackingScaleFactorChanged();
+    }
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
 - (nsCocoaWindow*)geckoWidget
 {
   return mGeckoWindow;
 }
 
 - (bool)toplevelActiveState
 {
   return mToplevelActiveState;
--- a/widget/cocoa/nsScreenCocoa.h
+++ b/widget/cocoa/nsScreenCocoa.h
@@ -13,16 +13,18 @@
 class nsScreenCocoa : public nsBaseScreen
 {
 public:
     nsScreenCocoa (NSScreen *screen);
     ~nsScreenCocoa ();
 
     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);
 
     NSScreen *CocoaScreen() { return mScreen; }
 
 private:
     CGFloat BackingScaleFactor();
--- a/widget/cocoa/nsScreenCocoa.mm
+++ b/widget/cocoa/nsScreenCocoa.mm
@@ -51,16 +51,46 @@ nsScreenCocoa::GetAvailRect(int32_t *out
   *outY = r.y;
   *outWidth = r.width;
   *outHeight = r.height;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScreenCocoa::GetRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight)
+{
+  NSRect frame = [mScreen frame];
+
+  nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame);
+
+  *outX = r.x;
+  *outY = r.y;
+  *outWidth = r.width;
+  *outHeight = r.height;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScreenCocoa::GetAvailRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight)
+{
+  NSRect frame = [mScreen visibleFrame];
+
+  nsIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame);
+
+  *outX = r.x;
+  *outY = r.y;
+  *outWidth = r.width;
+  *outHeight = r.height;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsScreenCocoa::GetPixelDepth(int32_t *aPixelDepth)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   NSWindowDepth depth = [mScreen depth];
   int bpp = NSBitsPerPixelFromDepth(depth);
 
   *aPixelDepth = bpp;
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -168,16 +168,21 @@ class nsHashKey;
 
 // Indicates a resize will occur
 #define NS_BEFORERESIZE_EVENT            (NS_WINDOW_START + 66)
 
 // Indicates that the user is either idle or active
 #define NS_MOZ_USER_IDLE                 (NS_WINDOW_START + 67)
 #define NS_MOZ_USER_ACTIVE               (NS_WINDOW_START + 68)
 
+// The resolution at which a plugin should draw has changed, for
+// example as the result of changing from a HiDPI mode to a non-
+// HiDPI mode.
+#define NS_PLUGIN_RESOLUTION_CHANGED     (NS_WINDOW_START + 69)
+
 #define NS_MOUSE_MESSAGE_START          300
 #define NS_MOUSE_MOVE                   (NS_MOUSE_MESSAGE_START)
 #define NS_MOUSE_BUTTON_UP              (NS_MOUSE_MESSAGE_START + 1)
 #define NS_MOUSE_BUTTON_DOWN            (NS_MOUSE_MESSAGE_START + 2)
 #define NS_MOUSE_ENTER                  (NS_MOUSE_MESSAGE_START + 22)
 #define NS_MOUSE_EXIT                   (NS_MOUSE_MESSAGE_START + 23)
 #define NS_MOUSE_DOUBLECLICK            (NS_MOUSE_MESSAGE_START + 24)
 #define NS_MOUSE_CLICK                  (NS_MOUSE_MESSAGE_START + 27)
@@ -1773,17 +1778,18 @@ enum {
  * position or not.  When it should be handled there (e.g., the mouse events),
  * this returns TRUE.
  */
 inline bool NS_IsEventUsingCoordinates(nsEvent* aEvent)
 {
   return !NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_RELATED_EVENT(aEvent) &&
          !NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_ACTIVATION_EVENT(aEvent) &&
          !NS_IS_PLUGIN_EVENT(aEvent) &&
-         !NS_IS_CONTENT_COMMAND_EVENT(aEvent);
+         !NS_IS_CONTENT_COMMAND_EVENT(aEvent) &&
+         aEvent->message != NS_PLUGIN_RESOLUTION_CHANGED;
 }
 
 /**
  * Whether the event should be handled by the focused DOM window in the
  * same top level window's or not.  E.g., key events, IME related events
  * (including the query content events, they are used in IME transaction)
  * should be handled by the (last) focused window rather than the dispatched
  * window.
--- a/widget/nsIBaseWindow.idl
+++ b/widget/nsIBaseWindow.idl
@@ -16,17 +16,17 @@ class nsIWidget;
 typedef voidPtr nativeWindow;
 
 /**
  * The nsIBaseWindow describes a generic window and basic operations that 
  * can be performed on it.  This is not to be a complete windowing interface
  * but rather a common set that nearly all windowed objects support.    
  */
 
-[scriptable, uuid(7144AC8B-6702-4A4B-A73D-D1D4E9717E46)]
+[scriptable, uuid(9DA319F3-EEE6-4504-81A5-6A19CF6215BF)]
 interface nsIBaseWindow : nsISupports
 {
 	/*
 	Allows a client to initialize an object implementing this interface with
 	the usually required window setup information.
 	It is possible to pass null for both parentNativeWindow and parentWidget,
 	but only docshells support this.
 
@@ -177,17 +177,26 @@ interface nsIBaseWindow : nsISupports
     attribute boolean enabled;
 
 	/*
 	Allows you to find out what the widget is of a given object.  Depending
 	on the object, this may return the parent widget in which this object
 	lives if it has not had to create its own widget.
 	*/
 	[noscript] readonly attribute nsIWidget mainWidget;
-	
+
+	/*
+	The number of	device pixels per CSS pixel used on this window's current
+	screen at the default zoom level.
+	This is the value returned by GetDefaultScale() of the underlying widget.
+	Note that this may change if the window is moved between screens with
+	differing resolutions.
+	*/
+	readonly attribute double unscaledDevicePixelsPerCSSPixel;
+
 	/**
 	* Give the window focus.
 	*/
 	void setFocus();
 
 	/*
 	Title of the window.
 	*/
--- a/widget/nsIScreen.idl
+++ b/widget/nsIScreen.idl
@@ -24,20 +24,29 @@ interface nsIScreen : nsISupports
    *
    * ROTATION_0_DEG is the default, unrotated configuration.
    */
   const unsigned long   ROTATION_0_DEG = 0;
   const unsigned long  ROTATION_90_DEG = 1;
   const unsigned long ROTATION_180_DEG = 2;
   const unsigned long ROTATION_270_DEG = 3;
 
+  /**
+   * These report screen dimensions in (screen-specific) device pixels
+   */
   void GetRect(out long left, out long top, out long width, out long height);
   void GetAvailRect(out long left, out long top, out long width, out long height);
 
   /**
+   * And these report in global display pixels
+   */
+  void GetRectDisplayPix(out long left, out long top, out long width, out long height);
+  void GetAvailRectDisplayPix(out long left, out long top, out long width, out long height);
+
+  /**
    * Locks the minimum brightness of the screen, forcing it to be at
    * least as bright as a certain brightness level. Each call to this
    * function must eventually be followed by a corresponding call to
    * unlockMinimumBrightness, with the same brightness level.
    *
    * @param brightness A brightness level, one of the above constants.
    */
   void lockMinimumBrightness(in unsigned long brightness);
--- a/widget/xpwidgets/nsBaseScreen.cpp
+++ b/widget/xpwidgets/nsBaseScreen.cpp
@@ -15,16 +15,30 @@ nsBaseScreen::nsBaseScreen()
 {
   for (uint32_t i = 0; i < nsIScreen::BRIGHTNESS_LEVELS; i++)
     mBrightnessLocks[i] = 0;
 }
 
 nsBaseScreen::~nsBaseScreen() { }
 
 NS_IMETHODIMP
+nsBaseScreen::GetRectDisplayPix(int32_t *outLeft,  int32_t *outTop,
+                                int32_t *outWidth, int32_t *outHeight)
+{
+  return GetRect(outLeft, outTop, outWidth, outHeight);
+}
+
+NS_IMETHODIMP
+nsBaseScreen::GetAvailRectDisplayPix(int32_t *outLeft,  int32_t *outTop,
+                                     int32_t *outWidth, int32_t *outHeight)
+{
+  return GetAvailRect(outLeft, outTop, outWidth, outHeight);
+}
+
+NS_IMETHODIMP
 nsBaseScreen::LockMinimumBrightness(uint32_t aBrightness)
 {
   NS_ABORT_IF_FALSE(
     aBrightness < nsIScreen::BRIGHTNESS_LEVELS,
     "Invalid brightness level to lock");
   mBrightnessLocks[aBrightness]++;
   NS_ABORT_IF_FALSE(mBrightnessLocks[aBrightness] > 0,
     "Overflow after locking brightness level");
--- a/widget/xpwidgets/nsBaseScreen.h
+++ b/widget/xpwidgets/nsBaseScreen.h
@@ -16,16 +16,24 @@ class nsBaseScreen : public nsIScreen
 public:
   nsBaseScreen();
   virtual ~nsBaseScreen();
   
   NS_DECL_ISUPPORTS
 
   // nsIScreen interface
 
+  // These simply forward to the device-pixel versions;
+  // implementations where global display pixels may not correspond
+  // to per-screen device pixels must override.
+  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);
+
   /**
    * Simple management of screen brightness locks. This abstract base class
    * allows all widget implementations to share brightness locking code.
    */
   NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness);
   NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness);
 
   NS_IMETHOD GetRotation(uint32_t* aRotation) {
--- a/widget/xpwidgets/nsBaseWidget.cpp
+++ b/widget/xpwidgets/nsBaseWidget.cpp
@@ -687,18 +687,22 @@ NS_IMETHODIMP nsBaseWidget::MakeFullScre
     GetScreenBounds(*mOriginalBounds);
 
     // Move to top-left corner of screen and size to the screen dimensions
     nsCOMPtr<nsIScreenManager> screenManager;
     screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1"); 
     NS_ASSERTION(screenManager, "Unable to grab screenManager.");
     if (screenManager) {
       nsCOMPtr<nsIScreen> screen;
-      screenManager->ScreenForRect(mOriginalBounds->x, mOriginalBounds->y,
-                                   mOriginalBounds->width, mOriginalBounds->height,
+      // convert dev pix to display/CSS pix for ScreenForRect
+      double scale = GetDefaultScale();
+      screenManager->ScreenForRect(mOriginalBounds->x / scale,
+                                   mOriginalBounds->y / scale,
+                                   mOriginalBounds->width / scale,
+                                   mOriginalBounds->height / scale,
                                    getter_AddRefs(screen));
       if (screen) {
         int32_t left, top, width, height;
         if (NS_SUCCEEDED(screen->GetRect(&left, &top, &width, &height))) {
           Resize(left, top, width, height, true);
         }
       }
     }
--- a/xpfe/appshell/src/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/src/nsChromeTreeOwner.cpp
@@ -343,16 +343,22 @@ NS_IMETHODIMP nsChromeTreeOwner::Create(
 }
 
 NS_IMETHODIMP nsChromeTreeOwner::Destroy()
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->Destroy();
 }
 
+NS_IMETHODIMP nsChromeTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+   NS_ENSURE_STATE(mXULWindow);
+   return mXULWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+}
+
 NS_IMETHODIMP nsChromeTreeOwner::SetPosition(int32_t x, int32_t y)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SetPosition(x, y);
 }
 
 NS_IMETHODIMP nsChromeTreeOwner::GetPosition(int32_t* x, int32_t* y)
 {
--- a/xpfe/appshell/src/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/src/nsContentTreeOwner.cpp
@@ -560,16 +560,22 @@ NS_IMETHODIMP nsContentTreeOwner::Create
 }
 
 NS_IMETHODIMP nsContentTreeOwner::Destroy()
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->Destroy();
 }
 
+NS_IMETHODIMP nsContentTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double* aScale)
+{
+   NS_ENSURE_STATE(mXULWindow);
+   return mXULWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
+}
+
 NS_IMETHODIMP nsContentTreeOwner::SetPosition(int32_t aX, int32_t aY)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SetPosition(aX, aY);
 }
 
 NS_IMETHODIMP nsContentTreeOwner::GetPosition(int32_t* aX, int32_t* aY)
 {
--- a/xpfe/appshell/src/nsWebShellWindow.cpp
+++ b/xpfe/appshell/src/nsWebShellWindow.cpp
@@ -123,16 +123,23 @@ nsresult nsWebShellWindow::Initialize(ns
   if (base) {
     rv = base->GetPositionAndSize(&mOpenerScreenRect.x,
                                   &mOpenerScreenRect.y,
                                   &mOpenerScreenRect.width,
                                   &mOpenerScreenRect.height);
     if (NS_FAILED(rv)) {
       mOpenerScreenRect.SetEmpty();
     } else {
+      double scale;
+      if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
+        mOpenerScreenRect.x = NSToIntRound(mOpenerScreenRect.x / scale);
+        mOpenerScreenRect.y = NSToIntRound(mOpenerScreenRect.y / scale);
+        mOpenerScreenRect.width = NSToIntRound(mOpenerScreenRect.width / scale);
+        mOpenerScreenRect.height = NSToIntRound(mOpenerScreenRect.height / scale);
+      }
       initialX = mOpenerScreenRect.x;
       initialY = mOpenerScreenRect.y;
       ConstrainToOpenerScreen(&initialX, &initialY);
     }
   }
 
   // XXX: need to get the default window size from prefs...
   // Doesn't come from prefs... will come from CSS/XUL/RDF
@@ -714,17 +721,17 @@ void nsWebShellWindow::ConstrainToOpener
   // Constrain initial positions to the same screen as opener
   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
   if (screenmgr) {
     nsCOMPtr<nsIScreen> screen;
     screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y,
                              mOpenerScreenRect.width, mOpenerScreenRect.height,
                              getter_AddRefs(screen));
     if (screen) {
-      screen->GetAvailRect(&left, &top, &width, &height);
+      screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
       if (*aX < left || *aX > left + width) {
         *aX = left;
       }
       if (*aY < top || *aY > top + height) {
         *aY = top;
       }
     }
   }
--- a/xpfe/appshell/src/nsXULWindow.cpp
+++ b/xpfe/appshell/src/nsXULWindow.cpp
@@ -68,31 +68,16 @@ using namespace mozilla;
 #define PERSIST_ATTRIBUTE  NS_LITERAL_STRING("persist")
 #define SCREENX_ATTRIBUTE  NS_LITERAL_STRING("screenX")
 #define SCREENY_ATTRIBUTE  NS_LITERAL_STRING("screenY")
 #define WIDTH_ATTRIBUTE    NS_LITERAL_STRING("width")
 #define HEIGHT_ATTRIBUTE   NS_LITERAL_STRING("height")
 #define MODE_ATTRIBUTE     NS_LITERAL_STRING("sizemode")
 #define ZLEVEL_ATTRIBUTE   NS_LITERAL_STRING("zlevel")
 
-// Unit conversion helpers
-static int32_t
-CSSToDevPixels(int32_t aPixels, int32_t aAppPerDev)
-{
-  return NSAppUnitsToIntPixels(nsPresContext::CSSPixelsToAppUnits(aPixels),
-                               aAppPerDev);
-}
-
-static int32_t
-DevToCSSPixels(int32_t aPixels, int32_t aAppPerDev)
-{
-  return nsPresContext::AppUnitsToIntCSSPixels(
-    NSIntPixelsToAppUnits(aPixels, aAppPerDev));
-}
-
 
 //*****************************************************************************
 //***    nsXULWindow: Object Management
 //*****************************************************************************
 
 nsXULWindow::nsXULWindow(uint32_t aChromeFlags)
   : mChromeTreeOwner(nullptr), 
     mContentTreeOwner(nullptr),
@@ -108,19 +93,17 @@ nsXULWindow::nsXULWindow(uint32_t aChrom
     mLockedUntilChromeLoad(false),
     mIgnoreXULSize(false),
     mIgnoreXULPosition(false),
     mChromeFlagsFrozen(false),
     mIgnoreXULSizeMode(false),
     mContextFlags(0),
     mPersistentAttributesDirty(0),
     mPersistentAttributesMask(0),
-    mChromeFlags(aChromeFlags),
-    // best guess till we have a widget
-    mAppPerDev(nsPresContext::AppUnitsPerCSSPixel())
+    mChromeFlags(aChromeFlags)
 {
 }
 
 nsXULWindow::~nsXULWindow()
 {
   Destroy();
 }
 
@@ -526,16 +509,22 @@ NS_IMETHODIMP nsXULWindow::Destroy()
 
     if (obssvc)
       obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP nsXULWindow::GetUnscaledDevicePixelsPerCSSPixel(double *aScale)
+{
+  *aScale = mWindow ? mWindow->GetDefaultScale() : 1.0;
+  return NS_OK;
+}
+
 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.
   NS_ENSURE_SUCCESS(mWindow->Move(aX, aY), 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.
@@ -651,51 +640,65 @@ NS_IMETHODIMP nsXULWindow::Center(nsIXUL
   nsCOMPtr<nsIScreen> screen;
 
   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
+          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));
         else
           windowCoordinates = true;
       } else {
         // something's wrong with the reference window.
         // fall back to the primary screen
         aRelative = 0;
         aScreen = true;
       }
     }
   }
   if (!aRelative) {
     if (!mOpenerScreenRect.IsEmpty()) {
+      // FIXME - check if these are device or display pixels
       screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y,
                                mOpenerScreenRect.width, mOpenerScreenRect.height,
                                getter_AddRefs(screen));
     } else {
       screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
     }
   }
 
   if (aScreen && screen) {
-    screen->GetAvailRect(&left, &top, &width, &height);
+    screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
     screenCoordinates = true;
   }
 
   if (screenCoordinates || windowCoordinates) {
+    NS_ASSERTION(mWindow, "what, no window?");
+    double scale = mWindow->GetDefaultScale();
     GetSize(&ourWidth, &ourHeight);
+    ourWidth = NSToIntRound(ourWidth / scale);
+    ourHeight = NSToIntRound(ourHeight / scale);
     left += (width - ourWidth) / 2;
     top += (height - ourHeight) / (aAlert ? 3 : 2);
-    if (windowCoordinates)
+    if (windowCoordinates) {
       mWindow->ConstrainPosition(false, &left, &top);
-    SetPosition(left, top);
+    }
+    SetPosition(left * scale, top * scale);
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP nsXULWindow::Repaint(bool aForce)
 {
   //XXX First Check In
@@ -1011,56 +1014,70 @@ bool nsXULWindow::LoadPositionFromXUL()
   int32_t currY = 0;
   int32_t currWidth = 0;
   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
+  double scale = mWindow->GetDefaultScale();
+  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;
-  int32_t appPerDev = AppUnitsPerDevPixel();
 
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("screenX"), posString);
+  rv = windowElement->GetAttribute(SCREENX_ATTRIBUTE, posString);
   if (NS_SUCCEEDED(rv)) {
     temp = posString.ToInteger(&errorCode);
     if (NS_SUCCEEDED(errorCode)) {
-      specX = CSSToDevPixels(temp, appPerDev);
+      specX = temp;
       gotPosition = true;
     }
   }
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("screenY"), posString);
+  rv = windowElement->GetAttribute(SCREENY_ATTRIBUTE, posString);
   if (NS_SUCCEEDED(rv)) {
     temp = posString.ToInteger(&errorCode);
     if (NS_SUCCEEDED(errorCode)) {
-      specY = CSSToDevPixels(temp, appPerDev);
+      specY = temp;
       gotPosition = true;
     }
   }
     
   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))) {
+          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)
-    SetPosition(specX, specY);
+  if (specX != currX || specY != currY) {
+    double scale = mWindow->GetDefaultScale();
+    SetPosition(specX * scale, specY * scale);
+  }
 
   return gotPosition;
 }
 
 bool nsXULWindow::LoadSizeFromXUL()
 {
   nsresult rv;
   bool     gotSize = false;
@@ -1073,65 +1090,69 @@ bool nsXULWindow::LoadSizeFromXUL()
   nsCOMPtr<nsIDOMElement> windowElement = GetWindowDOMElement();
   NS_ENSURE_TRUE(windowElement, false);
 
   int32_t currWidth = 0;
   int32_t currHeight = 0;
   nsresult errorCode;
   int32_t temp;
 
+  NS_ASSERTION(mWindow, "we expected to have a window already");
+
+  double scale = mWindow ? mWindow->GetDefaultScale() : 1.0;
   GetSize(&currWidth, &currHeight);
+  currWidth = NSToIntRound(currWidth / scale);
+  currHeight = NSToIntRound(currHeight / scale);
 
   // Obtain the position and sizing information from the <xul:window> element.
   int32_t specWidth = currWidth;
   int32_t specHeight = currHeight;
   nsAutoString sizeString;
-  int32_t appPerDev = AppUnitsPerDevPixel();
 
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("width"), sizeString);
+  rv = windowElement->GetAttribute(WIDTH_ATTRIBUTE, sizeString);
   if (NS_SUCCEEDED(rv)) {
     temp = sizeString.ToInteger(&errorCode);
     if (NS_SUCCEEDED(errorCode) && temp > 0) {
-      specWidth = CSSToDevPixels(NS_MAX(temp, 100), appPerDev);
+      specWidth = NS_MAX(temp, 100);
       gotSize = true;
     }
   }
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("height"), sizeString);
+  rv = windowElement->GetAttribute(HEIGHT_ATTRIBUTE, sizeString);
   if (NS_SUCCEEDED(rv)) {
     temp = sizeString.ToInteger(&errorCode);
     if (NS_SUCCEEDED(errorCode) && temp > 0) {
-      specHeight = CSSToDevPixels(NS_MAX(temp, 100), appPerDev);
+      specHeight = NS_MAX(temp, 100);
       gotSize = true;
     }
   }
 
   if (gotSize) {
     // constrain to screen size
     nsCOMPtr<nsIDOMWindow> domWindow;
     GetWindowDOMWindow(getter_AddRefs(domWindow));
     if (domWindow) {
       nsCOMPtr<nsIDOMScreen> screen;
       domWindow->GetScreen(getter_AddRefs(screen));
       if (screen) {
         int32_t screenWidth;
         int32_t screenHeight;
         screen->GetAvailWidth(&screenWidth);
         screen->GetAvailHeight(&screenHeight);
-        screenWidth = CSSToDevPixels(screenWidth, appPerDev);
-        screenHeight = CSSToDevPixels(screenHeight, appPerDev);
         if (specWidth > screenWidth)
           specWidth = screenWidth;
         if (specHeight > screenHeight)
           specHeight = screenHeight;
       }
     }
 
     mIntrinsicallySized = false;
-    if (specWidth != currWidth || specHeight != currHeight)
-      SetSize(specWidth, specHeight, false);
+    if (specWidth != currWidth || specHeight != currHeight) {
+      double scale = mWindow->GetDefaultScale();
+      SetSize(specWidth * scale, specHeight * scale, false);
+    }
   }
 
   return gotSize;
 }
 
 /* Miscellaneous persistent attributes are attributes named in the
    |persist| attribute, other than size and position. Those are special
    because it's important to load those before one of the misc
@@ -1149,17 +1170,17 @@ bool nsXULWindow::LoadMiscPersistentAttr
     return false;
 
   nsCOMPtr<nsIDOMElement> windowElement = GetWindowDOMElement();
   NS_ENSURE_TRUE(windowElement, false);
 
   nsAutoString stateString;
 
   // sizemode
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("sizemode"), stateString);
+  rv = windowElement->GetAttribute(MODE_ATTRIBUTE, stateString);
   if (NS_SUCCEEDED(rv)) {
     int32_t sizeMode = nsSizeMode_Normal;
     /* ignore request to minimize, to not confuse novices
     if (stateString.Equals(SIZEMODE_MINIMIZED))
       sizeMode = nsSizeMode_Minimized;
     */
     if (!mIgnoreXULSizeMode &&
         (stateString.Equals(SIZEMODE_MAXIMIZED) || stateString.Equals(SIZEMODE_FULLSCREEN))) {
@@ -1197,17 +1218,17 @@ bool nsXULWindow::LoadMiscPersistentAttr
       ourWindow->SetFullScreen(true);
     } else {
       mWindow->SetSizeMode(sizeMode);
     }
     gotState = true;
   }
 
   // zlevel
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("zlevel"), stateString);
+  rv = windowElement->GetAttribute(ZLEVEL_ATTRIBUTE, stateString);
   if (NS_SUCCEEDED(rv) && stateString.Length() > 0) {
     nsresult errorCode;
     uint32_t zLevel = stateString.ToInteger(&errorCode);
     if (NS_SUCCEEDED(errorCode) && zLevel >= lowestZ && zLevel <= highestZ)
       SetZLevel(zLevel);
   }
 
   return gotState;
@@ -1216,19 +1237,18 @@ bool nsXULWindow::LoadMiscPersistentAttr
 /* Stagger windows of the same type so they don't appear on top of each other.
    This code does have a scary double loop -- it'll keep passing through
    the entire list of open windows until it finds a non-collision. Doesn't
    seem to be a problem, but it deserves watching.
 */
 void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
                                   int32_t aSpecWidth, int32_t aSpecHeight)
 {
-  const int32_t appPerDev = AppUnitsPerDevPixel();
-  const int32_t kOffset = CSSToDevPixels(22, appPerDev);
-  const int32_t kSlop   = CSSToDevPixels(4, appPerDev);
+  const int32_t kOffset = 22;
+  const int32_t kSlop   = 4;
 
   nsresult rv;
   bool     keepTrying;
   int      bouncedX = 0, // bounced off vertical edge of screen
            bouncedY = 0; // bounced off horizontal edge
 
   // look for any other windows of this type
   nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
@@ -1252,23 +1272,24 @@ void nsXULWindow::StaggerPosition(int32_
           screenLeft = 0;   // ... issuing warnings.
   bool    gotScreen = false;
 
   { // fetch screen coordinates
     nsCOMPtr<nsIScreenManager> screenMgr(do_GetService(
                                          "@mozilla.org/gfx/screenmanager;1"));
     if (screenMgr) {
       nsCOMPtr<nsIScreen> ourScreen;
+      // the coordinates here are already display pixels
       screenMgr->ScreenForRect(aRequestedX, aRequestedY,
                                aSpecWidth, aSpecHeight,
                                getter_AddRefs(ourScreen));
       if (ourScreen) {
         int32_t screenWidth, screenHeight;
-        ourScreen->GetAvailRect(&screenLeft, &screenTop,
-                                &screenWidth, &screenHeight);
+        ourScreen->GetAvailRectDisplayPix(&screenLeft, &screenTop,
+                                          &screenWidth, &screenHeight);
         screenBottom = screenTop + screenHeight;
         screenRight = screenLeft + screenWidth;
         gotScreen = true;
       }
     }
   }
 
   // One full pass through all windows of this type, repeat until no collisions.
@@ -1290,16 +1311,21 @@ void nsXULWindow::StaggerPosition(int32_
       nsCOMPtr<nsISupports> supportsWindow;
       windowList->GetNext(getter_AddRefs(supportsWindow));
 
       nsCOMPtr<nsIXULWindow> listXULWindow(do_QueryInterface(supportsWindow));
       if (listXULWindow != ourXULWindow) {
         int32_t listX, listY;
         nsCOMPtr<nsIBaseWindow> listBaseWindow(do_QueryInterface(supportsWindow));
         listBaseWindow->GetPosition(&listX, &listY);
+        double scale;
+        if (NS_SUCCEEDED(listBaseWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
+          listX = NSToIntRound(listX / scale);
+          listY = NSToIntRound(listY / scale);
+        }
 
         if (NS_ABS(listX - aRequestedX) <= kSlop &&
             NS_ABS(listY - aRequestedY) <= kSlop) {
           // collision! offset and start over
           if (bouncedX & 0x1)
             aRequestedX -= kOffset;
           else
             aRequestedX += kOffset;
@@ -1360,17 +1386,17 @@ void nsXULWindow::SyncAttributesToWidget
   // "accelerated" attribute
   bool isAccelerated;
   rv = windowElement->HasAttribute(NS_LITERAL_STRING("accelerated"), &isAccelerated);
   if (NS_SUCCEEDED(rv)) {
     mWindow->SetAcceleratedRendering(isAccelerated);
   }
 
   // "windowtype" attribute
-  rv = windowElement->GetAttribute(NS_LITERAL_STRING("windowtype"), attr);
+  rv = windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, attr);
   if (NS_SUCCEEDED(rv) && !attr.IsEmpty()) {
     mWindow->SetWindowClass(attr);
   }
 
   // "id" attribute for icon
   rv = windowElement->GetAttribute(NS_LITERAL_STRING("id"), attr);
   if (NS_FAILED(rv) || attr.IsEmpty()) {
     attr.AssignLiteral("default");
@@ -1415,76 +1441,72 @@ NS_IMETHODIMP nsXULWindow::SavePersisten
   }
 
   int32_t x, y, cx, cy;
   int32_t sizeMode;
 
   // get our size, position and mode to persist
   NS_ENSURE_SUCCESS(GetPositionAndSize(&x, &y, &cx, &cy), NS_ERROR_FAILURE);
   mWindow->GetSizeMode(&sizeMode);
+  double scale = mWindow->GetDefaultScale();
 
   // make our position 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))) {
       x -= parentX;
       y -= parentY;
     }
   }
 
   char                        sizeBuf[10];
   nsAutoString                sizeString;
   nsAutoString                windowElementId;
   nsCOMPtr<nsIDOMXULDocument> ownerXULDoc;
-  int32_t appPerDev = AppUnitsPerDevPixel();
 
   { // fetch docShellElement's ID and XUL owner document
     nsCOMPtr<nsIDOMDocument> ownerDoc;
     docShellElement->GetOwnerDocument(getter_AddRefs(ownerDoc));
     ownerXULDoc = do_QueryInterface(ownerDoc);
     nsCOMPtr<nsIDOMXULElement> XULElement(do_QueryInterface(docShellElement));
     if (XULElement)
       XULElement->GetId(windowElementId);
   }
 
   // (only for size elements which are persisted)
   if ((mPersistentAttributesDirty & PAD_POSITION) &&
       sizeMode == nsSizeMode_Normal) {
     if (persistString.Find("screenX") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%ld",
-                  (long)DevToCSSPixels(x, appPerDev));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(x / scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString);
       if (ownerXULDoc) // force persistence in case the value didn't change
         ownerXULDoc->Persist(windowElementId, SCREENX_ATTRIBUTE);
     }
     if (persistString.Find("screenY") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%ld",
-                  (long)DevToCSSPixels(y, appPerDev));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(y / scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString);
       if (ownerXULDoc)
         ownerXULDoc->Persist(windowElementId, SCREENY_ATTRIBUTE);
     }
   }
 
   if ((mPersistentAttributesDirty & PAD_SIZE) &&
       sizeMode == nsSizeMode_Normal) {
     if (persistString.Find("width") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%ld",
-                  (long)DevToCSSPixels(cx, appPerDev));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(cx / scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString);
       if (ownerXULDoc)
         ownerXULDoc->Persist(windowElementId, WIDTH_ATTRIBUTE);
     }
     if (persistString.Find("height") >= 0) {
-      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%ld",
-                  (long)DevToCSSPixels(cy, appPerDev));
+      PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(cy / scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString);
       if (ownerXULDoc)
         ownerXULDoc->Persist(windowElementId, HEIGHT_ATTRIBUTE);
     }
   }
 
   if (mPersistentAttributesDirty & PAD_MISC) {
@@ -2037,31 +2059,16 @@ NS_IMETHODIMP nsXULWindow::GetXULBrowser
 
 NS_IMETHODIMP nsXULWindow::SetXULBrowserWindow(nsIXULBrowserWindow * aXULBrowserWindow)
 {
   mXULBrowserWindow = aXULBrowserWindow;
   return NS_OK;
 }
 
 //*****************************************************************************
-// nsXULWindow: Accessors
-//*****************************************************************************
-
-uint32_t nsXULWindow::AppUnitsPerDevPixel()
-{
-  if (mWindow && mWindow->GetDeviceContext()) {
-    mAppPerDev = mWindow->GetDeviceContext()->AppUnitsPerDevPixel();
-  } else {
-    NS_ERROR("nsXULWindow::AppUnitsPerDevPixel called with no window "
-             "or no dev context");
-  }
-  return mAppPerDev;
-}
-
-//*****************************************************************************
 //*** nsContentShellInfo: Object Management
 //*****************************************************************************   
 
 nsContentShellInfo::nsContentShellInfo(const nsAString& aID,
                                        nsIWeakReference* aContentShell)
   : id(aID),
     child(aContentShell)
 {
--- a/xpfe/appshell/src/nsXULWindow.h
+++ b/xpfe/appshell/src/nsXULWindow.h
@@ -107,17 +107,16 @@ protected:
    void       EnableParent(bool aEnable);
    bool       ConstrainToZLevel(bool aImmediate, nsWindowZ *aPlacement,
                                 nsIWidget *aReqBelow, nsIWidget **aActualBelow);
    void       PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
                                       nsIXULWindow *aBehind);
    void       SetContentScrollbarVisibility(bool aVisible);
    bool       GetContentScrollbarVisibility();
    void       PersistentAttributesDirty(uint32_t aDirtyFlags);
-   uint32_t   AppUnitsPerDevPixel();
 
    nsChromeTreeOwner*      mChromeTreeOwner;
    nsContentTreeOwner*     mContentTreeOwner;
    nsContentTreeOwner*     mPrimaryContentTreeOwner;
    nsCOMPtr<nsIWidget>     mWindow;
    nsCOMPtr<nsIDocShell>   mDocShell;
    nsCOMPtr<nsIDOMWindow>  mDOMWindow;
    nsCOMPtr<nsIWeakReference> mParentWindow;
@@ -138,18 +137,16 @@ protected:
    bool                    mIgnoreXULSize;
    bool                    mIgnoreXULPosition;
    bool                    mChromeFlagsFrozen;
    bool                    mIgnoreXULSizeMode;
    uint32_t                mContextFlags;
    uint32_t                mPersistentAttributesDirty; // persistentAttributes
    uint32_t                mPersistentAttributesMask;
    uint32_t                mChromeFlags;
-   uint32_t                mAppPerDev; // sometimes needed when we can't get
-                                       // it from the widget
    nsString                mTitle;
    nsIntRect               mOpenerScreenRect; // the screen rect of the opener
 
    nsCOMArray<nsIWeakReference> mTargetableShells; // targetable shells only
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)