Bug 1578377 - Render dark scrollbars for element with dark background on Windows. r=jmathies
authorXidorn Quan <me@upsuper.org>
Wed, 23 Oct 2019 21:00:28 +0000
changeset 498827 9f72a5a0105dffe5eed579f1af4c6e6cf84f49cb
parent 498826 d68244d4b2dc69acbd0d6f0d4620089a6ffeef8c
child 498828 724c75f9d98fa441703a4e36f294672859e138d7
child 498829 3433d3e346bf6b53348124253aebc2187221c3c3
push id36728
push usershindli@mozilla.com
push dateThu, 24 Oct 2019 14:47:35 +0000
treeherdermozilla-central@724c75f9d98f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmathies
bugs1578377
milestone72.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 1578377 - Render dark scrollbars for element with dark background on Windows. r=jmathies Differential Revision: https://phabricator.services.mozilla.com/D48289
layout/tools/reftest/runreftest.py
modules/libpref/init/StaticPrefList.yaml
testing/profiles/web-platform/user.js
widget/nsNativeTheme.cpp
widget/nsNativeTheme.h
widget/windows/nsNativeThemeWin.cpp
widget/windows/nsNativeThemeWin.h
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -357,16 +357,18 @@ class RefTest(object):
         if options.verify:
             prefs['reftest.verify'] = True
         if options.cleanupCrashes:
             prefs['reftest.cleanupPendingCrashes'] = True
         prefs['reftest.focusFilterMode'] = options.focusFilterMode
         prefs['reftest.logLevel'] = options.log_tbpl_level or 'info'
         prefs['reftest.suite'] = options.suite
         prefs['gfx.font_rendering.ahem_antialias_none'] = True
+        # Disable dark scrollbars because it's semi-transparent.
+        prefs['widget.disable-dark-scrollbar'] = True
 
         # Set tests to run or manifests to parse.
         if tests:
             testlist = os.path.join(profile.profile, 'reftests.json')
             with open(testlist, 'w') as fh:
                 json.dump(tests, fh)
             prefs['reftest.tests'] = testlist
         elif manifests:
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -7491,16 +7491,26 @@
 #---------------------------------------------------------------------------
 
 # Global user preference for disabling native theme. Used in NativeWindowTheme.
 - name: widget.disable-native-theme
   type: bool
   value: false
   mirror: always
 
+# Preference to disable dark scrollbar implementation.
+# This is mainly for testing because dark scrollbars have to be semi-
+# transparent, but many reftests expect scrollbars to look identical
+# among different backgrounds.
+# However, some users may want to disable this as well.
+- name: widget.disable-dark-scrollbar
+  type: bool
+  value: false
+  mirror: always
+
 - name: widget.window-transforms.disabled
   type: RelaxedAtomicBool
   value: false
   mirror: always
 
 #---------------------------------------------------------------------------
 # Prefs starting with "xul."
 #---------------------------------------------------------------------------
--- a/testing/profiles/web-platform/user.js
+++ b/testing/profiles/web-platform/user.js
@@ -35,12 +35,15 @@ user_pref("browser.safebrowsing.phishing
 // Automatically unload beforeunload alerts
 user_pref("dom.disable_beforeunload", true);
 // Enable implicit keyframes since the common animation interpolation test
 // function assumes this is available.
 user_pref("dom.animations-api.implicit-keyframes.enabled", true);
 // sometime wpt runs test even before the document becomes visible, which would
 // delay video.play() and cause play() running in wrong order.
 user_pref("media.block-autoplay-until-in-foreground", false);
+// Disable dark scrollbars as it can be semi-transparent that many reftests
+// don't expect.
+user_pref("widget.disable-dark-scrollbar", true);
 user_pref("media.block-autoplay-until-in-foreground", false);
 // Enable AppCache globally for now whilst it's being removed in Bug 1584984
 user_pref("browser.cache.offline.storage.enable", true);
 user_pref("browser.cache.offline.enable", true);
--- a/widget/nsNativeTheme.cpp
+++ b/widget/nsNativeTheme.cpp
@@ -647,16 +647,17 @@ static nsIFrame* GetBodyFrame(nsIFrame* 
   }
   nsIContent* body = content->OwnerDoc()->GetBodyElement();
   if (!body) {
     return nullptr;
   }
   return body->GetPrimaryFrame();
 }
 
+/* static */
 bool nsNativeTheme::IsDarkBackground(nsIFrame* aFrame) {
   nsIScrollableFrame* scrollFrame = nullptr;
   while (!scrollFrame && aFrame) {
     scrollFrame = aFrame->GetScrollTargetFrame();
     aFrame = aFrame->GetParent();
   }
   if (!scrollFrame) return false;
 
--- a/widget/nsNativeTheme.h
+++ b/widget/nsNativeTheme.h
@@ -180,17 +180,17 @@ class nsNativeTheme : public nsITimerCal
                                       uint32_t aMinimumFrameRate);
 
   nsIFrame* GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
                                                       bool aNextSibling);
 
   bool IsRangeHorizontal(nsIFrame* aFrame);
 
   // scrollbar
