Rewrite handling of CSS system fonts to fix bugs in cascading and serialization. b=377947 r+sr=bzbarsky
authordbaron@dbaron.org
Tue, 12 Jun 2007 11:28:56 -0700
changeset 2302 7e89f709c6ff9878288d9b66e18c75250304f926
parent 2301 427bda154a0a003554a62c825fdd825c2b933943
child 2303 89ae8d9892bad8a91de78a3245847b5b57dd0cd4
push idunknown
push userunknown
push dateunknown
bugs377947
milestone1.9a6pre
Rewrite handling of CSS system fonts to fix bugs in cascading and serialization. b=377947 r+sr=bzbarsky
layout/style/nsCSSDeclaration.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSStruct.h
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsRuleNode.cpp
layout/style/test/ListCSSProperties.cpp
layout/style/test/Makefile.in
layout/style/test/test_bug372770.html
layout/style/test/test_bug377947.html
layout/style/test/test_value_storage.html
--- a/layout/style/nsCSSDeclaration.cpp
+++ b/layout/style/nsCSSDeclaration.cpp
@@ -467,16 +467,17 @@ nsCSSDeclaration::AppendCSSValueToString
 
   switch (unit) {
     case eCSSUnit_Null:         break;
     case eCSSUnit_Auto:         aResult.AppendLiteral("auto");     break;
     case eCSSUnit_Inherit:      aResult.AppendLiteral("inherit");  break;
     case eCSSUnit_Initial:      aResult.AppendLiteral("-moz-initial"); break;
     case eCSSUnit_None:         aResult.AppendLiteral("none");     break;
     case eCSSUnit_Normal:       aResult.AppendLiteral("normal");   break;
+    case eCSSUnit_System_Font:  aResult.AppendLiteral("-moz-use-system-font"); break;
 
     case eCSSUnit_String:       break;
     case eCSSUnit_URL:          break;
     case eCSSUnit_Image:        break;
     case eCSSUnit_Array:        break;
     case eCSSUnit_Attr:
     case eCSSUnit_Counter:
     case eCSSUnit_Counters:     aResult.Append(PRUnichar(')'));    break;
@@ -528,17 +529,28 @@ nsCSSDeclaration::GetValue(nsCSSProperty
 
   // simple properties are easy.
   if (!nsCSSProps::IsShorthand(aProperty)) {
     AppendValueToString(aProperty, aValue);
     return NS_OK;
   }
 
   // shorthands
+  CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
+    if (!mData->StorageFor(*p) &&
+        (!mImportantData || !mImportantData->StorageFor(*p)))
+      // We don't have all the properties in the shorthand.
+      if (*p != eCSSProperty__x_system_font)
+        return NS_OK;
+  }
+
+
   // XXX What about checking the consistency of '!important'?
+  // XXX What about checking that we don't serialize inherit,
+  // -moz-initial, or other illegal values?
   // XXXldb Can we share shorthand logic with ToString?
   switch (aProperty) {
     case eCSSProperty_margin: 
     case eCSSProperty_padding: 
     case eCSSProperty_border_color: 
     case eCSSProperty_border_style: 
     case eCSSProperty__moz_border_radius: 
     case eCSSProperty__moz_outline_radius: 
@@ -630,33 +642,48 @@ nsCSSDeclaration::GetValue(nsCSSProperty
       if (AppendValueToString(eCSSProperty_cue_before, aValue)) {
         aValue.Append(PRUnichar(' '));
         if (!AppendValueToString(eCSSProperty_cue_after, aValue))
           aValue.Truncate();
       }
       break;
     }
     case eCSSProperty_font: {
-      if (AppendValueToString(eCSSProperty_font_style, aValue))
-        aValue.Append(PRUnichar(' '));
-      if (AppendValueToString(eCSSProperty_font_variant, aValue))
-        aValue.Append(PRUnichar(' '));
-      if (AppendValueToString(eCSSProperty_font_weight, aValue))
+      nsCSSValue style, variant, weight, size, lh, family, systemFont;
+      GetValueOrImportantValue(eCSSProperty__x_system_font, systemFont);
+      GetValueOrImportantValue(eCSSProperty_font_style, style);
+      GetValueOrImportantValue(eCSSProperty_font_variant, variant);
+      GetValueOrImportantValue(eCSSProperty_font_weight, weight);
+      GetValueOrImportantValue(eCSSProperty_font_size, size);
+      GetValueOrImportantValue(eCSSProperty_line_height, lh);
+      GetValueOrImportantValue(eCSSProperty_font_family, family);
+
+      if (systemFont.GetUnit() != eCSSUnit_None &&
+          systemFont.GetUnit() != eCSSUnit_Null) {
+        AppendCSSValueToString(eCSSProperty__x_system_font, systemFont, aValue);
+      } else {
+        if (style.GetUnit() != eCSSUnit_Normal) {
+          AppendCSSValueToString(eCSSProperty_font_style, style, aValue);
+          aValue.Append(PRUnichar(' '));
+        }
+        if (variant.GetUnit() != eCSSUnit_Normal) {
+          AppendCSSValueToString(eCSSProperty_font_variant, variant, aValue);
+          aValue.Append(PRUnichar(' '));
+        }
+        if (weight.GetUnit() != eCSSUnit_Normal) {
+          AppendCSSValueToString(eCSSProperty_font_weight, weight, aValue);
+          aValue.Append(PRUnichar(' '));
+        }
+        AppendCSSValueToString(eCSSProperty_font_size, size, aValue);
+        if (lh.GetUnit() != eCSSUnit_Normal) {
+          aValue.Append(PRUnichar('/'));
+          AppendCSSValueToString(eCSSProperty_line_height, lh, aValue);
+        }
         aValue.Append(PRUnichar(' '));
-      if (AppendValueToString(eCSSProperty_font_size, aValue)) {
-          nsAutoString tmp;
-          if (AppendValueToString(eCSSProperty_line_height, tmp)) {
-            aValue.Append(PRUnichar('/'));
-            aValue.Append(tmp);
-          }
-          aValue.Append(PRUnichar(' '));
-          if (!AppendValueToString(eCSSProperty_font_family, aValue))
-            aValue.Truncate();
-      } else {
-        aValue.Truncate();
+        AppendCSSValueToString(eCSSProperty_font_family, family, aValue);
       }
       break;
     }
     case eCSSProperty_list_style:
       if (AppendValueToString(eCSSProperty_list_style_type, aValue))
         aValue.Append(PRUnichar(' '));
       if (AppendValueToString(eCSSProperty_list_style_position, aValue))
         aValue.Append(PRUnichar(' '));
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -168,16 +168,17 @@ CSS_KEY(-moz-simp-chinese-informal, _moz
 CSS_KEY(-moz-spinning, _moz_spinning)
 CSS_KEY(-moz-stack, _moz_stack)
 CSS_KEY(-moz-tamil, _moz_tamil)
 CSS_KEY(-moz-telugu, _moz_telugu)
 CSS_KEY(-moz-thai, _moz_thai)
 CSS_KEY(-moz-trad-chinese-formal, _moz_trad_chinese_formal)
 CSS_KEY(-moz-trad-chinese-informal, _moz_trad_chinese_informal)
 CSS_KEY(-moz-urdu, _moz_urdu)
+CSS_KEY(-moz-use-system-font, _moz_use_system_font)
 CSS_KEY(-moz-use-text-color, _moz_use_text_color)
 CSS_KEY(-moz-visitedhyperlinktext, _moz_visitedhyperlinktext)
 CSS_KEY(-moz-window, _moz_window)
 CSS_KEY(-moz-workspace, _moz_workspace)
 CSS_KEY(-moz-zoom-in, _moz_zoom_in)
 CSS_KEY(-moz-zoom-out, _moz_zoom_out)
 CSS_KEY(above, above)
 CSS_KEY(absolute, absolute)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -164,16 +164,18 @@ protected:
   /**
    * This helper class automatically calls SetParsingCompoundProperty in its
    * constructor and takes care of resetting it to false in its destructor.
    */
   class nsAutoParseCompoundProperty {
     public:
       nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser)
       {
+        NS_ASSERTION(!aParser->IsParsingCompoundProperty(),
+                     "already parsing compound property");
         NS_ASSERTION(aParser, "Null parser?");
         aParser->SetParsingCompoundProperty(PR_TRUE);
       }
 
       ~nsAutoParseCompoundProperty()
       {
         mParser->SetParsingCompoundProperty(PR_FALSE);
       }
@@ -3465,22 +3467,16 @@ CSSParserImpl::DoTransferTempData(nsCSSD
     case eCSSType_Value: {
       nsCSSValue *source = NS_STATIC_CAST(nsCSSValue*, v_source);
       nsCSSValue *dest = NS_STATIC_CAST(nsCSSValue*, v_dest);
       if (*source != *dest)
         *aChanged = PR_TRUE;
       dest->~nsCSSValue();
       memcpy(dest, source, sizeof(nsCSSValue));
       new (source) nsCSSValue();
-      if (dest->GetUnit() == eCSSUnit_Null) {
-        // Some of our property parsers actually want to _clear_ properties in
-        // mData (eg the "font" shorthand parser does for system fonts).  We've
-        // cleared the data; now clear the bit too.
-        mData.ClearPropertyBit(aPropID);
-      }
     } break;
 
     case eCSSType_Rect: {
       nsCSSRect *source = NS_STATIC_CAST(nsCSSRect*, v_source);
       nsCSSRect *dest = NS_STATIC_CAST(nsCSSRect*, v_dest);
       if (*source != *dest)
         *aChanged = PR_TRUE;
       dest->~nsCSSRect();
@@ -3544,16 +3540,17 @@ CSSParserImpl::DoTransferTempData(nsCSSD
 #define VARIANT_STRING          0x000400  // S
 #define VARIANT_COUNTER         0x000800  // 
 #define VARIANT_ATTR            0x001000  //
 #define VARIANT_IDENTIFIER      0x002000  // D
 #define VARIANT_AUTO            0x010000  // A
 #define VARIANT_INHERIT         0x020000  // H eCSSUnit_Initial, eCSSUnit_Inherit
 #define VARIANT_NONE            0x040000  // O
 #define VARIANT_NORMAL          0x080000  // M
+#define VARIANT_SYSFONT         0x100000  // eCSSUnit_System_Font
 
 // Common combinations of variants
 #define VARIANT_AL   (VARIANT_AUTO | VARIANT_LENGTH)
 #define VARIANT_LP   (VARIANT_LENGTH | VARIANT_PERCENT)
 #define VARIANT_AH   (VARIANT_AUTO | VARIANT_INHERIT)
 #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
 #define VARIANT_AHI  (VARIANT_AH | VARIANT_INTEGER)
 #define VARIANT_AHK  (VARIANT_AH | VARIANT_KEYWORD)
@@ -3757,16 +3754,19 @@ PRBool CSSParserImpl::ParseVariant(nsres
     if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
       if ((aVariantMask & VARIANT_AUTO) != 0) {
         if (eCSSKeyword_auto == keyword) {
           aValue.SetAutoValue();
           return PR_TRUE;
         }
       }
       if ((aVariantMask & VARIANT_INHERIT) != 0) {
+        // XXX Should we check IsParsingCompoundProperty, or do all
+        // callers handle it?  (Not all callers set it, though, since
+        // they want the quirks that are disabled by setting it.)
         if (eCSSKeyword_inherit == keyword) {
           aValue.SetInheritValue();
           return PR_TRUE;
         }
         else if (eCSSKeyword__moz_initial == keyword) { // anything that can inherit can also take an initial val.
           aValue.SetInitialValue();
           return PR_TRUE;
         }
@@ -3778,16 +3778,23 @@ PRBool CSSParserImpl::ParseVariant(nsres
         }
       }
       if ((aVariantMask & VARIANT_NORMAL) != 0) {
         if (eCSSKeyword_normal == keyword) {
           aValue.SetNormalValue();
           return PR_TRUE;
         }
       }
+      if ((aVariantMask & VARIANT_SYSFONT) != 0) {
+        if (eCSSKeyword__moz_use_system_font == keyword &&
+            !IsParsingCompoundProperty()) {
+          aValue.SetSystemFontValue();
+          return PR_TRUE;
+        }
+      }
       if ((aVariantMask & VARIANT_KEYWORD) != 0) {
         PRInt32 value;
         if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) {
           aValue.SetIntValue(value, eCSSUnit_Enumerated);
           return PR_TRUE;
         }
       }
     }
@@ -4354,16 +4361,17 @@ PRBool CSSParserImpl::ParseProperty(nsre
     return ParsePaint(aErrorCode, &mTempData.mSVG.mStroke, eCSSProperty_stroke);
   case eCSSProperty_stroke_dasharray:
     return ParseDasharray(aErrorCode);
   case eCSSProperty_marker:
     return ParseMarker(aErrorCode);
 #endif
 
   // Strip out properties we use internally.
+  case eCSSProperty__x_system_font:
   case eCSSProperty_margin_end_value:
   case eCSSProperty_margin_left_value:
   case eCSSProperty_margin_right_value:
   case eCSSProperty_margin_start_value:
   case eCSSProperty_margin_left_ltr_source:
   case eCSSProperty_margin_left_rtl_source:
   case eCSSProperty_margin_right_ltr_source:
   case eCSSProperty_margin_right_rtl_source:
@@ -4458,16 +4466,17 @@ PRBool CSSParserImpl::ParseSingleValuePr
   case eCSSProperty_fill:
   case eCSSProperty_stroke:
   case eCSSProperty_stroke_dasharray:
   case eCSSProperty_marker:
 #endif
     NS_ERROR("not a single value property");
     return PR_FALSE;
 
+  case eCSSProperty__x_system_font:
   case eCSSProperty_margin_left_ltr_source:
   case eCSSProperty_margin_left_rtl_source:
   case eCSSProperty_margin_right_ltr_source:
   case eCSSProperty_margin_right_rtl_source:
   case eCSSProperty_padding_left_ltr_source:
   case eCSSProperty_padding_left_rtl_source:
   case eCSSProperty_padding_right_ltr_source:
   case eCSSProperty_padding_right_rtl_source:
@@ -4674,40 +4683,41 @@ PRBool CSSParserImpl::ParseSingleValuePr
     return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
                         nsCSSProps::kFloatKTable);
   case eCSSProperty_float_edge:
     return ParseVariant(aErrorCode, aValue, VARIANT_HK,
                         nsCSSProps::kFloatEdgeKTable);
   case eCSSProperty_font_family:
     return ParseFamily(aErrorCode, aValue);
   case eCSSProperty_font_size: 
-    return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKLP,
+    return ParsePositiveVariant(aErrorCode, aValue,
+                                VARIANT_HKLP | VARIANT_SYSFONT,
                                 nsCSSProps::kFontSizeKTable);
   case eCSSProperty_font_size_adjust:
-    return ParseVariant(aErrorCode, aValue, VARIANT_HON,
+    return ParseVariant(aErrorCode, aValue, VARIANT_HON | VARIANT_SYSFONT,
                         nsnull);
   case eCSSProperty_font_stretch:
-    return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
+    return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_SYSFONT,
                         nsCSSProps::kFontStretchKTable);
   case eCSSProperty_font_style:
-    return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
+    return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_SYSFONT,
                         nsCSSProps::kFontStyleKTable);
   case eCSSProperty_font_variant:
-    return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
+    return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_SYSFONT,
                         nsCSSProps::kFontVariantKTable);
   case eCSSProperty_font_weight:
     return ParseFontWeight(aErrorCode, aValue);
   case eCSSProperty_ime_mode:
     return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NORMAL,
                         nsCSSProps::kIMEModeKTable);
   case eCSSProperty_letter_spacing:
   case eCSSProperty_word_spacing:
     return ParseVariant(aErrorCode, aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
   case eCSSProperty_line_height:
-    return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN | VARIANT_NORMAL, nsnull);
+    return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN | VARIANT_NORMAL | VARIANT_SYSFONT, nsnull);
   case eCSSProperty_list_style_image:
     return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
   case eCSSProperty_list_style_position:
     return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
   case eCSSProperty_list_style_type:
     return ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
   case eCSSProperty_margin_bottom:
   case eCSSProperty_margin_end_value: // for internal use
@@ -5592,42 +5602,37 @@ PRBool CSSParserImpl::ParseFont(nsresult
     eCSSProperty_font_weight
   };
 
   nsCSSValue  family;
   if (ParseVariant(aErrorCode, family, VARIANT_HK, nsCSSProps::kFontKTable)) {
     if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
       if (eCSSUnit_Inherit == family.GetUnit() ||
           eCSSUnit_Initial == family.GetUnit()) {
+        AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
         AppendValue(eCSSProperty_font_family, family);
         AppendValue(eCSSProperty_font_style, family);
         AppendValue(eCSSProperty_font_variant, family);
         AppendValue(eCSSProperty_font_weight, family);
         AppendValue(eCSSProperty_font_size, family);
         AppendValue(eCSSProperty_line_height, family);
         AppendValue(eCSSProperty_font_stretch, family);
         AppendValue(eCSSProperty_font_size_adjust, family);
       }
       else {
-        AppendValue(eCSSProperty_font_family, family);  // keyword value overrides everything else
-        nsCSSValue empty;
-        // XXXbz this is actually _clearing_ the values for the following
-        // properties in mTempData, but setting the bit for them.  We need that
-        // because we want to clear out the values in mData when all is said
-        // and done.  See the code in TransferTempData that handles this.  The
-        // end result is that mData always has its property bits set like it
-        // should, but mTempData can, in fact, have bits set for properties
-        // that are not set...
-        AppendValue(eCSSProperty_font_style, empty);
-        AppendValue(eCSSProperty_font_variant, empty);
-        AppendValue(eCSSProperty_font_weight, empty);
-        AppendValue(eCSSProperty_font_size, empty);
-        AppendValue(eCSSProperty_line_height, empty);
-        AppendValue(eCSSProperty_font_stretch, empty);
-        AppendValue(eCSSProperty_font_size_adjust, empty);
+        AppendValue(eCSSProperty__x_system_font, family);
+        nsCSSValue systemFont(eCSSUnit_System_Font);
+        AppendValue(eCSSProperty_font_family, systemFont);
+        AppendValue(eCSSProperty_font_style, systemFont);
+        AppendValue(eCSSProperty_font_variant, systemFont);
+        AppendValue(eCSSProperty_font_weight, systemFont);
+        AppendValue(eCSSProperty_font_size, systemFont);
+        AppendValue(eCSSProperty_line_height, systemFont);
+        AppendValue(eCSSProperty_font_stretch, systemFont);
+        AppendValue(eCSSProperty_font_size_adjust, systemFont);
       }
       return PR_TRUE;
     }
     return PR_FALSE;
   }
 
   // Get optional font-style, font-variant and font-weight (in any order)
   const PRInt32 numProps = 3;
@@ -5665,36 +5670,38 @@ PRBool CSSParserImpl::ParseFont(nsresult
       return PR_FALSE;
     }
   }
   else {
     lineHeight.SetNormalValue();
   }
 
   // Get final mandatory font-family
