bug 820327 - work around potentially misleading backingScaleFactor returned by windows with zero area. r=roc
authorJonathan Kew <jkew@mozilla.com>
Fri, 14 Dec 2012 09:46:16 +0000
changeset 125148 11fd34243cbd30bf101cfe3f9b37e58862fb1e98
parent 125147 b1fb4089fa9bce285d6cdbc3c57272cabcada21e
child 125149 49545b3e71d6e0e3009618d3e2d0e43950f95522
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs820327
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 820327 - work around potentially misleading backingScaleFactor returned by windows with zero area. r=roc
widget/cocoa/nsCocoaWindow.mm
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -1424,33 +1424,75 @@ NS_IMETHODIMP nsCocoaWindow::GetScreenBo
 }
 
 double
 nsCocoaWindow::GetDefaultScaleInternal()
 {
   return BackingScaleFactor();
 }
 
+static CGFloat
+GetBackingScaleFactor(NSWindow* aWindow)
+{
+  NSRect frame = [aWindow frame];
+  if (frame.size.width > 0 && frame.size.height > 0) {
+    return nsCocoaUtils::GetBackingScaleFactor(aWindow);
+  }
+
+  // For windows with zero width or height, the backingScaleFactor method
+  // is broken - it will always return 2 on a retina macbook, even when
+  // the window position implies it's on a non-hidpi external display
+  // (to the extent that a zero-area window can be said to be "on" a
+  // display at all!)
+  // And to make matters worse, Cocoa even fires a
+  // windowDidChangeBackingProperties notification with the
+  // NSBackingPropertyOldScaleFactorKey key when a window on an
+  // external display is resized to/from zero height, even though it hasn't
+  // really changed screens.
+
+  // This causes us to handle popup window sizing incorrectly when the
+  // popup is resized to zero height (bug 820327) - nsXULPopupManager
+  // becomes (incorrectly) convinced the popup has been explicitly forced
+  // to a non-default size and needs to have size attributes attached.
+
+  // Workaround: instead of asking the window, we'll find the screen it is on
+  // and ask that for *its* backing scale factor.
+
+  // First, expand the rect so that it actually has a measurable area,
+  // for FindTargetScreenForRect to use.
+  if (frame.size.width == 0) {
+    frame.size.width = 1;
+  }
+  if (frame.size.height == 0) {
+    frame.size.height = 1;
+  }
+
+  // Then identify the screen it belongs to, and return its scale factor.
+  NSScreen *screen =
+    FindTargetScreenForRect(nsCocoaUtils::CocoaRectToGeckoRect(frame));
+  return nsCocoaUtils::GetBackingScaleFactor(screen);
+}
+
 CGFloat
 nsCocoaWindow::BackingScaleFactor()
 {
   if (mBackingScaleFactor > 0.0) {
     return mBackingScaleFactor;
   }
   if (!mWindow) {
     return 1.0;
   }
-  mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mWindow);
+  mBackingScaleFactor = GetBackingScaleFactor(mWindow);
   return mBackingScaleFactor;
 }
 
 void
 nsCocoaWindow::BackingScaleFactorChanged()
 {
-  CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mWindow);
+  CGFloat newScale = 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) {
@@ -2372,17 +2414,17 @@ GetDPI(NSWindow* aWindow)
   // of userSpaceScaleFactor, so every "pixel" we draw is actually
   // userSpaceScaleFactor screen pixels. So divide the screen height
   // by userSpaceScaleFactor to get the number of "device pixels"
   // available.
   float dpi = (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
 
   // Account for HiDPI mode where Cocoa's "points" do not correspond to real
   // device pixels
-  CGFloat backingScale = nsCocoaUtils::GetBackingScaleFactor(aWindow);
+  CGFloat backingScale = GetBackingScaleFactor(aWindow);
 
   return dpi * backingScale;
 }
 
 @interface BaseWindow(Private)
 - (void)removeTrackingArea;
 - (void)cursorUpdated:(NSEvent*)aEvent;
 @end