Bug 940393 - Win8: Text color on inactive tabs makes text very hard to read. r=gijs
authorJared Wein <jwein@mozilla.com>
Thu, 20 Mar 2014 20:23:36 -0400
changeset 174816 9863ccc8757357f6db700aa1ba61d29780d05caa
parent 174815 aa2073a4e646f55626a3e80c3d7d03959c4ce259
child 174817 6d0a341040a9b1bfb3834c196cd23c475f21986c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersgijs
bugs940393
milestone31.0a1
Bug 940393 - Win8: Text color on inactive tabs makes text very hard to read. r=gijs
browser/base/content/browser.js
browser/components/migration/src/IEProfileMigrator.js
browser/modules/Windows8WindowFrameColor.jsm
browser/modules/moz.build
browser/themes/windows/browser-aero.css
browser/themes/windows/browser.css
toolkit/modules/WindowsRegistry.jsm
toolkit/modules/moz.build
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1181,16 +1181,43 @@ var gBrowserInit = {
       Cu.reportError("Could not end startup crash tracking: " + ex);
     }
 
     if (typeof WindowsPrefSync !== 'undefined') {
       // Pulls in Metro controlled prefs and pushes out Desktop controlled prefs
       WindowsPrefSync.init();
     }
 
+#ifdef XP_WIN
+    if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
+        window.matchMedia("(-moz-windows-default-theme)").matches) {
+      let windows8WindowFrameColor = Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {}).Windows8WindowFrameColor;
+      let windowFrameColor = windows8WindowFrameColor.get();
+
+      // Formula from W3C Techniques For Accessibility Evaluation And
+      // Repair Tools, Section 2.2 http://www.w3.org/TR/AERT#color
+      let brightnessThreshold = 125;
+      let colorThreshold = 500;
+      let bY = windowFrameColor[0] * .299 +
+               windowFrameColor[1] * .587 +
+               windowFrameColor[2] * .114;
+      let fY = 0; // Default to black for foreground text.
+      let brightnessDifference = Math.abs(bY - fY);
+      // Color difference calculation is simplified because black is 0 for R,G,B.
+      let colorDifference = windowFrameColor[0] + windowFrameColor[1] + windowFrameColor[2];
+
+      // Brightness is defined within {0, 255}. Set an attribute
+      // if the window frame color doesn't reach these thresholds
+      // so the theme can be adjusted for readability.
+      if (brightnessDifference < brightnessThreshold && colorDifference < colorThreshold) {
+        document.documentElement.setAttribute("darkwindowframe", "true");
+      }
+    }
+#endif
+
     SessionStore.promiseInitialized.then(() => {
       // Bail out if the window has been closed in the meantime.
       if (window.closed) {
         return;
       }
 
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
--- a/browser/components/migration/src/IEProfileMigrator.js
+++ b/browser/components/migration/src/IEProfileMigrator.js
@@ -5,27 +5,28 @@
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
-const kRegMultiSz = 7;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
                                   "resource://gre/modules/ctypes.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
+                                  "resource://gre/modules/WindowsRegistry.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Helpers.
 
 let CtypesHelpers = {
   _structs: {},
   _functions: {},
   _libs: {},
@@ -121,55 +122,16 @@ function hostIsIPAddress(aHost) {
   try {
     Services.eTLD.getBaseDomainFromHost(aHost);
   } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
     return true;
   } catch (e) {}
   return false;
 }
 
-/**
- * Safely reads a value from the registry.
- *
- * @param aRoot
- *        The root registry to use.
- * @param aPath
- *        The registry path to the key.
- * @param aKey
- *        The key name.
- * @return The key value or undefined if it doesn't exist.  If the key is
- *         a REG_MULTI_SZ, an array is returned.
- */
-function readRegKey(aRoot, aPath, aKey) {
-  let registry = Cc["@mozilla.org/windows-registry-key;1"].
-                 createInstance(Ci.nsIWindowsRegKey);
-  try {
-    registry.open(aRoot, aPath, Ci.nsIWindowsRegKey.ACCESS_READ);
-    if (registry.hasValue(aKey)) {
-      let type = registry.getValueType(aKey);
-      switch (type) {
-        case kRegMultiSz:
-          // nsIWindowsRegKey doesn't support REG_MULTI_SZ type out of the box.
-          let str = registry.readStringValue(aKey);
-          return [v for each (v in str.split("\0")) if (v)];
-        case Ci.nsIWindowsRegKey.TYPE_STRING:
-          return registry.readStringValue(aKey);
-        case Ci.nsIWindowsRegKey.TYPE_INT:
-          return registry.readIntValue(aKey);
-        default:
-          throw new Error("Unsupported registry value.");
-      }
-    }
-  } catch (ex) {
-  } finally {
-    registry.close();
-  }
-  return undefined;
-};
-
 ////////////////////////////////////////////////////////////////////////////////
 //// Resources
 
 function Bookmarks() {
 }
 
 Bookmarks.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
@@ -187,19 +149,19 @@ Bookmarks.prototype = {
   },
 
   __toolbarFolderName: null,
   get _toolbarFolderName() {
     if (!this.__toolbarFolderName) {
       // Retrieve the name of IE's favorites subfolder that holds the bookmarks
       // in the toolbar. This was previously stored in the registry and changed
       // in IE7 to always be called "Links".
-      let folderName = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                                  "Software\\Microsoft\\Internet Explorer\\Toolbar",
-                                  "LinksFolderName");
+      let folderName = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                                  "Software\\Microsoft\\Internet Explorer\\Toolbar",
+                                                  "LinksFolderName");
       this.__toolbarFolderName = folderName || "Links";
     }
     return this.__toolbarFolderName;
   },
 
   migrate: function B_migrate(aCallback) {
     PlacesUtils.bookmarks.runInBatchMode({
       runBatched: (function migrateBatched() {
@@ -600,18 +562,18 @@ Settings.prototype = {
    * @param aKey
    *        Name of the key.
    * @param aPref
    *        Firefox preference.
    * @param [optional] aTransformFn
    *        Conversion function from the Registry format to the pref format.
    */
   _set: function S__set(aPath, aKey, aPref, aTransformFn) {
-    let value = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                           aPath, aKey);
+    let value = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                           aPath, aKey);
     // Don't import settings that have never been flipped.
     if (value === undefined)
       return;
 
     if (aTransformFn)
       value = aTransformFn(value);
 
     switch (typeof(value)) {
@@ -646,31 +608,31 @@ IEProfileMigrator.prototype.getResources
   , new Cookies()
   , new Settings()
   ];
   return [r for each (r in resources) if (r.exists)];
 };
 
 Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
   get: function IE_get_sourceHomePageURL() {
-    let defaultStartPage = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
-                                      kMainKey, "Default_Page_URL");
-    let startPage = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                               kMainKey, "Start Page");
+    let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+                                                      kMainKey, "Default_Page_URL");
+    let startPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                               kMainKey, "Start Page");
     // If the user didn't customize the Start Page, he is still on the default
     // page, that may be considered the equivalent of our about:home.  There's
     // no reason to retain it, since it is heavily targeted to IE.
     let homepage = startPage != defaultStartPage ? startPage : "";
 
     // IE7+ supports secondary home pages located in a REG_MULTI_SZ key.  These
     // are in addition to the Start Page, and no empty entries are possible,
     // thus a Start Page is always defined if any of these exists, though it
     // may be the default one.
-    let secondaryPages = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                                    kMainKey, "Secondary Start Pages");
+    let secondaryPages = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                                    kMainKey, "Secondary Start Pages");
     if (secondaryPages) {
       if (homepage)
         secondaryPages.unshift(homepage);
       homepage = secondaryPages.join("|");
     }
 
     return homepage;
   }
new file mode 100644
--- /dev/null
+++ b/browser/modules/Windows8WindowFrameColor.jsm
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+this.EXPORTED_SYMBOLS = ["Windows8WindowFrameColor"];
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/WindowsRegistry.jsm");
+
+const Windows8WindowFrameColor = {
+  _windowFrameColor: null,
+
+  get: function() {
+    if (this._windowFrameColor)
+      return this._windowFrameColor;
+
+    let windowFrameColor = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                                      "Software\\Microsoft\\Windows\\DWM",
+                                                      "ColorizationColor");
+    // The color returned from the Registry is in decimal form.
+    let windowFrameColorHex = windowFrameColor.toString(16);
+    // Zero-pad the number just to make sure that it is 8 digits.
+    windowFrameColorHex = ("00000000" + windowFrameColorHex).substr(-8);
+    let windowFrameColorArray = windowFrameColorHex.match(/../g);
+    let [pixelA, pixelR, pixelG, pixelB] = windowFrameColorArray.map(function(val) parseInt(val, 16));
+
+    return this._windowFrameColor = [pixelR, pixelG, pixelB];
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -21,16 +21,17 @@ EXTRA_JS_MODULES += [
     'Social.jsm',
     'TabCrashReporter.jsm',
     'WebappManager.jsm',
     'webrtcUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
+        'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
     ]
 
 EXTRA_PP_JS_MODULES += [
     'AboutHome.jsm',
     'RecentWindow.jsm',
     'UITour.jsm',
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -131,16 +131,42 @@
   #toolbar-menubar:not(:-moz-lwtheme),
   #TabsToolbar:not(:-moz-lwtheme) {
     background-color: transparent !important;
     color: black;
     border-left-style: none !important;
     border-right-style: none !important;
   }
 
+  #main-window[darkwindowframe="true"] #toolbar-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive),
+  #main-window[darkwindowframe="true"] #TabsToolbar:not(:-moz-lwtheme):not(:-moz-window-inactive) {
+    color: white;
+  }
+
+  #main-window[darkwindowframe="true"] :-moz-any(#toolbar-menubar, #TabsToolbar) :-moz-any(@primaryToolbarButtons@):not(:-moz-lwtheme):not(:-moz-window-inactive),
+  #main-window[darkwindowframe="true"] :-moz-any(#toolbar-menubar, #TabsToolbar) #bookmarks-menu-button:not(:-moz-lwtheme) > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:not(:-moz-window-inactive) {
+    list-style-image: url(chrome://browser/skin/Toolbar-inverted.png);
+  }
+
+  #main-window[darkwindowframe="true"] .tabs-newtab-button:not(:-moz-lwtheme):not(:-moz-window-inactive),
+  #main-window[darkwindowframe="true"] :-moz-any(#toolbar-menubar, #TabsToolbar) > #new-tab-button:not(:-moz-lwtheme):not(:-moz-window-inactive),
+  #main-window[darkwindowframe="true"] :-moz-any(#toolbar-menubar, #TabsToolbar) > toolbarpaletteitem > #new-tab-button:not(:-moz-lwtheme):not(:-moz-window-inactive) {
+    list-style-image: url(chrome://browser/skin/tabbrowser/newtab-inverted.png);
+  }
+
+  #main-window[darkwindowframe="true"] .tab-close-button:not(:-moz-any(:hover, [selected="true"], :-moz-lwtheme, :-moz-window-inactive)) {
+    -moz-image-region: rect(0, 64px, 16px, 48px);
+  }
+
+  #main-window[darkwindowframe="true"] :-moz-any(#toolbar-menubar, #TabsToolbar) > #sync-button[status="active"]:not(:-moz-lwtheme),
+  #main-window[darkwindowframe="true"] :-moz-any(#toolbar-menubar, #TabsToolbar) > toolbarpaletteitem > #sync-button[status="active"]:not(:-moz-lwtheme) {
+    list-style-image: url("chrome://browser/skin/syncProgress-toolbar-inverted.png");
+  }
+
+
   #toolbar-menubar:not(:-moz-lwtheme) {
     text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
   }
 
   /* Vertical toolbar border */
   #main-window[sizemode=normal] #navigator-toolbox::after,
   #main-window[sizemode=normal] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
     border-left: 1px solid @toolbarShadowColor@;
