Bug 1486204 - Make nsLookAndFeel.mm return transparent selection colors. r=mstange, a=RyanVM
authorTim Nguyen <ntim.bugs@gmail.com>
Mon, 10 Dec 2018 22:41:46 +0000
changeset 508943 28756c341f0273bd68676aae2beda4a09e63043d
parent 508942 a4803c65849c00df7b2b094ed2246ff5aaf666b8
child 508944 318239ee898f92d45f0cff66e7f431087c48edd1
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, RyanVM
bugs1486204
milestone65.0
Bug 1486204 - Make nsLookAndFeel.mm return transparent selection colors. r=mstange, a=RyanVM Differential Revision: https://phabricator.services.mozilla.com/D8210
layout/generic/nsTextFrame.cpp
widget/cocoa/moz.build
widget/cocoa/nsLookAndFeel.h
widget/cocoa/nsLookAndFeel.mm
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -430,16 +430,20 @@ class nsTextPaintStyle {
 
   // Color initializations
   void InitCommonColors();
   bool InitSelectionColorsAndShadow();
 
   nsSelectionStyle* GetSelectionStyle(int32_t aIndex);
   void InitSelectionStyle(int32_t aIndex);
 
+  // Ensures sufficient contrast between the frame background color and the
+  // selection background color, and swaps the selection text and background
+  // colors accordingly.
+  // Only used on platforms where mSelectionTextColor != NS_DONT_CHANGE_COLOR
   bool EnsureSufficientContrast(nscolor* aForeColor, nscolor* aBackColor);
 
   nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
                                nscolor aBackColor);
 };
 
 static TextRunUserData* CreateUserData(uint32_t aMappedFlowCount) {
   TextRunUserData* data = static_cast<TextRunUserData*>(moz_xmalloc(
@@ -4013,17 +4017,18 @@ bool nsTextPaintStyle::InitSelectionColo
   } else {
     mSelectionBGColor = selectionBGColor;
   }
 
   mSelectionTextColor =
       LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
 
   if (mResolveColors) {
-    // On MacOS X, we don't exchange text color and BG color.
+    // On MacOS X, only the background color gets set,
+    // the text color remains intact.
     if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
       nscolor frameColor =
           nsSVGUtils::IsInSVGTextSubtree(mFrame)
               ? mFrame->GetVisitedDependentColor(&nsStyleSVG::mFill)
               : mFrame->GetVisitedDependentColor(
                     &nsStyleText::mWebkitTextFillColor);
       mSelectionTextColor =
           EnsureDifferentColors(frameColor, mSelectionBGColor);
--- a/widget/cocoa/moz.build
+++ b/widget/cocoa/moz.build
@@ -79,16 +79,17 @@ if not CONFIG['RELEASE_OR_BETA'] or CONF
     SOURCES += [
         'nsSandboxViolationSink.mm',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
+    '/layout/base',
     '/layout/forms',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
     '/widget',
     '/widget/headless',
 ]
 
--- a/widget/cocoa/nsLookAndFeel.h
+++ b/widget/cocoa/nsLookAndFeel.h
@@ -32,16 +32,17 @@ class nsLookAndFeel final : public nsXPL
   virtual void SetIntCacheImpl(
       const nsTArray<LookAndFeelInt>& aLookAndFeelIntCache) override;
 
  protected:
   static bool SystemWantsOverlayScrollbars();
   static bool AllowOverlayScrollbarsOverlap();
 
   static bool SystemWantsDarkTheme();
+  static nscolor ProcessSelectionBackground(nscolor aColor);
 
  private:
   int32_t mUseOverlayScrollbars;
   bool mUseOverlayScrollbarsCached;
 
   int32_t mAllowOverlayScrollbarsOverlap;
   bool mAllowOverlayScrollbarsOverlapCached;
 
--- a/widget/cocoa/nsLookAndFeel.mm
+++ b/widget/cocoa/nsLookAndFeel.mm
@@ -8,16 +8,17 @@
 #include "nsIServiceManager.h"
 #include "nsNativeThemeColors.h"
 #include "nsStyleConsts.h"
 #include "nsCocoaFeatures.h"
 #include "nsIContent.h"
 #include "gfxFont.h"
 #include "gfxFontConstants.h"
 #include "gfxPlatformMac.h"
+#include "nsCSSColorUtils.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
 
 #import <Cocoa/Cocoa.h>
 
 // This must be included last:
 #include "nsObjCExceptions.h"
@@ -127,16 +128,42 @@ nsLookAndFeel::RefreshImpl()
     mAllowOverlayScrollbarsOverlapCached = false;
     mPrefersReducedMotionCached = false;
   }
 
   // Fetch colors next time they are requested.
   mInitialized = false;
 }
 
+// Turns an opaque selection color into a partially transparent selection color,
+// which usually leads to better contrast with the text color and which should
+// look more visually appealing in most contexts.
+// The idea is that the text and its regular, non-selected background are
+// usually chosen in such a way that they contrast well. Making the selection
+// color partially transparent causes the selection color to mix with the text's
+// regular background, so the end result will often have better contrast with
+// the text than an arbitrary opaque selection color.
+// The motivating example for this is the URL bar text field in the dark theme:
+// White text on a light blue selection color has very bad contrast, whereas
+// white text on dark blue (which what you get if you mix partially-transparent
+// light blue with the black textbox background) has much better contrast.
+nscolor
+nsLookAndFeel::ProcessSelectionBackground(nscolor aColor)
+{
+  uint16_t hue, sat, value;
+  uint8_t alpha;
+  nscolor resultColor = aColor;
+  NS_RGB2HSV(resultColor, hue, sat, value, alpha);
+  int factor = 2;
+  alpha = alpha / factor;
+  sat = sat * factor;
+  NS_HSV2RGB(resultColor, hue, sat, value, alpha);
+  return resultColor;
+}
+
 nsresult
 nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
 {
   EnsureInit();
 
   nsresult res = NS_OK;
 
   switch (aID) {
@@ -166,22 +193,22 @@ nsLookAndFeel::NativeGetColor(ColorID aI
       break;
     case eColorID_TextBackground:
       aColor = NS_RGB(0xff,0xff,0xff);
       break;
     case eColorID_TextForeground:
       aColor = NS_RGB(0x00,0x00,0x00);
       break;
     case eColorID_TextSelectBackground:
-      aColor = mColorTextSelectBackground;
+      aColor = ProcessSelectionBackground(mColorTextSelectBackground);
       break;
     // This is used to gray out the selection when it's not focused. Used with
     // nsISelectionController::SELECTION_DISABLED.
     case eColorID_TextSelectBackgroundDisabled:
-      aColor = mColorTextSelectBackgroundDisabled;
+      aColor = ProcessSelectionBackground(mColorTextSelectBackgroundDisabled);
       break;
     case eColorID_highlight: // CSS2 color
       aColor = mColorHighlight;
       break;
     case eColorID__moz_menuhover:
       aColor = mColorMenuHover;
       break;
     case eColorID_TextSelectForeground: