Bug 232227 - Do not expose system colors to CSS or canvas. r=jmuizelaar r=enndeakin tor-r=arthuredelstein
authorKathy Brade <brade@pearlcrescent.com>
Fri, 25 Sep 2015 00:59:00 +0200
changeset 264413 deb7126157d70e1ed0490791f96c42a8befc4fab
parent 264412 c6765de566a32511826143c2d5c83512e83f68ab
child 264414 bc13d18870417a37a6582cc837b283b24bdd73c6
push id65619
push usercbook@mozilla.com
push dateFri, 25 Sep 2015 13:38:23 +0000
treeherdermozilla-inbound@deb7126157d7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmuizelaar, enndeakin
bugs232227
milestone44.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 232227 - Do not expose system colors to CSS or canvas. r=jmuizelaar r=enndeakin tor-r=arthuredelstein
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/test/mochitest.ini
dom/canvas/test/test_bug232227.html
layout/style/nsRuleNode.cpp
widget/LookAndFeel.h
widget/nsXPLookAndFeel.cpp
widget/nsXPLookAndFeel.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -669,17 +669,19 @@ CanvasGradient::AddColorStop(float offse
   nsCSSValue value;
   nsCSSParser parser;
   if (!parser.ParseColorString(colorstr, nullptr, 0, value)) {
     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   nscolor color;
-  if (!nsRuleNode::ComputeColor(value, nullptr, nullptr, color)) {
+  nsCOMPtr<nsIPresShell> presShell = mContext ? mContext->GetPresShell() : nullptr;
+  if (!nsRuleNode::ComputeColor(value, presShell ? presShell->GetPresContext() : nullptr,
+                                nullptr, color)) {
     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   mStops = nullptr;
 
   GradientStop newStop;
 
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -208,16 +208,17 @@ skip-if = toolkit == 'cocoa'
 [test_2d.path.arc.shape.3.html]
 skip-if = toolkit != 'cocoa'
 [test_2d.path.rect.selfintersect.html]
 skip-if = toolkit != 'cocoa'
 # This test is bogus according to the spec; see bug 407107
 [test_2d.path.rect.zero.6.html]
 disabled = bug 407107
 [test_2d.strokeRect.zero.5.html]
+[test_bug232227.html]
 [test_bug613794.html]
 [test_bug753758.html]
 [test_bug764125.html]
 [test_bug856472.html]
 [test_bug866575.html]
 skip-if = (toolkit == 'gonk' && debug) #bug 1045153
 [test_bug902651.html]
 [test_canvas.html]
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_bug232227.html
@@ -0,0 +1,151 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=232227
+-->
+<head>
+  <title>Test for Bug 232227</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=232227">Mozilla Bug 232227</a>
+<!-- CSS system colors -->
+<!--<table cellspacing=0 border=1>
+<caption>CSS system colors</caption>
+<canvas id=colorTestCanvas width=1 height=1 style="display:none"></canvas> -->
+<script type="application/javascript">
+
+/** Test for Bug 232227 **/
+
+function beginTest() {
+	var colorNames = [
+		[ "ActiveBorder", 0xB4, 0xB4, 0xB4 ],
+		[ "ActiveCaption", 0x99, 0xB4, 0xD1 ],
+		[ "AppWorkspace", 0xAB, 0xAB, 0xAB ],
+		[ "Background", 0x00, 0x00, 0x00 ],
+		[ "ButtonFace", 0xF0, 0xF0, 0xF0 ],
+		[ "ButtonHighlight", 0xFF, 0xFF, 0xFF ],
+		[ "ButtonShadow", 0xA0, 0xA0, 0xA0 ],
+		[ "ButtonText", 0x00, 0x00, 0x00 ],
+		[ "CaptionText", 0x00, 0x00, 0x00 ],
+		[ "GrayText", 0x6D, 0x6D, 0x6D ],
+		[ "Highlight", 0x33, 0x99, 0xFF ],
+		[ "HighlightText", 0xFF, 0xFF, 0xFF ],
+		[ "InactiveBorder", 0xF4, 0xF7, 0xFC ],
+		[ "InactiveCaption", 0xBF, 0xCD, 0xDB ],
+		[ "InactiveCaptionText", 0x43, 0x4E, 0x54 ],
+		[ "InfoBackground", 0xFF, 0xFF, 0xE1 ],
+		[ "InfoText", 0x00, 0x00, 0x00 ],
+		[ "Menu", 0xF0, 0xF0, 0xF0 ],
+		[ "MenuText", 0x00, 0x00, 0x00 ],
+		[ "Scrollbar", 0xC8, 0xC8, 0xC8 ],
+		[ "ThreeDDarkShadow", 0x69, 0x69, 0x69 ],
+		[ "ThreeDFace", 0xF0, 0xF0, 0xF0 ],
+		[ "ThreeDHighlight", 0xFF, 0xFF, 0xFF ],
+		[ "ThreeDLightShadow", 0xE3, 0xE3, 0xE3 ],
+		[ "ThreeDShadow", 0xA0, 0xA0, 0xA0 ],
+		[ "Window", 0xFF, 0xFF, 0xFF ],
+		[ "WindowFrame", 0x64, 0x64, 0x64 ],
+		[ "WindowText", 0x00, 0x00, 0x00 ],
+		[ "-moz-ButtonDefault", 0x69, 0x69, 0x69 ],
+		[ "-moz-ButtonHoverFace", 0xF0, 0xF0, 0xF0 ],
+    [ "-moz-ButtonHoverText", 0x00, 0x00, 0x00 ],
+		[ "-moz-CellHighlight", 0xF0, 0xF0, 0xF0 ],
+		[ "-moz-CellHighlightText", 0x00, 0x00, 0x00 ],
+		[ "-moz-Combobox", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-ComboboxText", 0x00, 0x00, 0x00 ],
+		[ "-moz-Dialog", 0xF0, 0xF0, 0xF0 ],
+		[ "-moz-DialogText", 0x00, 0x00, 0x00 ],
+		[ "-moz-DragTargetZone", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-EvenTreeRow", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-Field", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-FieldText", 0x00, 0x00, 0x00 ],
+		[ "-moz-MenuHover", 0x33, 0x99, 0xFF ],
+		[ "-moz-MenuHoverText", 0x00, 0x00, 0x00 ],
+		[ "-moz-MenubarText", 0x00, 0x00, 0x00 ],
+    [ "-moz-MenubarHoverText", 0x00, 0x00, 0x00 ],
+		[ "-moz-NativeHyperlinkText", 0x00, 0x66, 0xCC ],
+		[ "-moz-OddTreeRow", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-html-CellHighlight", 0x33, 0x99, 0xFF ],
+		[ "-moz-html-CellHighlightText", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-mac-chrome-active", 0xB2, 0xB2, 0xB2 ],
+		[ "-moz-mac-chrome-inactive", 0xE1, 0xE1, 0xE1 ],
+		[ "-moz-mac-focusring", 0x60, 0x9D, 0xD7 ],
+		[ "-moz-mac-menuselect", 0x38, 0x75, 0xD7 ],
+		[ "-moz-mac-menushadow", 0xA3, 0xA3, 0xA3 ],
+		[ "-moz-mac-menutextdisable", 0x88, 0x88, 0x88 ],
+		[ "-moz-mac-menutextselect", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-mac-DisabledToolbarText", 0x3F, 0x3F, 0x3F ],
+		[ "-moz-mac-AlternatePrimaryHighlight", 0x3F, 0x3F, 0x3F ],
+		[ "-moz-mac-SecondaryHighlight", 0xD4, 0xD4, 0xD4 ],
+		[ "-moz-win-MediaText", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-win-CommunicationsText", 0xFF, 0xFF, 0xFF ],
+
+		// These five are configured via Tools -> Options -> Content -> Colors.
+		//"-moz-ActiveHyperlinkText",
+		//"-moz-HyperLinkText",
+		//"-moz-VisitedHyperlinkText",
+		//"-moz-default-background-color",
+		//"-moz-default-color",
+	];
+
+  var colorTestCanvas = document.createElement("canvas");
+  colorTestCanvas.width = colorTestCanvas.height = 1;
+  colorTestCanvas = colorTestCanvas.getContext("2d");
+
+	var colorTestDiv = document.createElement("div");
+	document.body.appendChild(colorTestDiv); // ie
+
+	for (var i in colorNames) {
+		(function(colorName, r, g, b) {
+      // test value
+      var ctest = "rgb(" + r + ", " + g + ", " + b + ")";
+
+      // computed value
+      colorTestDiv.style.backgroundColor = "#FF00FE";
+      try {
+        colorTestDiv.style.backgroundColor = colorName;
+      } catch (ex) {} // ie
+      var c1;
+      if (window.getComputedStyle) {
+        c1 = getComputedStyle(colorTestDiv, null).backgroundColor;
+      } else { // ie
+        var range = document.body.createTextRange();
+        range.moveToElementText(colorTestDiv);
+        c1 = range.queryCommandValue("BackColor");
+        c1 = "rgb(" + (c1 & 0xFF) + ", " + ((c1 >> 8) & 0xFF) + ", " + ((c1 >> 16) & 0xFF) + ")";
+      }
+      if (c1 != "rgb(255, 0, 254)") {
+        is(c1, ctest, "Stand-in computed color: " + colorName + " is correct.");
+      }
+
+      // canvas
+      if (colorTestCanvas) {
+        colorTestCanvas.fillStyle = colorName;
+        colorTestCanvas.fillRect(0, 0, 1, 1);
+        var c2 = colorTestCanvas.getImageData(0, 0, 1, 1).data;
+        c2 = "rgb(" + c2[0] + ", " + c2[1] + ", " + c2[2] + ")";
+
+        // combine
+        if (c2 != c1 && c2 != "rgb(0, 0, 0)") {
+          is(c2, ctest, "Stand-in canvas color: " + colorName + " is correct.");
+        }
+      }
+    })(colorNames[i][0], colorNames[i][1], colorNames[i][2], colorNames[i][3]);
+  }
+  SimpleTest.finish();
+}
+
+var prefs = [
+  [ "ui.use_standins_for_native_colors", true ],
+  [ "ui.use_native_colors", true ],
+];
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+
+</script>
+<!-- </table> -->
+</body>
+</html>
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -953,17 +953,20 @@ static bool SetColor(const nsCSSValue& a
       aResult = rgba;
       result = true;
     }
   }
   else if (eCSSUnit_EnumColor == unit) {
     int32_t intValue = aValue.GetIntValue();
     if (0 <= intValue) {
       LookAndFeel::ColorID colorID = (LookAndFeel::ColorID) intValue;
-      if (NS_SUCCEEDED(LookAndFeel::GetColor(colorID, &aResult))) {
+      bool useStandinsForNativeColors = aPresContext &&
+                                        !aPresContext->IsChrome();
+      if (NS_SUCCEEDED(LookAndFeel::GetColor(colorID,
+                                    useStandinsForNativeColors, &aResult))) {
         result = true;
       }
     }
     else {
       aResult = NS_RGB(0, 0, 0);
       result = false;
       switch (intValue) {
         case NS_COLOR_MOZ_HYPERLINKTEXT:
--- a/widget/LookAndFeel.h
+++ b/widget/LookAndFeel.h
@@ -521,16 +521,25 @@ public:
    *   eColorID_TextSelectForeground might return NS_DONT_CHANGE_COLOR.
    *   eColorID_IME* might return NS_TRANSPARENT, NS_SAME_AS_FOREGROUND_COLOR or
    *   NS_40PERCENT_FOREGROUND_COLOR.
    *   These values have particular meaning.  Then, they are not an actual
    *   color value.
    */
   static nsresult GetColor(ColorID aID, nscolor* aResult);
 
+   /**
+   * This variant of GetColor() takes an extra Boolean parameter that allows
+   * the caller to ask that hard-coded color values be substituted for
+   * native colors (used when it is desireable to hide system colors to
+   * avoid system fingerprinting).
+   */
+  static nsresult GetColor(ColorID aID, bool aUseStandinsForNativeColors,
+                           nscolor* aResult);
+
   /**
    * GetInt() and GetFloat() return a int or float value for aID.  The result
    * might be distance, time, some flags or a int value which has particular
    * meaning.  See each document at definition of each ID for the detail.
    * The result is always 0 when they return error.  Therefore, if you want to
    * use a value for the default value, you should use the other method which
    * returns int or float directly.
    */
--- a/widget/nsXPLookAndFeel.cpp
+++ b/widget/nsXPLookAndFeel.cpp
@@ -241,16 +241,17 @@ const char nsXPLookAndFeel::sColorPrefs[
   "ui.-moz-combobox"
 };
 
 int32_t nsXPLookAndFeel::sCachedColors[LookAndFeel::eColorID_LAST_COLOR] = {0};
 int32_t nsXPLookAndFeel::sCachedColorBits[COLOR_CACHE_SIZE] = {0};
 
 bool nsXPLookAndFeel::sInitialized = false;
 bool nsXPLookAndFeel::sUseNativeColors = true;
+bool nsXPLookAndFeel::sUseStandinsForNativeColors = false;
 
 nsLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
 bool nsXPLookAndFeel::sShutdown = false;
 
 // static
 nsLookAndFeel*
 nsXPLookAndFeel::GetInstance()
 {
@@ -452,20 +453,22 @@ nsXPLookAndFeel::Init()
   for (i = 0; i < ArrayLength(sFloatPrefs); ++i) {
     InitFromPref(&sFloatPrefs[i]);
   }
 
   for (i = 0; i < ArrayLength(sColorPrefs); ++i) {
     InitColorFromPref(i);
   }
 
-  bool val;
-  if (NS_SUCCEEDED(Preferences::GetBool("ui.use_native_colors", &val))) {
-    sUseNativeColors = val;
-  }
+  Preferences::AddBoolVarCache(&sUseNativeColors,
+                               "ui.use_native_colors",
+                               sUseNativeColors);
+  Preferences::AddBoolVarCache(&sUseStandinsForNativeColors,
+                               "ui.use_standins_for_native_colors",
+                               sUseStandinsForNativeColors);
 
   if (XRE_IsContentProcess()) {
     mozilla::dom::ContentChild* cc =
       mozilla::dom::ContentChild::GetSingleton();
 
     nsTArray<LookAndFeelInt> lookAndFeelIntCache;
     cc->SendGetLookAndFeelCache(&lookAndFeelIntCache);
     LookAndFeel::SetIntCache(lookAndFeelIntCache);
@@ -504,24 +507,172 @@ nsXPLookAndFeel::IsSpecialColor(ColorID 
        * In GetColor(), every color that is not a special color is color
        * corrected. Use false to make other colors color corrected.
        */
       return false;
   }
   return false;
 }
 
+bool
+nsXPLookAndFeel::ColorIsNotCSSAccessible(ColorID aID)
+{
+  bool result = false;
+
+  switch (aID) {
+    case eColorID_WindowBackground:
+    case eColorID_WindowForeground:
+    case eColorID_WidgetBackground:
+    case eColorID_WidgetForeground:
+    case eColorID_WidgetSelectBackground:
+    case eColorID_WidgetSelectForeground:
+    case eColorID_Widget3DHighlight:
+    case eColorID_Widget3DShadow:
+    case eColorID_TextBackground:
+    case eColorID_TextForeground:
+    case eColorID_TextSelectBackground:
+    case eColorID_TextSelectForeground:
+    case eColorID_TextSelectBackgroundDisabled:
+    case eColorID_TextSelectBackgroundAttention:
+    case eColorID_TextHighlightBackground:
+    case eColorID_TextHighlightForeground:
+    case eColorID_IMERawInputBackground:
+    case eColorID_IMERawInputForeground:
+    case eColorID_IMERawInputUnderline:
+    case eColorID_IMESelectedRawTextBackground:
+    case eColorID_IMESelectedRawTextForeground:
+    case eColorID_IMESelectedRawTextUnderline:
+    case eColorID_IMEConvertedTextBackground:
+    case eColorID_IMEConvertedTextForeground:
+    case eColorID_IMEConvertedTextUnderline:
+    case eColorID_IMESelectedConvertedTextBackground:
+    case eColorID_IMESelectedConvertedTextForeground:
+    case eColorID_IMESelectedConvertedTextUnderline:
+    case eColorID_SpellCheckerUnderline:
+      result = true;
+      break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
+nscolor
+nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID)
+{
+  nscolor result = NS_RGB(0xFF, 0xFF, 0xFF);
+
+  // The stand-in colors are taken from the Windows 7 Aero theme
+  // except Mac-specific colors which are taken from Mac OS 10.7.
+  switch (aID) {
+    // CSS 2 colors:
+    case eColorID_activeborder:      result = NS_RGB(0xB4, 0xB4, 0xB4); break;
+    case eColorID_activecaption:     result = NS_RGB(0x99, 0xB4, 0xD1); break;
+    case eColorID_appworkspace:      result = NS_RGB(0xAB, 0xAB, 0xAB); break;
+    case eColorID_background:        result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID_buttonface:        result = NS_RGB(0xF0, 0xF0, 0xF0); break;
+    case eColorID_buttonhighlight:   result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID_buttonshadow:      result = NS_RGB(0xA0, 0xA0, 0xA0); break;
+    case eColorID_buttontext:        result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID_captiontext:       result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID_graytext:          result = NS_RGB(0x6D, 0x6D, 0x6D); break;
+    case eColorID_highlight:         result = NS_RGB(0x33, 0x99, 0xFF); break;
+    case eColorID_highlighttext:     result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID_inactiveborder:    result = NS_RGB(0xF4, 0xF7, 0xFC); break;
+    case eColorID_inactivecaption:   result = NS_RGB(0xBF, 0xCD, 0xDB); break;
+    case eColorID_inactivecaptiontext:
+      result = NS_RGB(0x43, 0x4E, 0x54); break;
+    case eColorID_infobackground:    result = NS_RGB(0xFF, 0xFF, 0xE1); break;
+    case eColorID_infotext:          result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID_menu:              result = NS_RGB(0xF0, 0xF0, 0xF0); break;
+    case eColorID_menutext:          result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID_scrollbar:         result = NS_RGB(0xC8, 0xC8, 0xC8); break;
+    case eColorID_threeddarkshadow:  result = NS_RGB(0x69, 0x69, 0x69); break;
+    case eColorID_threedface:        result = NS_RGB(0xF0, 0xF0, 0xF0); break;
+    case eColorID_threedhighlight:   result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID_threedlightshadow: result = NS_RGB(0xE3, 0xE3, 0xE3); break;
+    case eColorID_threedshadow:      result = NS_RGB(0xA0, 0xA0, 0xA0); break;
+    case eColorID_window:            result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID_windowframe:       result = NS_RGB(0x64, 0x64, 0x64); break;
+    case eColorID_windowtext:        result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_buttondefault:
+      result = NS_RGB(0x69, 0x69, 0x69); break;
+    case eColorID__moz_field:        result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_fieldtext:    result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_dialog:       result = NS_RGB(0xF0, 0xF0, 0xF0); break;
+    case eColorID__moz_dialogtext:   result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_dragtargetzone:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_cellhighlight:
+      result = NS_RGB(0xF0, 0xF0, 0xF0); break;
+    case eColorID__moz_cellhighlighttext:
+      result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_html_cellhighlight:
+      result = NS_RGB(0x33, 0x99, 0xFF); break;
+    case eColorID__moz_html_cellhighlighttext:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_buttonhoverface:
+      result = NS_RGB(0xF0, 0xF0, 0xF0); break;
+    case eColorID__moz_buttonhovertext:
+      result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_menuhover:
+      result = NS_RGB(0x33, 0x99, 0xFF); break;
+    case eColorID__moz_menuhovertext:
+      result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_menubartext:
+      result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_menubarhovertext:
+      result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_oddtreerow:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_mac_chrome_active:
+      result = NS_RGB(0xB2, 0xB2, 0xB2); break;
+    case eColorID__moz_mac_chrome_inactive:
+      result = NS_RGB(0xE1, 0xE1, 0xE1); break;
+    case eColorID__moz_mac_focusring:
+      result = NS_RGB(0x60, 0x9D, 0xD7); break;
+    case eColorID__moz_mac_menuselect:
+      result = NS_RGB(0x38, 0x75, 0xD7); break;
+    case eColorID__moz_mac_menushadow:
+      result = NS_RGB(0xA3, 0xA3, 0xA3); break;
+    case eColorID__moz_mac_menutextdisable:
+      result = NS_RGB(0x88, 0x88, 0x88); break;
+    case eColorID__moz_mac_menutextselect:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_mac_disabledtoolbartext:
+      result = NS_RGB(0x3F, 0x3F, 0x3F); break;
+    case eColorID__moz_mac_secondaryhighlight:
+      result = NS_RGB(0xD4, 0xD4, 0xD4); break;
+    case eColorID__moz_win_mediatext:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_win_communicationstext:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    case eColorID__moz_nativehyperlinktext:
+      result = NS_RGB(0x00, 0x66, 0xCC); break;
+    case eColorID__moz_comboboxtext:
+      result = NS_RGB(0x00, 0x00, 0x00); break;
+    case eColorID__moz_combobox:
+      result = NS_RGB(0xFF, 0xFF, 0xFF); break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
 //
 // All these routines will return NS_OK if they have a value,
 // in which case the nsLookAndFeel should use that value;
 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
 // platform-specific nsLookAndFeel should use its own values instead.
 //
 nsresult
-nsXPLookAndFeel::GetColorImpl(ColorID aID, nscolor &aResult)
+nsXPLookAndFeel::GetColorImpl(ColorID aID, bool aUseStandinsForNativeColors,
+                              nscolor &aResult)
 {
   if (!sInitialized)
     Init();
 
   // define DEBUG_SYSTEM_COLOR_USE if you want to debug system color
   // use in a skin that uses them.  When set, it will make all system
   // color pairs that are appropriate for foreground/background
   // pairing the same.  This means if the skin is using system colors
@@ -597,17 +748,22 @@ nsXPLookAndFeel::GetColorImpl(ColorID aI
       default:
         rv = NS_ERROR_NOT_AVAILABLE;
     }
     if (NS_SUCCEEDED(rv))
       return rv;
   }
 #endif // DEBUG_SYSTEM_COLOR_USE
 
-  if (IS_COLOR_CACHED(aID)) {
+  if (aUseStandinsForNativeColors &&
+      (ColorIsNotCSSAccessible(aID) || !sUseStandinsForNativeColors)) {
+    aUseStandinsForNativeColors = false;
+  }
+
+  if (!aUseStandinsForNativeColors && IS_COLOR_CACHED(aID)) {
     aResult = sCachedColors[aID];
     return NS_OK;
   }
 
   // There are no system color settings for these, so set them manually
   if (aID == eColorID_TextSelectBackgroundDisabled) {
     // This is used to gray out the selection when it's not focused
     // Used with nsISelectionController::SELECTION_DISABLED
@@ -631,16 +787,21 @@ nsXPLookAndFeel::GetColorImpl(ColorID aI
 
   if (aID == eColorID_TextHighlightForeground) {
     // The foreground color for the matched text in findbar highlighting
     // Used with nsISelectionController::SELECTION_FIND
     aResult = NS_RGB(0xff, 0xff, 0xff);
     return NS_OK;
   }
 
+  if (sUseNativeColors && aUseStandinsForNativeColors) {
+    aResult = GetStandinForNativeColor(aID);
+    return NS_OK;
+  }
+
   if (sUseNativeColors && NS_SUCCEEDED(NativeGetColor(aID, aResult))) {
     if ((gfxPlatform::GetCMSMode() == eCMSMode_All) &&
          !IsSpecialColor(aID, aResult)) {
       qcms_transform *transform = gfxPlatform::GetCMSInverseRGBTransform();
       if (transform) {
         uint8_t color[3];
         color[0] = NS_GET_R(aResult);
         color[1] = NS_GET_G(aResult);
@@ -727,17 +888,25 @@ nsXPLookAndFeel::GetIntCacheImpl()
 }
 
 namespace mozilla {
 
 // static
 nsresult
 LookAndFeel::GetColor(ColorID aID, nscolor* aResult)
 {
-  return nsLookAndFeel::GetInstance()->GetColorImpl(aID, *aResult);
+  return nsLookAndFeel::GetInstance()->GetColorImpl(aID, false, *aResult);
+}
+
+nsresult
+LookAndFeel::GetColor(ColorID aID, bool aUseStandinsForNativeColors,
+                      nscolor* aResult)
+{
+  return nsLookAndFeel::GetInstance()->GetColorImpl(aID,
+                                       aUseStandinsForNativeColors, *aResult);
 }
 
 // static
 nsresult
 LookAndFeel::GetInt(IntID aID, int32_t* aResult)
 {
   return nsLookAndFeel::GetInstance()->GetIntImpl(aID, *aResult);
 }
--- a/widget/nsXPLookAndFeel.h
+++ b/widget/nsXPLookAndFeel.h
@@ -48,17 +48,18 @@ public:
   void Init();
 
   //
   // All these routines will return NS_OK if they have a value,
   // in which case the nsLookAndFeel should use that value;
   // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
   // platform-specific nsLookAndFeel should use its own values instead.
   //
-  nsresult GetColorImpl(ColorID aID, nscolor &aResult);
+  nsresult GetColorImpl(ColorID aID, bool aUseStandinsForNativeColors,
+                        nscolor &aResult);
   virtual nsresult GetIntImpl(IntID aID, int32_t &aResult);
   virtual nsresult GetFloatImpl(FloatID aID, float &aResult);
 
   // This one is different: there are no override prefs (fixme?), so
   // there is no XP implementation, only per-system impls.
   virtual bool GetFontImpl(FontID aID, nsString& aName,
                            gfxFontStyle& aStyle,
                            float aDevPixPerCSSPixel) = 0;
@@ -89,27 +90,30 @@ protected:
   static void IntPrefChanged(nsLookAndFeelIntPref *data);
   static void FloatPrefChanged(nsLookAndFeelFloatPref *data);
   static void ColorPrefChanged(unsigned int index, const char *prefName);
   void InitFromPref(nsLookAndFeelIntPref* aPref);
   void InitFromPref(nsLookAndFeelFloatPref* aPref);
   void InitColorFromPref(int32_t aIndex);
   virtual nsresult NativeGetColor(ColorID aID, nscolor &aResult) = 0;
   bool IsSpecialColor(ColorID aID, nscolor &aColor);
+  bool ColorIsNotCSSAccessible(ColorID aID);
+  nscolor GetStandinForNativeColor(ColorID aID);
 
   static void OnPrefChanged(const char* aPref, void* aClosure);
 
   static bool sInitialized;
   static nsLookAndFeelIntPref sIntPrefs[];
   static nsLookAndFeelFloatPref sFloatPrefs[];
   /* this length must not be shorter than the length of the longest string in the array
    * see nsXPLookAndFeel.cpp
    */
   static const char sColorPrefs[][38];
   static int32_t sCachedColors[LookAndFeel::eColorID_LAST_COLOR];
   static int32_t sCachedColorBits[COLOR_CACHE_SIZE];
   static bool sUseNativeColors;
+  static bool sUseStandinsForNativeColors;
 
   static nsLookAndFeel* sInstance;
   static bool sShutdown;
 };
 
 #endif