@@ -190,16 +216,17 @@
   }
 
   #appcontent:not(:-moz-lwtheme) {
     background-color: -moz-dialog;
   }
 
   #main-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive) {
     background-color: rgba(255,255,255,.5);
+    color: black;
   }
 
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7) {
     #main-window[sizemode=normal] #nav-bar {
       border-top-left-radius: 2.5px;
       border-top-right-radius: 2.5px;
     }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -104,18 +104,18 @@
     -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
     visibility: visible;
   }
 %ifdef WINDOWS_AERO
 }
 %endif
 
 /* Make the menu inherit the toolbar's color. On non-compositor (Aero Basic, XP modern, classic)
- * this is defined above. Otherwise (Aero Glass, Windows 8), this is hardcoded to black in
- * browser-aero.css. */
+ * this is defined above. Otherwise (Aero Glass, Windows 8 inactive windows), this is hardcoded
+ * to black in browser-aero.css. */
 #main-menubar > menu:not(:-moz-lwtheme) {
   color: inherit;
 }
 
 /**
  * In the classic themes, the titlebar has a horizontal gradient, which is
  * problematic for reading the text of background tabs when they're in the
  * titlebar. We side-step this issue by layering our own background underneath
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/WindowsRegistry.jsm
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+this.EXPORTED_SYMBOLS = ["WindowsRegistry"];
+
+const WindowsRegistry = {
+  /**
+   * Safely reads a value from the registry.
+   *
+   * @param aRoot
+   *        The root registry to use.
+   * @param aPath
+   *        The registry path to the key.
+   * @param aKey
+   *        The key name.
+   * @return The key value or undefined if it doesn't exist.  If the key is
+   *         a REG_MULTI_SZ, an array is returned.
+   */
+  readRegKey: function(aRoot, aPath, aKey) {
+    const kRegMultiSz = 7;
+    let registry = Cc["@mozilla.org/windows-registry-key;1"].
+                   createInstance(Ci.nsIWindowsRegKey);
+    try {
+      registry.open(aRoot, aPath, Ci.nsIWindowsRegKey.ACCESS_READ);
+      if (registry.hasValue(aKey)) {
+        let type = registry.getValueType(aKey);
+        switch (type) {
+          case kRegMultiSz:
+            // nsIWindowsRegKey doesn't support REG_MULTI_SZ type out of the box.
+            let str = registry.readStringValue(aKey);
+            return [v for each (v in str.split("\0")) if (v)];
+          case Ci.nsIWindowsRegKey.TYPE_STRING:
+            return registry.readStringValue(aKey);
+          case Ci.nsIWindowsRegKey.TYPE_INT:
+            return registry.readIntValue(aKey);
+          default:
+            throw new Error("Unsupported registry value.");
+        }
+      }
+    } catch (ex) {
+    } finally {
+      registry.close();
+    }
+    return undefined;
+  },
+};
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -65,13 +65,19 @@ EXTRA_PP_JS_MODULES += [
     'WindowsPrefSync.jsm',
 ]
 
 if 'Android' != CONFIG['OS_TARGET']:
     EXTRA_PP_JS_MODULES += [
         'LightweightThemeConsumer.jsm',
     ]
 
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    EXTRA_JS_MODULES += [
+        'WindowsRegistry.jsm',
+    ]
+
 DEFINES['MOZ_APP_NAME'] = CONFIG['MOZ_APP_NAME']
 DEFINES['MOZ_BUILD_APP'] = CONFIG['MOZ_BUILD_APP']
 
 if CONFIG['MOZ_TOOLKIT_SEARCH']:
     DEFINES['MOZ_TOOLKIT_SEARCH'] = True