-  bool IsDarkBackground(nsIFrame* aFrame);
+  static bool IsDarkBackground(nsIFrame* aFrame);
   // custom scrollbar
   typedef nscolor (*AutoColorGetter)(mozilla::ComputedStyle*);
   bool IsWidgetScrollbarPart(mozilla::StyleAppearance aAppearance);
 
  private:
   uint32_t mAnimatedContentTimeout;
   nsCOMPtr<nsITimer> mAnimatedContentTimer;
   AutoTArray<nsCOMPtr<nsIContent>, 20> mAnimatedContentList;
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -1509,36 +1509,52 @@ static bool IsScrollbarWidthThin(Compute
   return scrollbarWidth == StyleScrollbarWidth::Thin;
 }
 
 static bool IsScrollbarWidthThin(nsIFrame* aFrame) {
   ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
   return IsScrollbarWidthThin(style);
 }
 
-static bool ShouldDrawCustomScrollbar(ComputedStyle* aStyle) {
-  return aStyle->StyleUI()->HasCustomScrollbars() ||
-         IsScrollbarWidthThin(aStyle);
+// Returns the style for custom scrollbar if the scrollbar part frame should
+// use the custom drawing path, nullptr otherwise.
+//
+// Optionally the caller can pass a pointer to aForDarkBg for whether custom
+// scrollbar may be drawn due to dark background.
+static ComputedStyle* GetCustomScrollbarStyle(nsIFrame* aFrame,
+                                              bool* aDarkScrollbar = nullptr) {
+  ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
+  if (style->StyleUI()->HasCustomScrollbars()) {
+    return style;
+  }
+  bool useDarkScrollbar = !StaticPrefs::widget_disable_dark_scrollbar() &&
+                          nsNativeTheme::IsDarkBackground(aFrame);
+  if (useDarkScrollbar || IsScrollbarWidthThin(style)) {
+    if (aDarkScrollbar) {
+      *aDarkScrollbar = useDarkScrollbar;
+    }
+    return style;
+  }
+  return nullptr;
 }
 
 NS_IMETHODIMP
 nsNativeThemeWin::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
                                        StyleAppearance aAppearance,
                                        const nsRect& aRect,
                                        const nsRect& aDirtyRect) {
   if (aAppearance == StyleAppearance::MenulistButton &&
       StaticPrefs::layout_css_webkit_appearance_enabled()) {
     aAppearance = StyleAppearance::Menulist;
   }
 
   if (IsWidgetScrollbarPart(aAppearance)) {
-    ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
-    if (ShouldDrawCustomScrollbar(style)) {
-      return DrawCustomScrollbarPart(aContext, aFrame, style, aAppearance,
-                                     aRect, aDirtyRect);
+    if (MayDrawCustomScrollbarPart(aContext, aFrame, aAppearance, aRect,
+                                   aDirtyRect)) {
+      return NS_OK;
     }
   }
 
   HANDLE theme = GetTheme(aAppearance);
   if (!theme)
     return ClassicDrawWidgetBackground(aContext, aFrame, aAppearance, aRect,
                                        aDirtyRect);
 
@@ -2654,18 +2670,17 @@ nsITheme::ThemeGeometryType nsNativeThem
     default:
       return eThemeGeometryTypeUnknown;
   }
 }
 
 nsITheme::Transparency nsNativeThemeWin::GetWidgetTransparency(
     nsIFrame* aFrame, StyleAppearance aAppearance) {
   if (IsWidgetScrollbarPart(aAppearance)) {
-    ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
-    if (ShouldDrawCustomScrollbar(style)) {
+    if (ComputedStyle* style = GetCustomScrollbarStyle(aFrame)) {
       auto* ui = style->StyleUI();
       if (ui->mScrollbarColor.IsAuto() ||
           ui->mScrollbarColor.AsColors().track.MaybeTransparent()) {
         return eTransparent;
       }
       // DrawCustomScrollbarPart doesn't draw the track background for
       // widgets on it, and these widgets are thinner than the track,
       // so we need to return transparent for them.
@@ -4153,43 +4168,52 @@ static nscolor AdjustScrollbarFaceColor(
     } else {
       luminance /= 0.625f;
     }
   }
   return RelativeLuminanceUtils::Adjust(aFaceColor, luminance);
 }
 
 // This tries to draw a Windows 10 style scrollbar with given colors.
-nsresult nsNativeThemeWin::DrawCustomScrollbarPart(
-    gfxContext* aContext, nsIFrame* aFrame, ComputedStyle* aStyle,
-    StyleAppearance aAppearance, const nsRect& aRect, const nsRect& aClipRect) {
+bool nsNativeThemeWin::MayDrawCustomScrollbarPart(gfxContext* aContext,
+                                                  nsIFrame* aFrame,
+                                                  StyleAppearance aAppearance,
+                                                  const nsRect& aRect,
+                                                  const nsRect& aClipRect) {
+  bool darkScrollbar = false;
+  ComputedStyle* style = GetCustomScrollbarStyle(aFrame, &darkScrollbar);
+  if (!style) {
+    return false;
+  }
+
   EventStates eventStates = GetContentState(aFrame, aAppearance);
 
   gfxContextAutoSaveRestore autoSave(aContext);
   RefPtr<gfxContext> ctx = aContext;
   gfxFloat p2a = gfxFloat(aFrame->PresContext()->AppUnitsPerDevPixel());
   gfxRect clipRect = ThebesRect(
       LayoutDevicePixel::FromAppUnits(aClipRect, p2a).ToUnknownRect());
   ctx->Clip(clipRect);
   gfxRect rect =
       ThebesRect(LayoutDevicePixel::FromAppUnits(aRect, p2a).ToUnknownRect());
 
-  const nsStyleUI* ui = aStyle->StyleUI();
+  const nsStyleUI* ui = style->StyleUI();
   auto* customColors =
       ui->mScrollbarColor.IsAuto() ? nullptr : &ui->mScrollbarColor.AsColors();
-  nscolor trackColor = customColors ? customColors->track.CalcColor(*aStyle)
-                                    : NS_RGB(240, 240, 240);
+  nscolor trackColor = customColors ? customColors->track.CalcColor(*style)
+                                    : (darkScrollbar ? NS_RGBA(20, 20, 25, 77)
+                                                     : NS_RGB(240, 240, 240));
   switch (aAppearance) {
     case StyleAppearance::ScrollbarHorizontal:
     case StyleAppearance::ScrollbarVertical:
     case StyleAppearance::Scrollcorner: {
       ctx->SetColor(Color::FromABGR(trackColor));
       ctx->Rectangle(rect);
       ctx->Fill();
-      return NS_OK;
+      return true;
     }
     default:
       break;
   }
 
   // Scrollbar thumb and button are two CSS pixels thinner than the track.
   gfxRect bgRect = rect;
   gfxFloat dev2css = round(AppUnitsPerCSSPixel() / p2a);
@@ -4206,18 +4230,20 @@ nsresult nsNativeThemeWin::DrawCustomScr
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown widget type");
   }
 
   switch (aAppearance) {
     case StyleAppearance::ScrollbarthumbVertical:
     case StyleAppearance::ScrollbarthumbHorizontal: {
-      nscolor faceColor = customColors ? customColors->thumb.CalcColor(*aStyle)
-                                       : NS_RGB(205, 205, 205);
+      nscolor faceColor = customColors
+                              ? customColors->thumb.CalcColor(*style)
+                              : (darkScrollbar ? NS_RGBA(249, 249, 250, 102)
+                                               : NS_RGB(205, 205, 205));
       faceColor = AdjustScrollbarFaceColor(faceColor, eventStates);
       ctx->SetColor(Color::FromABGR(faceColor));
       ctx->Rectangle(bgRect);
       ctx->Fill();
       break;
     }
     case StyleAppearance::ScrollbarbuttonUp:
     case StyleAppearance::ScrollbarbuttonDown:
@@ -4271,17 +4297,17 @@ nsresult nsNativeThemeWin::DrawCustomScr
       nscolor arrowColor = GetScrollbarArrowColor(buttonColor);
       ctx->SetColor(Color::FromABGR(arrowColor));
       ctx->Fill();
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown widget type");
   }
-  return NS_OK;
+  return true;
 }
 
 ///////////////////////////////////////////
 // Creation Routine
 ///////////////////////////////////////////
 
 already_AddRefed<nsITheme> do_GetNativeTheme() {
   if (StaticPrefs::widget_disable_native_theme()) return nullptr;
--- a/widget/windows/nsNativeThemeWin.h
+++ b/widget/windows/nsNativeThemeWin.h
@@ -100,21 +100,19 @@ class nsNativeThemeWin : private nsNativ
   nsresult ClassicGetMinimumWidgetSize(nsIFrame* aFrame,
                                        StyleAppearance aAppearance,
                                        mozilla::LayoutDeviceIntSize* aResult,
                                        bool* aIsOverridable);
   bool ClassicThemeSupportsWidget(nsIFrame* aFrame,
                                   StyleAppearance aAppearance);
   void DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore, int32_t back,
                        HBRUSH defaultBack);
-  nsresult DrawCustomScrollbarPart(gfxContext* aContext, nsIFrame* aFrame,
-                                   mozilla::ComputedStyle* aStyle,
-                                   StyleAppearance aAppearance,
-                                   const nsRect& aRect,
-                                   const nsRect& aClipRect);
+  bool MayDrawCustomScrollbarPart(gfxContext* aContext, nsIFrame* aFrame,
+                                  StyleAppearance aAppearance,
+                                  const nsRect& aRect, const nsRect& aClipRect);
   uint32_t GetWidgetNativeDrawingFlags(StyleAppearance aAppearance);
   int32_t StandardGetState(nsIFrame* aFrame, StyleAppearance aAppearance,
                            bool wantFocused);
   bool IsMenuActive(nsIFrame* aFrame, StyleAppearance aAppearance);
   RECT CalculateProgressOverlayRect(nsIFrame* aFrame, RECT* aWidgetRect,
                                     bool aIsVertical, bool aIsIndeterminate,
                                     bool aIsClassic);
   void DrawThemedProgressMeter(nsIFrame* aFrame, StyleAppearance aAppearance,