Bug 1249279 - Let subdocuments' presContexts inherit the DPI setting of their parent, instead of retrieving it from their widget, to avoid using stale values from a currently-hidden widget on a screen with a different DPI. r=bz a=ritu
authorJonathan Kew <jkew@mozilla.com>
Thu, 07 Apr 2016 10:01:30 +0100
changeset 323881 1cff2bbb09ff3d87be20c32ceade91b70b40ad40
parent 323880 73630aefa1568ea416bafa24421612e4093b05c2
child 323882 81882fedce8acaf7dedfdf525cbea938fb651de5
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, ritu
bugs1249279
milestone47.0a2
Bug 1249279 - Let subdocuments' presContexts inherit the DPI setting of their parent, instead of retrieving it from their widget, to avoid using stale values from a currently-hidden widget on a screen with a different DPI. r=bz a=ritu
gfx/src/nsDeviceContext.cpp
gfx/src/nsDeviceContext.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -305,17 +305,17 @@ nsDeviceContext::FontMetricsDeleted(cons
 
 bool
 nsDeviceContext::IsPrinterSurface()
 {
     return mPrintingSurface != nullptr;
 }
 
 void
-nsDeviceContext::SetDPI()
+nsDeviceContext::SetDPI(double* aScale)
 {
     float dpi = -1.0f;
 
     // Use the printing DC to determine DPI values, if we have one.
     if (mDeviceContextSpec) {
         dpi = mDeviceContextSpec->GetDPI();
         mPrintingScale = mDeviceContextSpec->GetPrintingScale();
         mAppUnitsPerDevPixelAtUnitFullZoom =
@@ -334,19 +334,31 @@ nsDeviceContext::SetDPI()
 
             if (prefDPI < 0) {
                 dpi = std::max(96.0f, dpi);
             }
         } else {
             dpi = 96.0f;
         }
 
-        CSSToLayoutDeviceScale scale = mWidget ? mWidget->GetDefaultScale()
-                                               : CSSToLayoutDeviceScale(1.0);
-        double devPixelsPerCSSPixel = scale.scale;
+        double devPixelsPerCSSPixel;
+        if (aScale && *aScale > 0.0) {
+            // if caller provided a scale, we just use it
+            devPixelsPerCSSPixel = *aScale;
+        } else {
+            // otherwise get from the widget, and return it in aScale for
+            // the caller to pass to child contexts if needed
+            CSSToLayoutDeviceScale scale =
+                mWidget ? mWidget->GetDefaultScale()
+                        : CSSToLayoutDeviceScale(1.0);
+            devPixelsPerCSSPixel = scale.scale;
+            if (aScale) {
+                *aScale = devPixelsPerCSSPixel;
+            }
+        }
 
         mAppUnitsPerDevPixelAtUnitFullZoom =
             std::max(1, NS_lround(AppUnitsPerCSSPixel() / devPixelsPerCSSPixel));
     }
 
     NS_ASSERTION(dpi != -1.0, "no dpi set");
 
     mAppUnitsPerPhysicalInch = NS_lround(dpi * mAppUnitsPerDevPixelAtUnitFullZoom);
@@ -686,21 +698,22 @@ nsDeviceContext::CalcPrintingSize()
     mWidth = NSToCoordRound(size.width * AppUnitsPerPhysicalInch()
                             / POINTS_PER_INCH_FLOAT);
     mHeight = NSToCoordRound(size.height * AppUnitsPerPhysicalInch()
                              / POINTS_PER_INCH_FLOAT);
 
     return (mWidth > 0 && mHeight > 0);
 }
 