+  nsAutoParseCompoundProperty compound(this);
   if (ParseFamily(aErrorCode, family)) {
     if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
         ExpectEndProperty(aErrorCode, PR_TRUE)) {
+      AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
       AppendValue(eCSSProperty_font_family, family);
       AppendValue(eCSSProperty_font_style, values[0]);
       AppendValue(eCSSProperty_font_variant, values[1]);
       AppendValue(eCSSProperty_font_weight, values[2]);
       AppendValue(eCSSProperty_font_size, size);
       AppendValue(eCSSProperty_line_height, lineHeight);
       AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
       AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
       return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 PRBool CSSParserImpl::ParseFontWeight(nsresult& aErrorCode, nsCSSValue& aValue)
 {
-  if (ParseVariant(aErrorCode, aValue, VARIANT_HMKI, nsCSSProps::kFontWeightKTable)) {
+  if (ParseVariant(aErrorCode, aValue, VARIANT_HMKI | VARIANT_SYSFONT, nsCSSProps::kFontWeightKTable)) {
     if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value
       PRInt32 intValue = aValue.GetIntValue();
       if ((100 <= intValue) &&
           (intValue <= 900) &&
           (0 == (intValue % 100))) {
         return PR_TRUE;
       } else {
         UngetToken();
@@ -5717,20 +5724,25 @@ PRBool CSSParserImpl::ParseFamily(nsresu
     }
     if (eCSSToken_Ident == tk->mType) {
       if (firstOne) {
         nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
         if (keyword == eCSSKeyword_inherit) {
           aValue.SetInheritValue();
           return PR_TRUE;
         }
-        else if (keyword == eCSSKeyword__moz_initial) {
+        if (keyword == eCSSKeyword__moz_initial) {
           aValue.SetInitialValue();
           return PR_TRUE;
         }
+        if (keyword == eCSSKeyword__moz_use_system_font &&
+            !IsParsingCompoundProperty()) {
+          aValue.SetSystemFontValue();
+          return PR_TRUE;
+        }
       }
       else {
         family.Append(PRUnichar(','));
       }
       family.Append(tk->mIdent);
       for (;;) {
         if (!GetToken(aErrorCode, PR_FALSE)) {
           break;
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -264,16 +264,19 @@ CSS_PROP_BORDER(-moz-border-radius-tople
 CSS_PROP_BORDER(-moz-border-radius-topright, _moz_border_radius_topRight, MozBorderRadiusTopright, Margin, mBorderRadius.mRight, eCSSType_Value, nsnull)
 CSS_PROP_BORDER(-moz-border-radius-bottomleft, _moz_border_radius_bottomLeft, MozBorderRadiusBottomleft, Margin, mBorderRadius.mLeft, eCSSType_Value, nsnull)
 CSS_PROP_BORDER(-moz-border-radius-bottomright, _moz_border_radius_bottomRight, MozBorderRadiusBottomright, Margin, mBorderRadius.mBottom, eCSSType_Value, nsnull)
 CSS_PROP_SHORTHAND(-moz-outline-radius, _moz_outline_radius, MozOutlineRadius)
 CSS_PROP_OUTLINE(-moz-outline-radius-topleft, _moz_outline_radius_topLeft, MozOutlineRadiusTopleft, Margin, mOutlineRadius.mTop, eCSSType_Value, nsnull)
 CSS_PROP_OUTLINE(-moz-outline-radius-topright, _moz_outline_radius_topRight, MozOutlineRadiusTopright, Margin, mOutlineRadius.mRight, eCSSType_Value, nsnull)
 CSS_PROP_OUTLINE(-moz-outline-radius-bottomleft, _moz_outline_radius_bottomLeft, MozOutlineRadiusBottomleft, Margin, mOutlineRadius.mLeft, eCSSType_Value, nsnull)
 CSS_PROP_OUTLINE(-moz-outline-radius-bottomright, _moz_outline_radius_bottomRight, MozOutlineRadiusBottomright, Margin, mOutlineRadius.mBottom, eCSSType_Value, nsnull)
+#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
+CSS_PROP_FONT(-x-system-font, _x_system_font, X, Font, mSystemFont, eCSSType_Value, kFontKTable)
+#endif
 CSS_PROP_BACKENDONLY(azimuth, azimuth, Azimuth, Aural, mAzimuth, eCSSType_Value, kAzimuthKTable)
 CSS_PROP_SHORTHAND(background, background, Background)
 CSS_PROP_BACKGROUND(background-attachment, background_attachment, BackgroundAttachment, Color, mBackAttachment, eCSSType_Value, kBackgroundAttachmentKTable)
 CSS_PROP_BACKGROUND(-moz-background-clip, _moz_background_clip, MozBackgroundClip, Color, mBackClip, eCSSType_Value, kBackgroundClipKTable)
 CSS_PROP_BACKGROUND(background-color, background_color, BackgroundColor, Color, mBackColor, eCSSType_Value, kBackgroundColorKTable)
 CSS_PROP_BACKGROUND(background-image, background_image, BackgroundImage, Color, mBackImage, eCSSType_Value, nsnull)
 CSS_PROP_BACKGROUND(-moz-background-inline-policy, _moz_background_inline_policy, MozBackgroundInlinePolicy, Color, mBackInlinePolicy, eCSSType_Value, kBackgroundInlinePolicyKTable)
 CSS_PROP_BACKGROUND(-moz-background-origin, _moz_background_origin, MozBackgroundOrigin, Color, mBackOrigin, eCSSType_Value, kBackgroundOriginKTable)
@@ -324,17 +327,17 @@ CSS_PROP_BACKENDONLY(cue-before, cue_bef
 CSS_PROP_USERINTERFACE(cursor, cursor, Cursor, UserInterface, mCursor, eCSSType_ValueList, kCursorKTable)
 CSS_PROP_VISIBILITY(direction, direction, Direction, Display, mDirection, eCSSType_Value, kDirectionKTable)
 CSS_PROP_DISPLAY(display, display, Display, Display, mDisplay, eCSSType_Value, kDisplayKTable)
 CSS_PROP_BACKENDONLY(elevation, elevation, Elevation, Aural, mElevation, eCSSType_Value, kElevationKTable)
 CSS_PROP_TABLEBORDER(empty-cells, empty_cells, EmptyCells, Table, mEmptyCells, eCSSType_Value, kEmptyCellsKTable)
 CSS_PROP_DISPLAY(float, float, CssFloat, Display, mFloat, eCSSType_Value, kFloatKTable)
 CSS_PROP_BORDER(-moz-float-edge, float_edge, MozFloatEdge, Margin, mFloatEdge, eCSSType_Value, kFloatEdgeKTable) // XXX bug 3935
 CSS_PROP_SHORTHAND(font, font, Font)
-CSS_PROP_FONT(font-family, font_family, FontFamily, Font, mFamily, eCSSType_Value, kFontKTable)
+CSS_PROP_FONT(font-family, font_family, FontFamily, Font, mFamily, eCSSType_Value, nsnull)
 CSS_PROP_FONT(font-size, font_size, FontSize, Font, mSize, eCSSType_Value, kFontSizeKTable)
 CSS_PROP_FONT(font-size-adjust, font_size_adjust, FontSizeAdjust, Font, mSizeAdjust, eCSSType_Value, nsnull)
 CSS_PROP_BACKENDONLY(font-stretch, font_stretch, FontStretch, Font, mStretch, eCSSType_Value, kFontStretchKTable)
 CSS_PROP_FONT(font-style, font_style, FontStyle, Font, mStyle, eCSSType_Value, kFontStyleKTable)
 CSS_PROP_FONT(font-variant, font_variant, FontVariant, Font, mVariant, eCSSType_Value, kFontVariantKTable)
 CSS_PROP_FONT(font-weight, font_weight, FontWeight, Font, mWeight, eCSSType_Value, kFontWeightKTable)
 CSS_PROP_UIRESET(-moz-force-broken-image-icon, force_broken_image_icon, MozForceBrokenImageIcon, UserInterface, mForceBrokenImageIcon, eCSSType_Value, nsnull) // bug 58646
 CSS_PROP_POSITION(height, height, Height, Position, mHeight, eCSSType_Value, nsnull)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1328,16 +1328,17 @@ static const nsCSSProperty gFontSubpropT
   eCSSProperty_font_family,
   eCSSProperty_font_style,
   eCSSProperty_font_variant,
   eCSSProperty_font_weight,
   eCSSProperty_font_size,
   eCSSProperty_line_height,
   eCSSProperty_font_size_adjust, // XXX Added LDB.
   eCSSProperty_font_stretch, // XXX Added LDB.
+  eCSSProperty__x_system_font,
   eCSSProperty_UNKNOWN
 };
 
 static const nsCSSProperty gListStyleSubpropTable[] = {
   eCSSProperty_list_style_type,
   eCSSProperty_list_style_image,
   eCSSProperty_list_style_position,
   eCSSProperty_UNKNOWN
--- a/layout/style/nsCSSStruct.h
+++ b/layout/style/nsCSSStruct.h
@@ -197,16 +197,17 @@ struct nsCSSStruct {
 typedef nsCSSStruct nsRuleDataStruct;
 
 
 struct nsCSSFont : public nsCSSStruct {
   nsCSSFont(void);
   nsCSSFont(const nsCSSFont& aCopy);
   ~nsCSSFont(void);
 
+  nsCSSValue mSystemFont;
   nsCSSValue mFamily;
   nsCSSValue mStyle;
   nsCSSValue mVariant;
   nsCSSValue mWeight;
   nsCSSValue mSize;
   nsCSSValue mSizeAdjust; // NEW
   nsCSSValue mStretch; // NEW
 };
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -344,16 +344,22 @@ void nsCSSValue::SetNoneValue()
 }
 
 void nsCSSValue::SetNormalValue()
 {
   Reset();
   mUnit = eCSSUnit_Normal;
 }
 
+void nsCSSValue::SetSystemFontValue()
+{
+  Reset();
+  mUnit = eCSSUnit_System_Font;
+}
+
 void nsCSSValue::StartImageLoad(nsIDocument* aDocument, PRBool aIsBGImage) const
 {
   NS_PRECONDITION(eCSSUnit_URL == mUnit, "Not a URL value!");
   nsCSSValue::Image* image =
     new nsCSSValue::Image(mValue.mURL->mURI,
                           mValue.mURL->mString,
                           mValue.mURL->mReferrer,
                           mValue.mURL->mOriginPrincipal,
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -57,16 +57,17 @@ class nsIPrincipal;
 
 enum nsCSSUnit {
   eCSSUnit_Null         = 0,      // (n/a) null unit, value is not specified
   eCSSUnit_Auto         = 1,      // (n/a) value is algorithmic
   eCSSUnit_Inherit      = 2,      // (n/a) value is inherited
   eCSSUnit_Initial      = 3,      // (n/a) value is default UA value
   eCSSUnit_None         = 4,      // (n/a) value is none
   eCSSUnit_Normal       = 5,      // (n/a) value is normal (algorithmic, different than auto)
+  eCSSUnit_System_Font  = 6,      // (n/a) value is -moz-use-system-font
   eCSSUnit_String       = 10,     // (PRUnichar*) a string value
   eCSSUnit_Attr         = 11,     // (PRUnichar*) a attr(string) value
   eCSSUnit_Array        = 20,     // (nsCSSValue::Array*) a list of values
   eCSSUnit_Counter      = 21,     // (nsCSSValue::Array*) a counter(string,[string]) value
   eCSSUnit_Counters     = 22,     // (nsCSSValue::Array*) a counters(string,string[,string]) value
   eCSSUnit_URL          = 30,     // (nsCSSValue::URL*) value
   eCSSUnit_Image        = 31,     // (nsCSSValue::Image*) value
   eCSSUnit_Integer      = 50,     // (int) simple value
@@ -130,18 +131,18 @@ public:
 
   struct Image;
   friend struct Image;
   
   // for valueless units only (null, auto, inherit, none, normal)
   explicit nsCSSValue(nsCSSUnit aUnit = eCSSUnit_Null)
     : mUnit(aUnit)
   {
-    NS_ASSERTION(aUnit <= eCSSUnit_Normal, "not a valueless unit");
-    if (aUnit > eCSSUnit_Normal) {
+    NS_ASSERTION(aUnit <= eCSSUnit_System_Font, "not a valueless unit");
+    if (aUnit > eCSSUnit_System_Font) {
       mUnit = eCSSUnit_Null;
     }
     mValue.mInt = 0;
   }
 
   nsCSSValue(PRInt32 aValue, nsCSSUnit aUnit) NS_HIDDEN;
   nsCSSValue(float aValue, nsCSSUnit aUnit) NS_HIDDEN;
   nsCSSValue(const nsString& aValue, nsCSSUnit aUnit) NS_HIDDEN;
@@ -270,16 +271,17 @@ public:
   NS_HIDDEN_(void)  SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
   NS_HIDDEN_(void)  SetURLValue(nsCSSValue::URL* aURI);
   NS_HIDDEN_(void)  SetImageValue(nsCSSValue::Image* aImage);
   NS_HIDDEN_(void)  SetAutoValue();
   NS_HIDDEN_(void)  SetInheritValue();
   NS_HIDDEN_(void)  SetInitialValue();
   NS_HIDDEN_(void)  SetNoneValue();
   NS_HIDDEN_(void)  SetNormalValue();
+  NS_HIDDEN_(void)  SetSystemFontValue();
   NS_HIDDEN_(void)  StartImageLoad(nsIDocument* aDocument,
                                    PRBool aIsBGImage = PR_FALSE)
                                    const;  // Not really const, but pretending
 
   // Returns an already addrefed buffer.  Can return null on allocation
   // failure.
   static nsStringBuffer* BufferFromString(const nsString& aValue);
   
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1906,34 +1906,21 @@ nsRuleNode::SetFont(nsPresContext* aPres
                     nscoord aMinFontSize,
                     PRBool aIsGeneric, const nsRuleDataFont& aFontData,
                     const nsFont& aDefaultFont, const nsStyleFont* aParentFont,
                     nsStyleFont* aFont, PRBool& aInherited)
 {
   const nsFont* defaultVariableFont =
     aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID);
 
-  // font-family: string list, enum, inherit
-  if (eCSSUnit_String == aFontData.mFamily.GetUnit()) {
-    // set the correct font if we are using DocumentFonts OR we are overriding for XUL
-    // MJA: bug 31816
-    if (!aIsGeneric) {
-      // only bother appending fallback fonts if this isn't a fallback generic font itself
-      if (!aFont->mFont.name.IsEmpty())
-        aFont->mFont.name.Append((PRUnichar)',');
-      // XXXldb Should this name be quoted?
-      aFont->mFont.name.Append(aDefaultFont.name);
-    }
-    aFont->mFont.familyNameQuirks =
-        (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks &&
-         aFontData.mFamilyFromHTML);
-  }
-  else if (eCSSUnit_Enumerated == aFontData.mFamily.GetUnit()) {
+  // -moz-system-font: enum (never inherit!)
+  nsFont systemFont;
+  if (eCSSUnit_Enumerated == aFontData.mSystemFont.GetUnit()) {
     nsSystemFontID sysID;
-    switch (aFontData.mFamily.GetIntValue()) {
+    switch (aFontData.mSystemFont.GetIntValue()) {
       // If you add fonts to this list, you need to also patch the list
       // in CheckFontCallback (also in this file).
       case NS_STYLE_FONT_CAPTION:       sysID = eSystemFont_Caption;      break;    // css2
       case NS_STYLE_FONT_ICON:          sysID = eSystemFont_Icon;         break;
       case NS_STYLE_FONT_MENU:          sysID = eSystemFont_Menu;         break;
       case NS_STYLE_FONT_MESSAGE_BOX:   sysID = eSystemFont_MessageBox;   break;
       case NS_STYLE_FONT_SMALL_CAPTION: sysID = eSystemFont_SmallCaption; break;
       case NS_STYLE_FONT_STATUS_BAR:    sysID = eSystemFont_StatusBar;    break;
@@ -1945,27 +1932,24 @@ nsRuleNode::SetFont(nsPresContext* aPres
       case NS_STYLE_FONT_DIALOG:        sysID = eSystemFont_Dialog;       break;
       case NS_STYLE_FONT_BUTTON:        sysID = eSystemFont_Button;       break;
       case NS_STYLE_FONT_PULL_DOWN_MENU:sysID = eSystemFont_PullDownMenu; break;
       case NS_STYLE_FONT_LIST:          sysID = eSystemFont_List;         break;
       case NS_STYLE_FONT_FIELD:         sysID = eSystemFont_Field;        break;
     }
 
     // GetSystemFont sets the font face but not necessarily the size
-    aFont->mFont.size = defaultVariableFont->size;
+    // XXX Or at least it used to -- no longer true for thebes.  Maybe
+    // it should be again, though.
+    systemFont.size = defaultVariableFont->size;
 
     if (NS_FAILED(aPresContext->DeviceContext()->GetSystemFont(sysID,
-                                                             &aFont->mFont))) {
-        aFont->mFont.name = defaultVariableFont->name;
+                                                               &systemFont))) {
+        systemFont.name = defaultVariableFont->name;
     }
-    // this becomes our cascading size
-    aFont->mSize = aFont->mFont.size =
-      nsStyleFont::ZoomText(aPresContext, aFont->mFont.size);
-
-    aFont->mFont.familyNameQuirks = PR_FALSE;
 
     // XXXldb All of this platform-specific stuff should be in the
     // nsIDeviceContext implementations, not here.
 
 #ifdef XP_WIN
     //
     // As far as I can tell the system default fonts and sizes for
     // on MS-Windows for Buttons, Listboxes/Comboxes and Text Fields are 
@@ -1978,54 +1962,90 @@ nsRuleNode::SetFont(nsPresContext* aPres
       //    We use whatever font is defined by the system. Which it appears
       //    (and the assumption is) it is always a proportional font. Then we
       //    always use 2 points smaller than what the browser has defined as
       //    the default proportional font.
       case eSystemFont_Field:
       case eSystemFont_Button:
       case eSystemFont_List:
         // Assumption: system defined font is proportional
-        aFont->mSize = nsStyleFont::ZoomText(aPresContext,
-             PR_MAX(defaultVariableFont->size - aPresContext->PointsToAppUnits(2), 0));
+        systemFont.size = 
+          PR_MAX(defaultVariableFont->size - aPresContext->PointsToAppUnits(2), 0);
         break;
     }
 #endif
+  } else {
+    // In case somebody explicitly used -moz-use-system-font.
+    systemFont = *defaultVariableFont;
+  }
+
+
+  // font-family: string list, enum, inherit
+  NS_ASSERTION(eCSSUnit_Enumerated != aFontData.mFamily.GetUnit(),
+               "system fonts should not be in mFamily anymore");
+  if (eCSSUnit_String == aFontData.mFamily.GetUnit()) {
+    // set the correct font if we are using DocumentFonts OR we are overriding for XUL
+    // MJA: bug 31816
+    if (!aIsGeneric) {
+      // only bother appending fallback fonts if this isn't a fallback generic font itself
+      if (!aFont->mFont.name.IsEmpty())
+        aFont->mFont.name.Append((PRUnichar)',');
+      // XXXldb Should this name be quoted?
+      aFont->mFont.name.Append(aDefaultFont.name);
+    }
+    aFont->mFont.familyNameQuirks =
+        (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks &&
+         aFontData.mFamilyFromHTML);
+    aFont->mFont.systemFont = PR_FALSE;
+  }
+  else if (eCSSUnit_System_Font == aFontData.mFamily.GetUnit()) {
+    aFont->mFont.name = systemFont.name;
+    aFont->mFont.familyNameQuirks = PR_FALSE;
+    aFont->mFont.systemFont = PR_TRUE;
   }
   else if (eCSSUnit_Inherit == aFontData.mFamily.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mFont.name = aParentFont->mFont.name;
     aFont->mFont.familyNameQuirks = aParentFont->mFont.familyNameQuirks;
+    aFont->mFont.systemFont = aParentFont->mFont.systemFont;
   }
   else if (eCSSUnit_Initial == aFontData.mFamily.GetUnit()) {
     aFont->mFont.name = defaultVariableFont->name;
     aFont->mFont.familyNameQuirks = PR_FALSE;
+    aFont->mFont.systemFont = defaultVariableFont->systemFont;
   }
 
   // font-style: enum, normal, inherit
   if (eCSSUnit_Enumerated == aFontData.mStyle.GetUnit()) {
     aFont->mFont.style = aFontData.mStyle.GetIntValue();
   }
   else if (eCSSUnit_Normal == aFontData.mStyle.GetUnit()) {
     aFont->mFont.style = NS_STYLE_FONT_STYLE_NORMAL;
   }
+  else if (eCSSUnit_System_Font == aFontData.mStyle.GetUnit()) {
+    aFont->mFont.style = systemFont.style;
+  }
   else if (eCSSUnit_Inherit == aFontData.mStyle.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mFont.style = aParentFont->mFont.style;
   }
   else if (eCSSUnit_Initial == aFontData.mStyle.GetUnit()) {
     aFont->mFont.style = defaultVariableFont->style;
   }
 
   // font-variant: enum, normal, inherit
   if (eCSSUnit_Enumerated == aFontData.mVariant.GetUnit()) {
     aFont->mFont.variant = aFontData.mVariant.GetIntValue();
   }
   else if (eCSSUnit_Normal == aFontData.mVariant.GetUnit()) {
     aFont->mFont.variant = NS_STYLE_FONT_VARIANT_NORMAL;
   }
+  else if (eCSSUnit_System_Font == aFontData.mVariant.GetUnit()) {
+    aFont->mFont.variant = systemFont.variant;
+  }
   else if (eCSSUnit_Inherit == aFontData.mVariant.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mFont.variant = aParentFont->mFont.variant;
   }
   else if (eCSSUnit_Initial == aFontData.mVariant.GetUnit()) {
     aFont->mFont.variant = defaultVariableFont->variant;
   }
 
@@ -2045,16 +2065,19 @@ nsRuleNode::SetFont(nsPresContext* aPres
         aInherited = PR_TRUE;
         aFont->mFont.weight = nsStyleUtil::ConstrainFontWeight(aParentFont->mFont.weight + value);
         break;
     }
   }
   else if (eCSSUnit_Normal == aFontData.mWeight.GetUnit()) {
     aFont->mFont.weight = NS_STYLE_FONT_WEIGHT_NORMAL;
   }
+  else if (eCSSUnit_System_Font == aFontData.mWeight.GetUnit()) {
+    aFont->mFont.weight = systemFont.weight;
+  }
   else if (eCSSUnit_Inherit == aFontData.mWeight.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mFont.weight = aParentFont->mFont.weight;
   }
   else if (eCSSUnit_Initial == aFontData.mWeight.GetUnit()) {
     aFont->mFont.weight = defaultVariableFont->weight;
   }
 
@@ -2105,16 +2128,21 @@ nsRuleNode::SetFont(nsPresContext* aPres
            aFontData.mSize.GetUnit() == eCSSUnit_Pixel;
   }
   else if (eCSSUnit_Percent == aFontData.mSize.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mSize = NSToCoordRound(float(aParentFont->mSize) *
                                   aFontData.mSize.GetPercentValue());
     zoom = PR_FALSE;
   }
+  else if (eCSSUnit_System_Font == aFontData.mSize.GetUnit()) {
+    // this becomes our cascading size
+    aFont->mSize = systemFont.size;
+    zoom = PR_TRUE;
+  }
   else if (eCSSUnit_Inherit == aFontData.mSize.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mSize = aParentFont->mSize;
     zoom = PR_FALSE;
   }
   else if (eCSSUnit_Initial == aFontData.mSize.GetUnit()) {
     // The initial value is 'medium', which has magical sizing based on
     // the generic font family, so do that here too.
@@ -2132,16 +2160,19 @@ nsRuleNode::SetFont(nsPresContext* aPres
 
   // font-size-adjust: number, none, inherit
   if (eCSSUnit_Number == aFontData.mSizeAdjust.GetUnit()) {
     aFont->mFont.sizeAdjust = aFontData.mSizeAdjust.GetFloatValue();
   }
   else if (eCSSUnit_None == aFontData.mSizeAdjust.GetUnit()) {
     aFont->mFont.sizeAdjust = 0.0f;
   }
+  else if (eCSSUnit_System_Font == aFontData.mSizeAdjust.GetUnit()) {
+    aFont->mFont.sizeAdjust = systemFont.sizeAdjust;
+  }
   else if (eCSSUnit_Inherit == aFontData.mSizeAdjust.GetUnit()) {
     aInherited = PR_TRUE;
     aFont->mFont.sizeAdjust = aParentFont->mFont.sizeAdjust;
   }
   else if (eCSSUnit_Initial == aFontData.mSizeAdjust.GetUnit()) {
     aFont->mFont.sizeAdjust = 0.0f;
   }
 }
@@ -2352,17 +2383,18 @@ nsRuleNode::ComputeTextData(nsStyleStruc
   // line-height: normal, number, length, percent, inherit
   if (eCSSUnit_Percent == textData.mLineHeight.GetUnit()) {
     inherited = PR_TRUE;
     // Use |mFont.size| to pick up minimum font size.
     text->mLineHeight.SetCoordValue(
         nscoord(float(aContext->GetStyleFont()->mFont.size) *
                 textData.mLineHeight.GetPercentValue()));
   }
-  else if (eCSSUnit_Initial == textData.mLineHeight.GetUnit()) {
+  else if (eCSSUnit_Initial == textData.mLineHeight.GetUnit() ||
+           eCSSUnit_System_Font == textData.mLineHeight.GetUnit()) {
     text->mLineHeight.SetNormalValue();
   }
   else {
     SetCoord(textData.mLineHeight, text->mLineHeight, parentText->mLineHeight,
              SETCOORD_LH | SETCOORD_FACTOR | SETCOORD_NORMAL,
              aContext, mPresContext, inherited);
     if (textData.mLineHeight.IsFixedLengthUnit() ||
         textData.mLineHeight.GetUnit() == eCSSUnit_Pixel) {
--- a/layout/style/test/ListCSSProperties.cpp
+++ b/layout/style/test/ListCSSProperties.cpp
@@ -107,16 +107,17 @@ const char* gShorthandPropertiesWithDOMP
 };
 
 
 #define ARRAY_LENGTH(a_) (sizeof(a_)/sizeof((a_)[0]))
 
 const char *gInaccessibleProperties[] = {
     // Don't print the properties that aren't accepted by the parser, per
     // CSSParserImpl::ParseProperty
+    "-x-system-font",
     "margin-end-value",
     "margin-left-value",
     "margin-right-value",
     "margin-start-value",
     "margin-left-ltr-source",
     "margin-left-rtl-source",
     "margin-right-ltr-source",
     "margin-right-rtl-source",
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -67,16 +67,17 @@ css_properties.js: host_ListCSSPropertie
 	cat $(srcdir)/css_properties_like_longhand.js >> $@
 
 _TEST_FILES = 	test_bug221428.html \
 		test_bug302186.html \
 		test_bug319381.html \
 		test_bug365932.html \
 		test_bug372770.html \
 		test_bug373293.html \
+		test_bug377947.html \
 		test_bug379440.html \
 		test_bug379741.html \
 		test_bug383075.html \
 		test_dont_use_document_colors.html \
 		test_inherit_storage.html \
 		test_inherit_computation.html \
 		test_initial_storage.html \
 		test_initial_computation.html \
--- a/layout/style/test/test_bug372770.html
+++ b/layout/style/test/test_bug372770.html
@@ -60,29 +60,29 @@ for (var i = 0; i <= 100; ++i) {
   style1.backgroundColor = color2;
   style2.color = color2;
   style2.background = color1;
 
   if (i == 100) {
     // Bug 372783 means this doesn't round-trip quite right
     todo(style1.color == color1,
        "Inline style color roundtripping failed at opacity " + i);
-    todo(style1.background == color2,
+    todo(style1.backgroundColor == color2,
        "Inline style background roundtripping failed at opacity " + i);
     todo(style2.color == color2,
        "Rule style color roundtripping failed at opacity " + i);
     todo(style2.backgroundColor == color1,
        "Rule style background roundtripping failed at opacity " + i);
     color1 = "rgb(128, 128, 128)";
     color2 = "rgb(175, 63, 27)";
   }
 
   is(style1.color, color1,
      "Inline style color roundtripping failed at opacity " + i);
-  is(style1.background, color2,
+  is(style1.backgroundColor, color2,
      "Inline style background roundtripping failed at opacity " + i);
   is(style2.color, color2,
      "Rule style color roundtripping failed at opacity " + i);
   is(style2.backgroundColor, color1,
      "Rule style background roundtripping failed at opacity " + i);
 
 }
 </script>
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_bug377947.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=377947
+-->
+<head>
+  <title>Test for Bug 377947</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/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=377947">Mozilla Bug 377947</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 377947 **/
+
+/*
+ * In particular, test that CSSStyleDeclaration.getPropertyValue doesn't
+ * return values for shorthands when some of the subproperties are not
+ * specified (a change that wasn't all that related to the main point of
+ * the bug).  And also test that the internal system-font property added
+ * in bug 377947 doesn't interfere with that.
+ */
+
+var s = document.getElementById("display").style;
+
+is(s.getPropertyValue("list-style"), "",
+   "list-style shorthand should start off empty");
+s.listStyleType="disc";
+s.listStyleImage="none";
+is(s.getPropertyValue("list-style"), "",
+   "list-style shorthand should be empty when some subproperties specified");
+s.listStylePosition="inside";
+isnot(s.getPropertyValue("list-style"), "",
+      "list-style shorthand should produce value when all subproperties set");
+s.removeProperty("list-style");
+is(s.getPropertyValue("list-style"), "",
+   "list-style shorthand be empty after removal");
+s.listStyle="none";
+isnot(s.getPropertyValue("list-style"), "",
+      "list-style shorthand should produce value when shorthand set");
+s.removeProperty("list-style");
+is(s.getPropertyValue("list-style"), "",
+   "list-style shorthand be empty after removal");
+
+is(s.getPropertyValue("font"), "",
+   "font shorthand should start off empty");
+var all_but_one = {
+  "font-family": "serif",
+  "font-style": "normal",
+  "font-variant": "normal",
+  "font-weight": "bold",
+  "font-size": "small",
+  "font-size-adjust": "0.45",
+  "font-stretch": "normal"
+};
+for (var prop in all_but_one) {
+  s.setProperty(prop, all_but_one[prop], "");
+}
+is(s.getPropertyValue("font"), "",
+   "font shorthand should be empty when some subproperties specified");
+s.setProperty("line-height", "1.5", "");
+isnot(s.getPropertyValue("font"), "",
+      "font shorthand should produce value when all subproperties set");
+s.removeProperty("font");
+is(s.getPropertyValue("font"), "",
+   "font shorthand be empty after removal");
+s.font="medium serif";
+isnot(s.getPropertyValue("font"), "",
+      "font shorthand should produce value when shorthand set");
+s.removeProperty("font");
+is(s.getPropertyValue("font"), "",
+   "font shorthand be empty after removal");
+s.font="menu";
+isnot(s.getPropertyValue("font"), "",
+      "font shorthand should produce value when shorthand (system font) set");
+s.removeProperty("font");
+is(s.getPropertyValue("font"), "",
+   "font shorthand be empty after removal");
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/layout/style/test/test_value_storage.html
+++ b/layout/style/test/test_value_storage.html
@@ -64,19 +64,24 @@ var gNoComputedValue = {
 
 var gNotAccepted = {
   "-moz-column-width": [ "50%" ],
   "-moz-user-select": [ "auto" ],
   "background-color": [ "rgb(255.0,0.387,3489)" ],
   "list-style": [ "none disc outside" ],
 };
 
-var gSpecialFont = [
-  "caption", "icon", "menu", "message-box", "small-caption", "status-bar"
-];
+var gSystemFont = {
+  "caption": true,
+  "icon": true,
+  "menu": true,
+  "message-box": true,
+  "small-caption": true,
+  "status-bar": true,
+};
 
 var gBadCompute = {
   // output wrapped around to positive, in exponential notation
   "-moz-box-ordinal-group": [ "-1", "-1000" ],
 };
 
 var gShortenableValues = {
   "border-color": [ "currentColor currentColor currentcolor CURRENTcolor" ],
@@ -84,32 +89,25 @@ var gShortenableValues = {
 };
 
 function xfail_accepted(property, value)
 {
   if (property in gNotAccepted &&
       gNotAccepted[property].indexOf(value) != -1)
     return true;
 
-  if (property == "font" && gSpecialFont.indexOf(value) != -1)
-    return true;
-
   return false;
 }
 
 function xfail_accepted_split(property, subprop, value)
 {
   if (property in gNotAccepted &&
       gNotAccepted[property].indexOf(value) != -1)
     return true;
 
-  if (property == "font" && subprop != "font-family" &&
-      gSpecialFont.indexOf(value) != -1)
-    return true;
-
   return false;
 }
 
 function xfail_ser_val(property, value)
 {
   if (property != "font" && xfail_accepted(property, value))
     // We already failed the first test, which will make us always pass this
     // one.
@@ -150,23 +148,16 @@ function xfail_idparseser(property, valu
 
 function xfail_idserparse_compute(property, value)
 {
   return false;
 }
 
 function xfail_idsersplitparse_compute(property, subprop, value, step1subcomp)
 {
-  // These failures depend on what the system fonts actually are,
-  // since they show up in computed style but not serialization!
-  if (property == "font" &&
-      gSpecialFont.indexOf(value) != -1 &&
-      gCSSProperties[subprop].initial_values.indexOf(step1subcomp) == -1)
-    return true;
-
   return false;
 }
 
 function xfail_idparsesplitser(property, value)
 {
   return false;
 }
 
@@ -242,17 +233,20 @@ function test_property(property)
          property + ": " + value + "'");
     if (test_computed && info.type != CSS_TYPE_TRUE_SHORTHAND) {
       func = xfail_idserparse_compute(property, value) ? todo_is : is;
       func(gComputedStyle.getPropertyValue(property), step1comp,
            "serialize+parse should be identity transform for '" +
            property + ": " + value + "'");
     }
 
-    if ("subproperties" in info) {
+    if ("subproperties" in info &&
+        // Using setProperty over subproperties is not sufficient for
+        // system fonts, since the shorthand does more than its parts.
+        (property != "font" || !(value in gSystemFont))) {
       gDeclaration.removeProperty(property);
       for (idx in info.subproperties) {
         var subprop = info.subproperties[idx];
         gDeclaration.setProperty(subprop, step1vals[idx], "");
       }
 
       // Now that all the subprops are set, check their values.  Note that we
       // need this in a separate loop, in case parts of the shorthand affect