-bool nsDeviceContext::CheckDPIChange() {
+bool nsDeviceContext::CheckDPIChange(double* aScale)
+{
     int32_t oldDevPixels = mAppUnitsPerDevPixelAtUnitFullZoom;
     int32_t oldInches = mAppUnitsPerPhysicalInch;
 
-    SetDPI();
+    SetDPI(aScale);
 
     return oldDevPixels != mAppUnitsPerDevPixelAtUnitFullZoom ||
         oldInches != mAppUnitsPerPhysicalInch;
 }
 
 bool
 nsDeviceContext::SetFullZoom(float aScale)
 {
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -222,22 +222,28 @@ public:
      * Inform the output device that output of a page is ending
      * Used for print related device contexts. Must be matched 1:1 with
      * BeginPage() and within a BeginDocument()/EndDocument() pair.
      * @return error status
      */
     nsresult EndPage();
 
     /**
-     * Check to see if the DPI has changed
+     * Check to see if the DPI has changed, or impose a new DPI scale value.
+     * @param  aScale - If non-null, the default (unzoomed) CSS to device pixel
+     *                  scale factor will be returned here; and if it is > 0.0
+     *                  on input, the given value will be used instead of
+     *                  getting it from the widget (if any). This is used to
+     *                  allow subdocument contexts to inherit the resolution
+     *                  setting of their parent.
      * @return whether there was actually a change in the DPI (whether
      *         AppUnitsPerDevPixel() or AppUnitsPerPhysicalInch()
      *         changed)
      */
-    bool CheckDPIChange();
+    bool CheckDPIChange(double* aScale = nullptr);
 
     /**
      * Set the full zoom factor: all lengths are multiplied by this factor
      * when we convert them to device pixels. Returns whether the ratio of
      * app units to dev pixels changed because of the zoom factor.
      */
     bool SetFullZoom(float aScale);
 
@@ -252,17 +258,17 @@ public:
     bool IsPrinterSurface();
 
     mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale();
 
 private:
     // Private destructor, to discourage deletion outside of Release():
     ~nsDeviceContext();
 
-    void SetDPI();
+    void SetDPI(double* aScale = nullptr);
     void ComputeClientRectUsingScreen(nsRect *outRect);
     void ComputeFullAreaUsingScreen(nsRect *outRect);
     void FindScreen(nsIScreen **outScreen);
 
     // Return false if the surface is not right
     bool CalcPrintingSize();
     void UpdateAppUnitsForFullZoom();
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1939,29 +1939,34 @@ nsPresContext::UIResolutionChanged()
   }
 }
 
 void
 nsPresContext::UIResolutionChangedSync()
 {
   if (!mPendingUIResolutionChanged) {
     mPendingUIResolutionChanged = true;
-    UIResolutionChangedInternal();
+    UIResolutionChangedInternalScale(0.0);
   }
 }
 
 /*static*/ bool
 nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument,
                                                       void* aData)
 {
   nsIPresShell* shell = aDocument->GetShell();
   if (shell) {
     nsPresContext* pc = shell->GetPresContext();
     if (pc) {
-      pc->UIResolutionChangedInternal();
+      // For subdocuments, we want to apply the parent's scale, because there
+      // are cases where the subdoc's device context is connected to a widget
+      // that has an out-of-date resolution (it's on a different screen, but
+      // currently hidden, and will not be updated until shown): bug 1249279.
+      double scale = *static_cast<double*>(aData);
+      pc->UIResolutionChangedInternalScale(scale);
     }
   }
   return true;
 }
 
 static void
 NotifyTabUIResolutionChanged(TabParent* aTab, void *aArg)
 {
@@ -1977,30 +1982,36 @@ NotifyChildrenUIResolutionChanged(nsPIDO
     return;
   }
   topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
 }
 
 void
 nsPresContext::UIResolutionChangedInternal()
 {
+  UIResolutionChangedInternalScale(0.0);
+}
+
+void
+nsPresContext::UIResolutionChangedInternalScale(double aScale)
+{
   mPendingUIResolutionChanged = false;
 
-  mDeviceContext->CheckDPIChange();
+  mDeviceContext->CheckDPIChange(&aScale);
   if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) {
     AppUnitsPerDevPixelChanged();
   }
 
   // Recursively notify all remote leaf descendants of the change.
   if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
     NotifyChildrenUIResolutionChanged(window);
   }
 
   mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
-                                   nullptr);
+                                   &aScale);
 }
 
 void
 nsPresContext::EmulateMedium(const nsAString& aMediaType)
 {
   nsIAtom* previousMedium = Medium();
   mIsEmulatingMedia = true;
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1098,18 +1098,27 @@ public:
     return false;
 #endif
   }
 
 protected:
   friend class nsRunnableMethod<nsPresContext>;
   void ThemeChangedInternal();
   void SysColorChangedInternal();
+
+  // update device context's resolution from the widget
   void UIResolutionChangedInternal();
 
+  // if aScale > 0.0, use it as resolution scale factor to the device context
+  // (otherwise get it from the widget)
+  void UIResolutionChangedInternalScale(double aScale);
+
+  // aData here is a pointer to a double that holds the CSS to device-pixel
+  // scale factor from the parent, which will be applied to the subdocument's
+  // device context instead of retrieving a scale from the widget.
   static bool
   UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData);
 
   void SetImgAnimations(nsIContent *aParent, uint16_t aMode);
   void SetSMILAnimations(nsIDocument *aDoc, uint16_t aNewMode,
                                      uint16_t aOldMode);
   void GetDocumentColorPreferences();