Bug 569719 part 6: Move Declaration::AppendCSSValueToString into nsCSSValue. r=dbaron
authorZack Weinberg <zweinberg@mozilla.com>
Fri, 23 Jul 2010 11:00:27 -0700
changeset 48854 aa2c754aff8b8e588c3cb0e80b82bafc4f17fe6f
parent 48853 ae6c685d5a378f82eb4c8caf5eafe30e35fdfc1c
child 48855 6d6e4fea65296b07719b1a9668d2234b299581b4
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs569719
milestone2.0b4pre
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 569719 part 6: Move Declaration::AppendCSSValueToString into nsCSSValue. r=dbaron
layout/style/Declaration.cpp
layout/style/Declaration.h
layout/style/nsCSSRules.cpp
layout/style/nsCSSStyleSheet.cpp
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -53,17 +53,16 @@
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 #include "nsCSSProps.h"
 #include "nsFont.h"
 #include "nsReadableUtils.h"
 #include "nsStyleUtil.h"
 #include "nsStyleConsts.h"
 #include "nsCOMPtr.h"
-#include "CSSCalc.h"
 
 namespace mozilla {
 namespace css {
 
 Declaration::Declaration()
 {
   // check that we can fit all the CSS properties into a PRUint8
   // for the mOrder array - if not, might need to use PRUint16!
@@ -133,500 +132,107 @@ PRBool Declaration::AppendValueToString(
 Declaration::AppendStorageToString(nsCSSProperty aProperty,
                                    const void* aStorage,
                                    nsAString& aResult)
 {
   if (aStorage) {
     switch (nsCSSProps::kTypeTable[aProperty]) {
       case eCSSType_Value: {
         const nsCSSValue *val = static_cast<const nsCSSValue*>(aStorage);
-        AppendCSSValueToString(aProperty, *val, aResult);
+        val->AppendToString(aProperty, aResult);
       } break;
       case eCSSType_Rect: {
         const nsCSSRect *rect = static_cast<const nsCSSRect*>(aStorage);
         const nsCSSUnit topUnit = rect->mTop.GetUnit();
         if (topUnit == eCSSUnit_Inherit ||
             topUnit == eCSSUnit_Initial ||
             topUnit == eCSSUnit_RectIsAuto) {
           NS_ASSERTION(rect->mRight.GetUnit() == topUnit &&
                        rect->mBottom.GetUnit() == topUnit &&
                        rect->mLeft.GetUnit() == topUnit,
                        "parser should make all sides have the same unit");
           if (topUnit == eCSSUnit_RectIsAuto)
             aResult.AppendLiteral("auto");
           else
-            AppendCSSValueToString(aProperty, rect->mTop, aResult);
+            rect->mTop.AppendToString(aProperty, aResult);
         } else {
           aResult.AppendLiteral("rect(");
-          AppendCSSValueToString(aProperty, rect->mTop, aResult);
+          rect->mTop.AppendToString(aProperty, aResult);
           NS_NAMED_LITERAL_STRING(comma, ", ");
           aResult.Append(comma);
-          AppendCSSValueToString(aProperty, rect->mRight, aResult);
+          rect->mRight.AppendToString(aProperty, aResult);
           aResult.Append(comma);
-          AppendCSSValueToString(aProperty, rect->mBottom, aResult);
+          rect->mBottom.AppendToString(aProperty, aResult);
           aResult.Append(comma);
-          AppendCSSValueToString(aProperty, rect->mLeft, aResult);
+          rect->mLeft.AppendToString(aProperty, aResult);
           aResult.Append(PRUnichar(')'));
         }
       } break;
       case eCSSType_ValuePair: {
         const nsCSSValuePair *pair = static_cast<const nsCSSValuePair*>(aStorage);
-        AppendCSSValueToString(aProperty, pair->mXValue, aResult);
+        pair->mXValue.AppendToString(aProperty, aResult);
         if (pair->mYValue != pair->mXValue ||
             ((aProperty == eCSSProperty_background_position ||
               aProperty == eCSSProperty__moz_transform_origin) &&
              pair->mXValue.GetUnit() != eCSSUnit_Inherit &&
              pair->mXValue.GetUnit() != eCSSUnit_Initial) ||
             (aProperty == eCSSProperty_background_size &&
              pair->mXValue.GetUnit() != eCSSUnit_Inherit &&
              pair->mXValue.GetUnit() != eCSSUnit_Initial &&
              pair->mXValue.GetUnit() != eCSSUnit_Enumerated)) {
           // Only output a Y value if it's different from the X value,
           // or if it's a background-position value other than 'initial'
           // or 'inherit', or if it's a -moz-transform-origin value other
           // than 'initial' or 'inherit', or if it's a background-size
           // value other than 'initial' or 'inherit' or 'contain' or 'cover'.
           aResult.Append(PRUnichar(' '));
-          AppendCSSValueToString(aProperty, pair->mYValue, aResult);
+          pair->mYValue.AppendToString(aProperty, aResult);
         }
       } break;
       case eCSSType_ValueList: {
         const nsCSSValueList* val =
             *static_cast<nsCSSValueList*const*>(aStorage);
         do {
-          AppendCSSValueToString(aProperty, val->mValue, aResult);
+          val->mValue.AppendToString(aProperty, aResult);
           val = val->mNext;
           if (val) {
             if (nsCSSProps::PropHasFlags(aProperty,
                                          CSS_PROPERTY_VALUE_LIST_USES_COMMAS))
               aResult.Append(PRUnichar(','));
             aResult.Append(PRUnichar(' '));
           }
         } while (val);
       } break;
       case eCSSType_ValuePairList: {
         const nsCSSValuePairList* item =
             *static_cast<nsCSSValuePairList*const*>(aStorage);
         do {
           NS_ASSERTION(item->mXValue.GetUnit() != eCSSUnit_Null,
                        "unexpected null unit");
-          AppendCSSValueToString(aProperty, item->mXValue, aResult);
+          item->mXValue.AppendToString(aProperty, aResult);
           if (item->mXValue.GetUnit() != eCSSUnit_Inherit &&
               item->mXValue.GetUnit() != eCSSUnit_Initial &&
               item->mYValue.GetUnit() != eCSSUnit_Null) {
             aResult.Append(PRUnichar(' '));
-            AppendCSSValueToString(aProperty, item->mYValue, aResult);
+            item->mYValue.AppendToString(aProperty, aResult);
           }
           item = item->mNext;
           if (item) {
             if (nsCSSProps::PropHasFlags(aProperty,
                                          CSS_PROPERTY_VALUE_LIST_USES_COMMAS))
               aResult.Append(PRUnichar(','));
             aResult.Append(PRUnichar(' '));
           }
         } while (item);
       } break;
     }
   }
   return aStorage != nsnull;
 }
 
-struct CSSValueSerializeCalcOps {
-  CSSValueSerializeCalcOps(nsCSSProperty aProperty, nsAString& aResult)
-    : mProperty(aProperty),
-      mResult(aResult)
-  {
-  }
-
-  typedef nsCSSValue input_type;
-  typedef nsCSSValue::Array input_array_type;
-
-  static nsCSSUnit GetUnit(const input_type& aValue) {
-    return aValue.GetUnit();
-  }
-
-  void Append(const char* aString)
-  {
-    mResult.AppendASCII(aString);
-  }
-
-  void AppendLeafValue(const input_type& aValue)
-  {
-    NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Percent ||
-                      aValue.IsLengthUnit(), "unexpected unit");
-    Declaration::AppendCSSValueToString(mProperty, aValue, mResult);
-  }
-
-  void AppendNumber(const input_type& aValue)
-  {
-    NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
-    Declaration::AppendCSSValueToString(mProperty, aValue, mResult);
-  }
-
-private:
-  nsCSSProperty mProperty;
-  nsAString &mResult;
-};
-
-/* static */ PRBool
-Declaration::AppendCSSValueToString(nsCSSProperty aProperty,
-                                    const nsCSSValue& aValue,
-                                    nsAString& aResult)
-{
-  nsCSSUnit unit = aValue.GetUnit();
-
-  if (eCSSUnit_Null == unit) {
-    return PR_FALSE;
-  }
-
-  if (eCSSUnit_String <= unit && unit <= eCSSUnit_Attr) {
-    if (unit == eCSSUnit_Attr) {
-      aResult.AppendLiteral("attr(");
-    }
-    nsAutoString  buffer;
-    aValue.GetStringValue(buffer);
-    if (unit == eCSSUnit_String) {
-      nsStyleUtil::AppendEscapedCSSString(buffer, aResult);
-    } else if (unit == eCSSUnit_Families) {
-      // XXX We really need to do *some* escaping.
-      aResult.Append(buffer);
-    } else {
-      nsStyleUtil::AppendEscapedCSSIdent(buffer, aResult);
-    }
-  }
-  else if (eCSSUnit_Array <= unit && unit <= eCSSUnit_Cubic_Bezier) {
-    switch (unit) {
-      case eCSSUnit_Counter:  aResult.AppendLiteral("counter(");  break;
-      case eCSSUnit_Counters: aResult.AppendLiteral("counters("); break;
-      case eCSSUnit_Cubic_Bezier: aResult.AppendLiteral("cubic-bezier("); break;
-      default: break;
-    }
-
-    nsCSSValue::Array *array = aValue.GetArrayValue();
-    PRBool mark = PR_FALSE;
-    for (size_t i = 0, i_end = array->Count(); i < i_end; ++i) {
-      if (aProperty == eCSSProperty_border_image && i >= 5) {
-        if (array->Item(i).GetUnit() == eCSSUnit_Null) {
-          continue;
-        }
-        if (i == 5) {
-          aResult.AppendLiteral(" /");
-        }
-      }
-      if (mark && array->Item(i).GetUnit() != eCSSUnit_Null) {
-        if (unit == eCSSUnit_Array &&
-            eCSSProperty_transition_timing_function != aProperty)
-          aResult.AppendLiteral(" ");
-        else
-          aResult.AppendLiteral(", ");
-      }
-      nsCSSProperty prop =
-        ((eCSSUnit_Counter <= unit && unit <= eCSSUnit_Counters) &&
-         i == array->Count() - 1)
-        ? eCSSProperty_list_style_type : aProperty;
-      if (AppendCSSValueToString(prop, array->Item(i), aResult)) {
-        mark = PR_TRUE;
-      }
-    }
-    if (eCSSUnit_Array == unit && aProperty == eCSSProperty_transition_timing_function) {
-      aResult.AppendLiteral(")");
-    }
-  }
-  /* Although Function is backed by an Array, we'll handle it separately
-   * because it's a bit quirky.
-   */
-  else if (eCSSUnit_Function == unit) {
-    const nsCSSValue::Array* array = aValue.GetArrayValue();
-    NS_ASSERTION(array->Count() >= 1, "Functions must have at least one element for the name.");
-
-    /* Append the function name. */
-    const nsCSSValue& functionName = array->Item(0);
-    if (functionName.GetUnit() == eCSSUnit_Enumerated) {
-      // We assume that the first argument is always of nsCSSKeyword type.
-      const nsCSSKeyword functionId =
-        static_cast<nsCSSKeyword>(functionName.GetIntValue());
-      nsStyleUtil::AppendEscapedCSSIdent(
-        NS_ConvertASCIItoUTF16(nsCSSKeywords::GetStringValue(functionId)),
-        aResult);
-    } else {
-      AppendCSSValueToString(aProperty, functionName, aResult);
-    }
-    aResult.AppendLiteral("(");
-
-    /* Now, step through the function contents, writing each of them as we go. */
-    for (size_t index = 1; index < array->Count(); ++index) {
-      AppendCSSValueToString(aProperty, array->Item(index), aResult);
-
-      /* If we're not at the final element, append a comma. */
-      if (index + 1 != array->Count())
-        aResult.AppendLiteral(", ");
-    }
-
-    /* Finally, append the closing parenthesis. */
-    aResult.AppendLiteral(")");
-  }
-  else if (aValue.IsCalcUnit()) {
-    NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Calc ||
-                      aValue.GetUnit() == eCSSUnit_Calc_Maximum ||
-                      aValue.GetUnit() == eCSSUnit_Calc_Minimum,
-                      "unexpected unit");
-    CSSValueSerializeCalcOps ops(aProperty, aResult);
-    css::SerializeCalc(aValue, ops);
-  }
-  else if (eCSSUnit_Integer == unit) {
-    nsAutoString tmpStr;
-    tmpStr.AppendInt(aValue.GetIntValue(), 10);
-    aResult.Append(tmpStr);
-  }
-  else if (eCSSUnit_Enumerated == unit) {
-    if (eCSSProperty_text_decoration == aProperty) {
-      PRInt32 intValue = aValue.GetIntValue();
-      if (NS_STYLE_TEXT_DECORATION_NONE == intValue) {
-        AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, intValue),
-                           aResult);
-      } else {
-        // Ignore the "override all" internal value.
-        // (It doesn't have a string representation.)
-        intValue &= ~NS_STYLE_TEXT_DECORATION_OVERRIDE_ALL;
-        nsStyleUtil::AppendBitmaskCSSValue(
-          aProperty, intValue,
-          NS_STYLE_TEXT_DECORATION_UNDERLINE,
-          NS_STYLE_TEXT_DECORATION_PREF_ANCHORS,
-          aResult);
-      }
-    }
-    else if (eCSSProperty_azimuth == aProperty) {
-      PRInt32 intValue = aValue.GetIntValue();
-      AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, (intValue & ~NS_STYLE_AZIMUTH_BEHIND)), aResult);
-      if ((NS_STYLE_AZIMUTH_BEHIND & intValue) != 0) {
-        aResult.Append(PRUnichar(' '));
-        AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, NS_STYLE_AZIMUTH_BEHIND), aResult);
-      }
-    }
-    else if (eCSSProperty_marks == aProperty) {
-      PRInt32 intValue = aValue.GetIntValue();
-      if (intValue == NS_STYLE_PAGE_MARKS_NONE) {
-        AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, intValue),
-                           aResult);
-      } else {
-        nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
-                                           NS_STYLE_PAGE_MARKS_CROP,
-                                           NS_STYLE_PAGE_MARKS_REGISTER,
-                                           aResult);
-      }
-    }
-    else {
-      const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, aValue.GetIntValue());
-      AppendASCIItoUTF16(name, aResult);
-    }
-  }
-  else if (eCSSUnit_EnumColor == unit) {
-    // we can lookup the property in the ColorTable and then
-    // get a string mapping the name
-    nsCAutoString str;
-    if (nsCSSProps::GetColorName(aValue.GetIntValue(), str)){
-      AppendASCIItoUTF16(str, aResult);
-    } else {
-      NS_NOTREACHED("bad color value");
-    }
-  }
-  else if (eCSSUnit_Color == unit) {
-    nscolor color = aValue.GetColorValue();
-    if (color == NS_RGBA(0, 0, 0, 0)) {
-      // Use the strictest match for 'transparent' so we do correct
-      // round-tripping of all other rgba() values.
-      aResult.AppendLiteral("transparent");
-    } else {
-      nsAutoString tmpStr;
-      PRUint8 a = NS_GET_A(color);
-      if (a < 255) {
-        tmpStr.AppendLiteral("rgba(");
-      } else {
-        tmpStr.AppendLiteral("rgb(");
-      }
-
-      NS_NAMED_LITERAL_STRING(comma, ", ");
-
-      tmpStr.AppendInt(NS_GET_R(color), 10);
-      tmpStr.Append(comma);
-      tmpStr.AppendInt(NS_GET_G(color), 10);
-      tmpStr.Append(comma);
-      tmpStr.AppendInt(NS_GET_B(color), 10);
-      if (a < 255) {
-        tmpStr.Append(comma);
-        tmpStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(a));
-      }
-      tmpStr.Append(PRUnichar(')'));
-
-      aResult.Append(tmpStr);
-    }
-  }
-  else if (eCSSUnit_URL == unit || eCSSUnit_Image == unit) {
-    aResult.Append(NS_LITERAL_STRING("url("));
-    nsStyleUtil::AppendEscapedCSSString(
-      nsDependentString(aValue.GetOriginalURLValue()), aResult);
-    aResult.Append(NS_LITERAL_STRING(")"));
-  }
-  else if (eCSSUnit_Percent == unit) {
-    nsAutoString tmpStr;
-    tmpStr.AppendFloat(aValue.GetPercentValue() * 100.0f);
-    aResult.Append(tmpStr);
-  }
-  else if (eCSSUnit_Percent < unit) {  // length unit
-    nsAutoString tmpStr;
-    tmpStr.AppendFloat(aValue.GetFloatValue());
-    aResult.Append(tmpStr);
-  }
-  else if (eCSSUnit_Gradient == unit) {
-    nsCSSValueGradient* gradient = aValue.GetGradientValue();
-
-    if (gradient->mIsRepeating) {
-      if (gradient->mIsRadial)
-        aResult.AppendLiteral("-moz-repeating-radial-gradient(");
-      else
-        aResult.AppendLiteral("-moz-repeating-linear-gradient(");
-    } else {
-      if (gradient->mIsRadial)
-        aResult.AppendLiteral("-moz-radial-gradient(");
-      else
-        aResult.AppendLiteral("-moz-linear-gradient(");
-    }
-
-    if (gradient->mBgPosX.GetUnit() != eCSSUnit_None ||
-        gradient->mBgPosY.GetUnit() != eCSSUnit_None ||
-        gradient->mAngle.GetUnit() != eCSSUnit_None) {
-      if (gradient->mBgPosX.GetUnit() != eCSSUnit_None) {
-        AppendCSSValueToString(eCSSProperty_background_position,
-                               gradient->mBgPosX, aResult);
-        aResult.AppendLiteral(" ");
-      }
-      if (gradient->mBgPosY.GetUnit() != eCSSUnit_None) {
-        AppendCSSValueToString(eCSSProperty_background_position,
-                               gradient->mBgPosY, aResult);
-        aResult.AppendLiteral(" ");
-      }
-      if (gradient->mAngle.GetUnit() != eCSSUnit_None) {
-        AppendCSSValueToString(aProperty, gradient->mAngle, aResult);
-      }
-      aResult.AppendLiteral(", ");
-    }
-
-    if (gradient->mIsRadial &&
-        (gradient->mRadialShape.GetUnit() != eCSSUnit_None ||
-         gradient->mRadialSize.GetUnit() != eCSSUnit_None)) {
-      if (gradient->mRadialShape.GetUnit() != eCSSUnit_None) {
-        NS_ASSERTION(gradient->mRadialShape.GetUnit() == eCSSUnit_Enumerated,
-                     "bad unit for radial gradient shape");
-        PRInt32 intValue = gradient->mRadialShape.GetIntValue();
-        NS_ASSERTION(intValue != NS_STYLE_GRADIENT_SHAPE_LINEAR,
-                     "radial gradient with linear shape?!");
-        AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue,
-                               nsCSSProps::kRadialGradientShapeKTable),
-                           aResult);
-        aResult.AppendLiteral(" ");
-      }
-
-      if (gradient->mRadialSize.GetUnit() != eCSSUnit_None) {
-        NS_ASSERTION(gradient->mRadialSize.GetUnit() == eCSSUnit_Enumerated,
-                     "bad unit for radial gradient size");
-        PRInt32 intValue = gradient->mRadialSize.GetIntValue();
-        AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue,
-                               nsCSSProps::kRadialGradientSizeKTable),
-                           aResult);
-      }
-      aResult.AppendLiteral(", ");
-    }
-
-    for (PRUint32 i = 0 ;;) {
-      AppendCSSValueToString(aProperty, gradient->mStops[i].mColor, aResult);
-      if (gradient->mStops[i].mLocation.GetUnit() != eCSSUnit_None) {
-        aResult.AppendLiteral(" ");
-        AppendCSSValueToString(aProperty, gradient->mStops[i].mLocation,
-                               aResult);
-      }
-      if (++i == gradient->mStops.Length()) {
-        break;
-      }
-      aResult.AppendLiteral(", ");
-    }
-
-    aResult.AppendLiteral(")");
-  }
-
-  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_All:          aResult.AppendLiteral("all"); break;
-    case eCSSUnit_Dummy:
-    case eCSSUnit_DummyInherit:
-    case eCSSUnit_RectIsAuto:
-      NS_NOTREACHED("should never serialize");
-      break;
-
-    case eCSSUnit_String:       break;
-    case eCSSUnit_Ident:        break;
-    case eCSSUnit_Families:     break;
-    case eCSSUnit_URL:          break;
-    case eCSSUnit_Image:        break;
-    case eCSSUnit_Array:        break;
-    case eCSSUnit_Attr:
-    case eCSSUnit_Cubic_Bezier:
-    case eCSSUnit_Counter:
-    case eCSSUnit_Counters:     aResult.Append(PRUnichar(')'));    break;
-    case eCSSUnit_Local_Font:   break;
-    case eCSSUnit_Font_Format:  break;
-    case eCSSUnit_Function:     break;
-    case eCSSUnit_Calc:         break;
-    case eCSSUnit_Calc_Plus:    break;
-    case eCSSUnit_Calc_Minus:   break;
-    case eCSSUnit_Calc_Times_L: break;
-    case eCSSUnit_Calc_Times_R: break;
-    case eCSSUnit_Calc_Divided: break;
-    case eCSSUnit_Calc_Minimum: break;
-    case eCSSUnit_Calc_Maximum: break;
-    case eCSSUnit_Integer:      break;
-    case eCSSUnit_Enumerated:   break;
-    case eCSSUnit_EnumColor:    break;
-    case eCSSUnit_Color:        break;
-    case eCSSUnit_Percent:      aResult.Append(PRUnichar('%'));    break;
-    case eCSSUnit_Number:       break;
-    case eCSSUnit_Gradient:     break;
-
-    case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
-    case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
-    case eCSSUnit_Centimeter:   aResult.AppendLiteral("cm");   break;
-    case eCSSUnit_Point:        aResult.AppendLiteral("pt");   break;
-    case eCSSUnit_Pica:         aResult.AppendLiteral("pc");   break;
-
-    case eCSSUnit_EM:           aResult.AppendLiteral("em");   break;
-    case eCSSUnit_XHeight:      aResult.AppendLiteral("ex");   break;
-    case eCSSUnit_Char:         aResult.AppendLiteral("ch");   break;
-    case eCSSUnit_RootEM:       aResult.AppendLiteral("rem");  break;
-
-    case eCSSUnit_Pixel:        aResult.AppendLiteral("px");   break;
-
-    case eCSSUnit_Degree:       aResult.AppendLiteral("deg");  break;
-    case eCSSUnit_Grad:         aResult.AppendLiteral("grad"); break;
-    case eCSSUnit_Radian:       aResult.AppendLiteral("rad");  break;
-
-    case eCSSUnit_Hertz:        aResult.AppendLiteral("Hz");   break;
-    case eCSSUnit_Kilohertz:    aResult.AppendLiteral("kHz");  break;
-
-    case eCSSUnit_Seconds:      aResult.Append(PRUnichar('s'));    break;
-    case eCSSUnit_Milliseconds: aResult.AppendLiteral("ms");   break;
-  }
-
-  return PR_TRUE;
-}
-
 nsresult
 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
 {
   aValue.Truncate(0);
 
   // simple properties are easy.
   if (!nsCSSProps::IsShorthand(aProperty)) {
     AppendValueToString(aProperty, aValue);
@@ -708,24 +314,22 @@ Declaration::GetValue(nsCSSProperty aPro
     }
   }
   if (importantCount != 0 && importantCount != totalCount) {
     // Case (3), no consistent importance.
     return NS_OK;
   }
   if (initialCount == totalCount) {
     // Simplify serialization below by serializing initial up-front.
-    AppendCSSValueToString(eCSSProperty_UNKNOWN, nsCSSValue(eCSSUnit_Initial),
-                           aValue);
+    nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue);
     return NS_OK;
   }
   if (inheritCount == totalCount) {
     // Simplify serialization below by serializing inherit up-front.
-    AppendCSSValueToString(eCSSProperty_UNKNOWN, nsCSSValue(eCSSUnit_Inherit),
-                           aValue);
+    nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue);
     return NS_OK;
   }
   if (initialCount != 0 || inheritCount != 0) {
     // Case (2): partially initial or inherit.
     return NS_OK;
   }
 
   nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
@@ -749,32 +353,32 @@ Declaration::GetValue(nsCSSProperty aPro
       NS_ASSERTION(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
                      kNotFound, "third subprop must be bottom");
       NS_ASSERTION(nsCSSProps::GetStringValue(subprops[3]).Find("-left") !=
                      kNotFound, "fourth subprop must be left");
       const nsCSSValue &topValue = *data->ValueStorageFor(subprops[0]);
       const nsCSSValue &rightValue = *data->ValueStorageFor(subprops[1]);
       const nsCSSValue &bottomValue = *data->ValueStorageFor(subprops[2]);
       const nsCSSValue &leftValue = *data->ValueStorageFor(subprops[3]);
-      PRBool haveValue;
-      haveValue = AppendCSSValueToString(subprops[0], topValue, aValue);
-      NS_ASSERTION(haveValue, "should have bailed before");
+
+      NS_ASSERTION(topValue.GetUnit() != eCSSUnit_Null, "null top");
+      topValue.AppendToString(subprops[0], aValue);
       if (topValue != rightValue || topValue != leftValue ||
           topValue != bottomValue) {
         aValue.Append(PRUnichar(' '));
-        haveValue = AppendCSSValueToString(subprops[1], rightValue, aValue);
-        NS_ASSERTION(haveValue, "should have bailed before");
+        NS_ASSERTION(rightValue.GetUnit() != eCSSUnit_Null, "null right");
+        rightValue.AppendToString(subprops[1], aValue);
         if (topValue != bottomValue || rightValue != leftValue) {
           aValue.Append(PRUnichar(' '));
-          haveValue = AppendCSSValueToString(subprops[2], bottomValue, aValue);
-          NS_ASSERTION(haveValue, "should have bailed before");
+          NS_ASSERTION(bottomValue.GetUnit() != eCSSUnit_Null, "null bottom");
+          bottomValue.AppendToString(subprops[2], aValue);
           if (rightValue != leftValue) {
             aValue.Append(PRUnichar(' '));
-            haveValue = AppendCSSValueToString(subprops[3], leftValue, aValue);
-            NS_ASSERTION(haveValue, "should have bailed before");
+            NS_ASSERTION(leftValue.GetUnit() != eCSSUnit_Null, "null left");
+            leftValue.AppendToString(subprops[3], aValue);
           }
         }
       }
       break;
     }
     case eCSSProperty__moz_border_radius: 
     case eCSSProperty__moz_outline_radius: {
       const nsCSSProperty* subprops =
@@ -786,38 +390,38 @@ Declaration::GetValue(nsCSSProperty aPro
                    "type mismatch");
       const nsCSSValuePair* vals[4] = {
         data->ValuePairStorageFor(subprops[0]),
         data->ValuePairStorageFor(subprops[1]),
         data->ValuePairStorageFor(subprops[2]),
         data->ValuePairStorageFor(subprops[3])
       };
 
-      AppendCSSValueToString(aProperty, vals[0]->mXValue, aValue);
+      vals[0]->mXValue.AppendToString(subprops[0], aValue);
       aValue.Append(PRUnichar(' '));
-      AppendCSSValueToString(aProperty, vals[1]->mXValue, aValue);
+      vals[1]->mXValue.AppendToString(subprops[1], aValue);
       aValue.Append(PRUnichar(' '));
-      AppendCSSValueToString(aProperty, vals[2]->mXValue, aValue);
+      vals[2]->mXValue.AppendToString(subprops[2], aValue);
       aValue.Append(PRUnichar(' '));
-      AppendCSSValueToString(aProperty, vals[3]->mXValue, aValue);
-        
+      vals[3]->mXValue.AppendToString(subprops[3], aValue);
+
       // For compatibility, only write a slash and the y-values
       // if they're not identical to the x-values.
       if (vals[0]->mXValue != vals[0]->mYValue ||
           vals[1]->mXValue != vals[1]->mYValue ||
           vals[2]->mXValue != vals[2]->mYValue ||
           vals[3]->mXValue != vals[3]->mYValue) {
         aValue.AppendLiteral(" / ");
-        AppendCSSValueToString(aProperty, vals[0]->mYValue, aValue);
+        vals[0]->mYValue.AppendToString(subprops[0], aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(aProperty, vals[1]->mYValue, aValue);
+        vals[1]->mYValue.AppendToString(subprops[1], aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(aProperty, vals[2]->mYValue, aValue);
+        vals[2]->mYValue.AppendToString(subprops[2], aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(aProperty, vals[3]->mYValue, aValue);
+        vals[3]->mYValue.AppendToString(subprops[3], aValue);
       }
       break;
     }
     case eCSSProperty_border: {
       const nsCSSProperty* subproptables[3] = {
         nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color),
         nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style),
         nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width)
@@ -928,30 +532,28 @@ Declaration::GetValue(nsCSSProperty aPro
         * data->ValuePairListStorageFor(eCSSProperty_background_size);
       for (;;) {
         if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
             size->mYValue.GetUnit() != eCSSUnit_Auto) {
           // Non-default background-size, so can't be serialized as shorthand.
           aValue.Truncate();
           return NS_OK;
         }
-        AppendCSSValueToString(eCSSProperty_background_image,
-                               image->mValue, aValue);
+        image->mValue.AppendToString(eCSSProperty_background_image, aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(eCSSProperty_background_repeat,
-                               repeat->mValue, aValue);
+        repeat->mValue.AppendToString(eCSSProperty_background_repeat, aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(eCSSProperty_background_attachment,
-                               attachment->mValue, aValue);
+        attachment->mValue.AppendToString(eCSSProperty_background_attachment,
+                                          aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(eCSSProperty_background_position,
-                               position->mXValue, aValue);
+        position->mXValue.AppendToString(eCSSProperty_background_position,
+                                         aValue);
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(eCSSProperty_background_position,
-                               position->mYValue, aValue);
+        position->mYValue.AppendToString(eCSSProperty_background_position,
+                                         aValue);
         NS_ASSERTION(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
                      origin->mValue.GetUnit() == eCSSUnit_Enumerated,
                      "should not be inherit/initial within list and "
                      "should have returned early for real inherit/initial");
         if (clip->mValue.GetIntValue() != NS_STYLE_BG_CLIP_BORDER ||
             origin->mValue.GetIntValue() != NS_STYLE_BG_ORIGIN_PADDING) {
           PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER ==
                            NS_STYLE_BG_ORIGIN_BORDER);
@@ -964,18 +566,17 @@ Declaration::GetValue(nsCSSProperty aPro
           // non-default), we can't represent the state using the
           // shorthand.
           if (clip->mValue != origin->mValue) {
             aValue.Truncate();
             return NS_OK;
           }
 
           aValue.Append(PRUnichar(' '));
-          AppendCSSValueToString(eCSSProperty_background_clip,
-                                 clip->mValue, aValue);
+          clip->mValue.AppendToString(eCSSProperty_background_clip, aValue);
         }
 
         image = image->mNext;
         repeat = repeat->mNext;
         attachment = attachment->mNext;
         position = position->mNext;
         clip = clip->mNext;
         origin = origin->mNext;
@@ -1047,70 +648,69 @@ Declaration::GetValue(nsCSSProperty aPro
             family.GetUnit() != eCSSUnit_System_Font ||
             stretch.GetUnit() != eCSSUnit_System_Font ||
             sizeAdjust.GetUnit() != eCSSUnit_System_Font ||
             featureSettings.GetUnit() != eCSSUnit_System_Font ||
             languageOverride.GetUnit() != eCSSUnit_System_Font) {
           // This can't be represented as a shorthand.
           return NS_OK;
         }
-        AppendCSSValueToString(eCSSProperty__x_system_font, *systemFont,
-                               aValue);
+        systemFont->AppendToString(eCSSProperty__x_system_font, aValue);
       } else {
         // The font-stretch, font-size-adjust,
         // -moz-font-feature-settings, and -moz-font-language-override
         // properties are reset by this shorthand property to their
         // initial values, but can't be represented in its syntax.
         if (stretch.GetUnit() != eCSSUnit_Enumerated ||
             stretch.GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL ||
             sizeAdjust.GetUnit() != eCSSUnit_None ||
             featureSettings.GetUnit() != eCSSUnit_Normal ||
             languageOverride.GetUnit() != eCSSUnit_Normal) {
           return NS_OK;
         }
 
         if (style.GetUnit() != eCSSUnit_Enumerated ||
             style.GetIntValue() != NS_FONT_STYLE_NORMAL) {
-          AppendCSSValueToString(eCSSProperty_font_style, style, aValue);
+          style.AppendToString(eCSSProperty_font_style, aValue);
           aValue.Append(PRUnichar(' '));
         }
         if (variant.GetUnit() != eCSSUnit_Enumerated ||
             variant.GetIntValue() != NS_FONT_VARIANT_NORMAL) {
-          AppendCSSValueToString(eCSSProperty_font_variant, variant, aValue);
+          variant.AppendToString(eCSSProperty_font_variant, aValue);
           aValue.Append(PRUnichar(' '));
         }
         if (weight.GetUnit() != eCSSUnit_Enumerated ||
             weight.GetIntValue() != NS_FONT_WEIGHT_NORMAL) {
-          AppendCSSValueToString(eCSSProperty_font_weight, weight, aValue);
+          weight.AppendToString(eCSSProperty_font_weight, aValue);
           aValue.Append(PRUnichar(' '));
         }
-        AppendCSSValueToString(eCSSProperty_font_size, size, aValue);
+        size.AppendToString(eCSSProperty_font_size, aValue);
         if (lh.GetUnit() != eCSSUnit_Normal) {
           aValue.Append(PRUnichar('/'));
-          AppendCSSValueToString(eCSSProperty_line_height, lh, aValue);
+          lh.AppendToString(eCSSProperty_line_height, aValue);
         }
         aValue.Append(PRUnichar(' '));
-        AppendCSSValueToString(eCSSProperty_font_family, family, aValue);
+        family.AppendToString(eCSSProperty_font_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(' '));
       AppendValueToString(eCSSProperty_list_style_image, aValue);
       break;
     case eCSSProperty_overflow: {
       const nsCSSValue &xValue =
         *data->ValueStorageFor(eCSSProperty_overflow_x);
       const nsCSSValue &yValue =
         *data->ValueStorageFor(eCSSProperty_overflow_y);
       if (xValue == yValue)
-        AppendCSSValueToString(eCSSProperty_overflow_x, xValue, aValue);
+        xValue.AppendToString(eCSSProperty_overflow_x, aValue);
       break;
     }
     case eCSSProperty_pause: {
       if (AppendValueToString(eCSSProperty_pause_before, aValue)) {
         aValue.Append(PRUnichar(' '));
         if (!AppendValueToString(eCSSProperty_pause_after, aValue))
           aValue.Truncate();
       }
@@ -1130,17 +730,17 @@ Declaration::GetValue(nsCSSProperty aPro
 #endif
       const nsCSSValueList* val[NUM_TRANSITION_SUBPROPS];
       for (int i = 0; i < NUM_TRANSITION_SUBPROPS; ++i) {
         val[i] = *data->ValueListStorageFor(subprops[i]);
       }
       // Merge the lists of the subproperties into a single list.
       for (;;) {
         for (int i = 0; i < NUM_TRANSITION_SUBPROPS; ++i) {
-          AppendCSSValueToString(subprops[i], val[i]->mValue, aValue);
+          val[i]->mValue.AppendToString(subprops[i], aValue);
           aValue.Append(PRUnichar(' '));
           val[i] = val[i]->mNext;
         }
         // Remove the last space.
         aValue.Truncate(aValue.Length() - 1);
 
         PR_STATIC_ASSERT(NUM_TRANSITION_SUBPROPS == 4);
         if (!val[0] || !val[1] || !val[2] || !val[3]) {
@@ -1295,18 +895,17 @@ Declaration::ToString(nsAString& aString
       NS_ASSERTION(shorthand != eCSSProperty_font ||
                    *(shorthands + 1) == eCSSProperty_UNKNOWN,
                    "font should always be the only containing shorthand");
       if (shorthand == eCSSProperty_font) {
         if (haveSystemFont && !didSystemFont) {
           // Output the shorthand font declaration that we will
           // partially override later.  But don't add it to
           // |shorthandsUsed|, since we will have to override it.
-          AppendCSSValueToString(eCSSProperty__x_system_font, *systemFont,
-                                 value);
+          systemFont->AppendToString(eCSSProperty__x_system_font, value);
           AppendPropertyAndValueToString(eCSSProperty_font, value, aString);
           value.Truncate();
           didSystemFont = PR_TRUE;
         }
 
         // That we output the system font is enough for this property if:
         //   (1) it's the hidden system font subproperty (which either
         //       means we output it or we don't have it), or
--- a/layout/style/Declaration.h
+++ b/layout/style/Declaration.h
@@ -182,21 +182,16 @@ public:
     mImportantData = nsnull;
     mOrder.Clear();
   }
 
 #ifdef DEBUG
   void List(FILE* out = stdout, PRInt32 aIndent = 0) const;
 #endif
 
-  // return whether there was a value in |aValue| (i.e., it had a non-null unit)
-  static PRBool AppendCSSValueToString(nsCSSProperty aProperty,
-                                       const nsCSSValue& aValue,
-                                       nsAString& aResult);
-
   // return whether there was a value in |aStorage| (i.e., it was non-null)
   static PRBool AppendStorageToString(nsCSSProperty aProperty,
                                       const void* aStorage,
                                       nsAString& aResult);
 
 private:
   // Not implemented, and not supported.
   Declaration& operator=(const Declaration& aCopy);
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -68,18 +68,16 @@
 
 #include "nsContentUtils.h"
 #include "nsStyleConsts.h"
 #include "nsDOMError.h"
 #include "nsStyleUtil.h"
 #include "mozilla/css/Declaration.h"
 #include "nsPrintfCString.h"
 
-namespace css = mozilla::css;
-
 #define IMPL_STYLE_RULE_INHERIT(_class, super) \
 /* virtual */ already_AddRefed<nsIStyleSheet> _class::GetStyleSheet() const { return super::GetStyleSheet(); }  \
 /* virtual */ void _class::SetStyleSheet(nsCSSStyleSheet* aSheet) { super::SetStyleSheet(aSheet); }  \
 /* virtual */ void _class::SetParentRule(nsICSSGroupRule* aRule) { super::SetParentRule(aRule); }  \
 nsIDOMCSSRule* _class::GetDOMRuleWeak(nsresult *aResult) { *aResult = NS_OK; return this; }  \
 /* virtual */ void _class::MapRuleInfoInto(nsRuleData* aRuleData) { }
 
 #define IMPL_STYLE_RULE_INHERIT2(_class, super) \
@@ -1609,38 +1607,35 @@ nsCSSFontFaceStyleDecl::GetPropertyValue
 
   if (val.GetUnit() == eCSSUnit_Null) {
     // Avoid having to check no-value in the Family and Src cases below.
     return NS_OK;
   }
 
   switch (aFontDescID) {
   case eCSSFontDesc_Family: {
-      // we don't use AppendCSSValueToString here because it doesn't
+      // we don't use nsCSSValue::AppendToString here because it doesn't
       // canonicalize the way we want, and anyway it's overkill when
       // we know we have eCSSUnit_String
       NS_ASSERTION(val.GetUnit() == eCSSUnit_String, "unexpected unit");
       nsDependentString family(val.GetStringBufferValue());
       nsStyleUtil::AppendEscapedCSSString(family, aResult);
       return NS_OK;
     }
 
   case eCSSFontDesc_Style:
-    css::Declaration::AppendCSSValueToString(eCSSProperty_font_style, val,
-                                             aResult);
+    val.AppendToString(eCSSProperty_font_style, aResult);
     return NS_OK;
 
   case eCSSFontDesc_Weight:
-    css::Declaration::AppendCSSValueToString(eCSSProperty_font_weight, val,
-                                             aResult);
+    val.AppendToString(eCSSProperty_font_weight, aResult);
     return NS_OK;
-    
+
   case eCSSFontDesc_Stretch:
-    css::Declaration::AppendCSSValueToString(eCSSProperty_font_stretch, val,
-                                             aResult);
+    val.AppendToString(eCSSProperty_font_stretch, aResult);
     return NS_OK;
 
   case eCSSFontDesc_Src:
     AppendSerializedFontSrc(val, aResult);
     return NS_OK;
 
   case eCSSFontDesc_UnicodeRange:
     AppendSerializedUnicodeRange(val, aResult);
--- a/layout/style/nsCSSStyleSheet.cpp
+++ b/layout/style/nsCSSStyleSheet.cpp
@@ -407,43 +407,39 @@ nsMediaQuery::AppendToString(nsAString& 
 
     if (expr.mValue.GetUnit() != eCSSUnit_Null) {
       aString.AppendLiteral(": ");
       switch (feature->mValueType) {
         case nsMediaFeature::eLength:
           NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit");
           // Use 'width' as a property that takes length values
           // written in the normal way.
-          css::Declaration::AppendCSSValueToString(eCSSProperty_width,
-                                                   expr.mValue, aString);
+          expr.mValue.AppendToString(eCSSProperty_width, aString);
           break;
         case nsMediaFeature::eInteger:
         case nsMediaFeature::eBoolInteger:
           NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer,
                        "bad unit");
           // Use 'z-index' as a property that takes integer values
           // written without anything extra.
-          css::Declaration::AppendCSSValueToString(eCSSProperty_z_index,
-                                                   expr.mValue, aString);
+          expr.mValue.AppendToString(eCSSProperty_z_index, aString);
           break;
         case nsMediaFeature::eIntRatio:
           {
             NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array,
                          "bad unit");
             nsCSSValue::Array *array = expr.mValue.GetArrayValue();
             NS_ASSERTION(array->Count() == 2, "unexpected length");
             NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
                          "bad unit");
             NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer,
                          "bad unit");
-            css::Declaration::AppendCSSValueToString(eCSSProperty_z_index,
-                                                     array->Item(0), aString);
+            array->Item(0).AppendToString(eCSSProperty_z_index, aString);
             aString.AppendLiteral("/");
-            css::Declaration::AppendCSSValueToString(eCSSProperty_z_index,
-                                                     array->Item(1), aString);
+            array->Item(1).AppendToString(eCSSProperty_z_index, aString);
           }
           break;
         case nsMediaFeature::eResolution:
           {
             nsAutoString buffer;
             buffer.AppendFloat(expr.mValue.GetFloatValue());
             aString.Append(buffer);
             buffer.Truncate();
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -41,19 +41,21 @@
 #include "nsString.h"
 #include "nsCSSProps.h"
 #include "nsReadableUtils.h"
 #include "imgIRequest.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsIPrincipal.h"
 #include "nsMathUtils.h"
+#include "nsStyleUtil.h"
+#include "CSSCalc.h"
+#include "prenv.h"  // for paint forcing
 
-// Paint forcing
-#include "prenv.h"
+namespace css = mozilla::css;
 
 nsCSSValue::nsCSSValue(PRInt32 aValue, nsCSSUnit aUnit)
   : mUnit(aUnit)
 {
   NS_ASSERTION(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated ||
                aUnit == eCSSUnit_EnumColor, "not an int value");
   if (aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated ||
       aUnit == eCSSUnit_EnumColor) {
@@ -488,16 +490,415 @@ nsCSSValue::BufferFromString(const nsStr
     nsCharTraits<PRUnichar>::copy(data, aValue.get(), length);
     // Null-terminate.
     data[length] = 0;
   }
 
   return buffer;
 }
 
+namespace {
+
+struct CSSValueSerializeCalcOps {
+  CSSValueSerializeCalcOps(nsCSSProperty aProperty, nsAString& aResult)
+    : mProperty(aProperty),
+      mResult(aResult)
+  {
+  }
+
+  typedef nsCSSValue input_type;
+  typedef nsCSSValue::Array input_array_type;
+
+  static nsCSSUnit GetUnit(const input_type& aValue) {
+    return aValue.GetUnit();
+  }
+
+  void Append(const char* aString)
+  {
+    mResult.AppendASCII(aString);
+  }
+
+  void AppendLeafValue(const input_type& aValue)
+  {
+    NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Percent ||
+                      aValue.IsLengthUnit(), "unexpected unit");
+    aValue.AppendToString(mProperty, mResult);
+  }
+
+  void AppendNumber(const input_type& aValue)
+  {
+    NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
+    aValue.AppendToString(mProperty, mResult);
+  }
+
+private:
+  nsCSSProperty mProperty;
+  nsAString &mResult;
+};
+
+} // anonymous namespace
+
+void
+nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
+{
+  // eCSSProperty_UNKNOWN gets used for some recursive calls below.
+  NS_ABORT_IF_FALSE((0 <= aProperty &&
+                     aProperty <= eCSSProperty_COUNT_no_shorthands) ||
+                    aProperty == eCSSProperty_UNKNOWN,
+                    "property ID out of range");
+
+  nsCSSUnit unit = GetUnit();
+  if (unit == eCSSUnit_Null) {
+    return;
+  }
+
+  if (eCSSUnit_String <= unit && unit <= eCSSUnit_Attr) {
+    if (unit == eCSSUnit_Attr) {
+      aResult.AppendLiteral("attr(");
+    }
+    nsAutoString  buffer;
+    GetStringValue(buffer);
+    if (unit == eCSSUnit_String) {
+      nsStyleUtil::AppendEscapedCSSString(buffer, aResult);
+    } else if (unit == eCSSUnit_Families) {
+      // XXX We really need to do *some* escaping.
+      aResult.Append(buffer);
+    } else {
+      nsStyleUtil::AppendEscapedCSSIdent(buffer, aResult);
+    }
+  }
+  else if (eCSSUnit_Array <= unit && unit <= eCSSUnit_Cubic_Bezier) {
+    switch (unit) {
+      case eCSSUnit_Counter:  aResult.AppendLiteral("counter(");  break;
+      case eCSSUnit_Counters: aResult.AppendLiteral("counters("); break;
+      case eCSSUnit_Cubic_Bezier: aResult.AppendLiteral("cubic-bezier("); break;
+      default: break;
+    }
+
+    nsCSSValue::Array *array = GetArrayValue();
+    PRBool mark = PR_FALSE;
+    for (size_t i = 0, i_end = array->Count(); i < i_end; ++i) {
+      if (aProperty == eCSSProperty_border_image && i >= 5) {
+        if (array->Item(i).GetUnit() == eCSSUnit_Null) {
+          continue;
+        }
+        if (i == 5) {
+          aResult.AppendLiteral(" /");
+        }
+      }
+      if (mark && array->Item(i).GetUnit() != eCSSUnit_Null) {
+        if (unit == eCSSUnit_Array &&
+            eCSSProperty_transition_timing_function != aProperty)
+          aResult.AppendLiteral(" ");
+        else
+          aResult.AppendLiteral(", ");
+      }
+      nsCSSProperty prop =
+        ((eCSSUnit_Counter <= unit && unit <= eCSSUnit_Counters) &&
+         i == array->Count() - 1)
+        ? eCSSProperty_list_style_type : aProperty;
+      if (array->Item(i).GetUnit() != eCSSUnit_Null) {
+        array->Item(i).AppendToString(prop, aResult);
+        mark = PR_TRUE;
+      }
+    }
+    if (eCSSUnit_Array == unit &&
+        aProperty == eCSSProperty_transition_timing_function) {
+      aResult.AppendLiteral(")");
+    }
+  }
+  /* Although Function is backed by an Array, we'll handle it separately
+   * because it's a bit quirky.
+   */
+  else if (eCSSUnit_Function == unit) {
+    const nsCSSValue::Array* array = GetArrayValue();
+    NS_ASSERTION(array->Count() >= 1, "Functions must have at least one element for the name.");
+
+    /* Append the function name. */
+    const nsCSSValue& functionName = array->Item(0);
+    if (functionName.GetUnit() == eCSSUnit_Enumerated) {
+      // We assume that the first argument is always of nsCSSKeyword type.
+      const nsCSSKeyword functionId =
+        static_cast<nsCSSKeyword>(functionName.GetIntValue());
+      nsStyleUtil::AppendEscapedCSSIdent(
+        NS_ConvertASCIItoUTF16(nsCSSKeywords::GetStringValue(functionId)),
+        aResult);
+    } else {
+      functionName.AppendToString(aProperty, aResult);
+    }
+    aResult.AppendLiteral("(");
+
+    /* Now, step through the function contents, writing each of them as we go. */
+    for (size_t index = 1; index < array->Count(); ++index) {
+      array->Item(index).AppendToString(aProperty, aResult);
+
+      /* If we're not at the final element, append a comma. */
+      if (index + 1 != array->Count())
+        aResult.AppendLiteral(", ");
+    }
+
+    /* Finally, append the closing parenthesis. */
+    aResult.AppendLiteral(")");
+  }
+  else if (IsCalcUnit()) {
+    NS_ABORT_IF_FALSE(GetUnit() == eCSSUnit_Calc ||
+                      GetUnit() == eCSSUnit_Calc_Maximum ||
+                      GetUnit() == eCSSUnit_Calc_Minimum,
+                      "unexpected unit");
+    CSSValueSerializeCalcOps ops(aProperty, aResult);
+    css::SerializeCalc(*this, ops);
+  }
+  else if (eCSSUnit_Integer == unit) {
+    nsAutoString tmpStr;
+    tmpStr.AppendInt(GetIntValue(), 10);
+    aResult.Append(tmpStr);
+  }
+  else if (eCSSUnit_Enumerated == unit) {
+    if (eCSSProperty_text_decoration == aProperty) {
+      PRInt32 intValue = GetIntValue();
+      if (NS_STYLE_TEXT_DECORATION_NONE == intValue) {
+        AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, intValue),
+                           aResult);
+      } else {
+        // Ignore the "override all" internal value.
+        // (It doesn't have a string representation.)
+        intValue &= ~NS_STYLE_TEXT_DECORATION_OVERRIDE_ALL;
+        nsStyleUtil::AppendBitmaskCSSValue(
+          aProperty, intValue,
+          NS_STYLE_TEXT_DECORATION_UNDERLINE,
+          NS_STYLE_TEXT_DECORATION_PREF_ANCHORS,
+          aResult);
+      }
+    }
+    else if (eCSSProperty_azimuth == aProperty) {
+      PRInt32 intValue = GetIntValue();
+      AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, (intValue & ~NS_STYLE_AZIMUTH_BEHIND)), aResult);
+      if ((NS_STYLE_AZIMUTH_BEHIND & intValue) != 0) {
+        aResult.Append(PRUnichar(' '));
+        AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, NS_STYLE_AZIMUTH_BEHIND), aResult);
+      }
+    }
+    else if (eCSSProperty_marks == aProperty) {
+      PRInt32 intValue = GetIntValue();
+      if (intValue == NS_STYLE_PAGE_MARKS_NONE) {
+        AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, intValue),
+                           aResult);
+      } else {
+        nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
+                                           NS_STYLE_PAGE_MARKS_CROP,
+                                           NS_STYLE_PAGE_MARKS_REGISTER,
+                                           aResult);
+      }
+    }
+    else {
+      const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, GetIntValue());
+      AppendASCIItoUTF16(name, aResult);
+    }
+  }
+  else if (eCSSUnit_EnumColor == unit) {
+    // we can lookup the property in the ColorTable and then
+    // get a string mapping the name
+    nsCAutoString str;
+    if (nsCSSProps::GetColorName(GetIntValue(), str)){
+      AppendASCIItoUTF16(str, aResult);
+    } else {
+      NS_NOTREACHED("bad color value");
+    }
+  }
+  else if (eCSSUnit_Color == unit) {
+    nscolor color = GetColorValue();
+    if (color == NS_RGBA(0, 0, 0, 0)) {
+      // Use the strictest match for 'transparent' so we do correct
+      // round-tripping of all other rgba() values.
+      aResult.AppendLiteral("transparent");
+    } else {
+      nsAutoString tmpStr;
+      PRUint8 a = NS_GET_A(color);
+      if (a < 255) {
+        tmpStr.AppendLiteral("rgba(");
+      } else {
+        tmpStr.AppendLiteral("rgb(");
+      }
+
+      NS_NAMED_LITERAL_STRING(comma, ", ");
+
+      tmpStr.AppendInt(NS_GET_R(color), 10);
+      tmpStr.Append(comma);
+      tmpStr.AppendInt(NS_GET_G(color), 10);
+      tmpStr.Append(comma);
+      tmpStr.AppendInt(NS_GET_B(color), 10);
+      if (a < 255) {
+        tmpStr.Append(comma);
+        tmpStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(a));
+      }
+      tmpStr.Append(PRUnichar(')'));
+
+      aResult.Append(tmpStr);
+    }
+  }
+  else if (eCSSUnit_URL == unit || eCSSUnit_Image == unit) {
+    aResult.Append(NS_LITERAL_STRING("url("));
+    nsStyleUtil::AppendEscapedCSSString(
+      nsDependentString(GetOriginalURLValue()), aResult);
+    aResult.Append(NS_LITERAL_STRING(")"));
+  }
+  else if (eCSSUnit_Percent == unit) {
+    nsAutoString tmpStr;
+    tmpStr.AppendFloat(GetPercentValue() * 100.0f);
+    aResult.Append(tmpStr);
+  }
+  else if (eCSSUnit_Percent < unit) {  // length unit
+    nsAutoString tmpStr;
+    tmpStr.AppendFloat(GetFloatValue());
+    aResult.Append(tmpStr);
+  }
+  else if (eCSSUnit_Gradient == unit) {
+    nsCSSValueGradient* gradient = GetGradientValue();
+
+    if (gradient->mIsRepeating) {
+      if (gradient->mIsRadial)
+        aResult.AppendLiteral("-moz-repeating-radial-gradient(");
+      else
+        aResult.AppendLiteral("-moz-repeating-linear-gradient(");
+    } else {
+      if (gradient->mIsRadial)
+        aResult.AppendLiteral("-moz-radial-gradient(");
+      else
+        aResult.AppendLiteral("-moz-linear-gradient(");
+    }
+
+    if (gradient->mBgPosX.GetUnit() != eCSSUnit_None ||
+        gradient->mBgPosY.GetUnit() != eCSSUnit_None ||
+        gradient->mAngle.GetUnit() != eCSSUnit_None) {
+      if (gradient->mBgPosX.GetUnit() != eCSSUnit_None) {
+        gradient->mBgPosX.AppendToString(eCSSProperty_background_position,
+                                         aResult);
+        aResult.AppendLiteral(" ");
+      }
+      if (gradient->mBgPosY.GetUnit() != eCSSUnit_None) {
+        gradient->mBgPosY.AppendToString(eCSSProperty_background_position,
+                                         aResult);
+        aResult.AppendLiteral(" ");
+      }
+      if (gradient->mAngle.GetUnit() != eCSSUnit_None) {
+        gradient->mAngle.AppendToString(aProperty, aResult);
+      }
+      aResult.AppendLiteral(", ");
+    }
+
+    if (gradient->mIsRadial &&
+        (gradient->mRadialShape.GetUnit() != eCSSUnit_None ||
+         gradient->mRadialSize.GetUnit() != eCSSUnit_None)) {
+      if (gradient->mRadialShape.GetUnit() != eCSSUnit_None) {
+        NS_ASSERTION(gradient->mRadialShape.GetUnit() == eCSSUnit_Enumerated,
+                     "bad unit for radial gradient shape");
+        PRInt32 intValue = gradient->mRadialShape.GetIntValue();
+        NS_ASSERTION(intValue != NS_STYLE_GRADIENT_SHAPE_LINEAR,
+                     "radial gradient with linear shape?!");
+        AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue,
+                               nsCSSProps::kRadialGradientShapeKTable),
+                           aResult);
+        aResult.AppendLiteral(" ");
+      }
+
+      if (gradient->mRadialSize.GetUnit() != eCSSUnit_None) {
+        NS_ASSERTION(gradient->mRadialSize.GetUnit() == eCSSUnit_Enumerated,
+                     "bad unit for radial gradient size");
+        PRInt32 intValue = gradient->mRadialSize.GetIntValue();
+        AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue,
+                               nsCSSProps::kRadialGradientSizeKTable),
+                           aResult);
+      }
+      aResult.AppendLiteral(", ");
+    }
+
+    for (PRUint32 i = 0 ;;) {
+      gradient->mStops[i].mColor.AppendToString(aProperty, aResult);
+      if (gradient->mStops[i].mLocation.GetUnit() != eCSSUnit_None) {
+        aResult.AppendLiteral(" ");
+        gradient->mStops[i].mLocation.AppendToString(aProperty, aResult);
+      }
+      if (++i == gradient->mStops.Length()) {
+        break;
+      }
+      aResult.AppendLiteral(", ");
+    }
+
+    aResult.AppendLiteral(")");
+  }
+
+  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_All:          aResult.AppendLiteral("all"); break;
+    case eCSSUnit_Dummy:
+    case eCSSUnit_DummyInherit:
+    case eCSSUnit_RectIsAuto:
+      NS_NOTREACHED("should never serialize");
+      break;
+
+    case eCSSUnit_String:       break;
+    case eCSSUnit_Ident:        break;
+    case eCSSUnit_Families:     break;
+    case eCSSUnit_URL:          break;
+    case eCSSUnit_Image:        break;
+    case eCSSUnit_Array:        break;
+    case eCSSUnit_Attr:
+    case eCSSUnit_Cubic_Bezier:
+    case eCSSUnit_Counter:
+    case eCSSUnit_Counters:     aResult.Append(PRUnichar(')'));    break;
+    case eCSSUnit_Local_Font:   break;
+    case eCSSUnit_Font_Format:  break;
+    case eCSSUnit_Function:     break;
+    case eCSSUnit_Calc:         break;
+    case eCSSUnit_Calc_Plus:    break;
+    case eCSSUnit_Calc_Minus:   break;
+    case eCSSUnit_Calc_Times_L: break;
+    case eCSSUnit_Calc_Times_R: break;
+    case eCSSUnit_Calc_Divided: break;
+    case eCSSUnit_Calc_Minimum: break;
+    case eCSSUnit_Calc_Maximum: break;
+    case eCSSUnit_Integer:      break;
+    case eCSSUnit_Enumerated:   break;
+    case eCSSUnit_EnumColor:    break;
+    case eCSSUnit_Color:        break;
+    case eCSSUnit_Percent:      aResult.Append(PRUnichar('%'));    break;
+    case eCSSUnit_Number:       break;
+    case eCSSUnit_Gradient:     break;
+
+    case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
+    case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
+    case eCSSUnit_Centimeter:   aResult.AppendLiteral("cm");   break;
+    case eCSSUnit_Point:        aResult.AppendLiteral("pt");   break;
+    case eCSSUnit_Pica:         aResult.AppendLiteral("pc");   break;
+
+    case eCSSUnit_EM:           aResult.AppendLiteral("em");   break;
+    case eCSSUnit_XHeight:      aResult.AppendLiteral("ex");   break;
+    case eCSSUnit_Char:         aResult.AppendLiteral("ch");   break;
+    case eCSSUnit_RootEM:       aResult.AppendLiteral("rem");  break;
+
+    case eCSSUnit_Pixel:        aResult.AppendLiteral("px");   break;
+
+    case eCSSUnit_Degree:       aResult.AppendLiteral("deg");  break;
+    case eCSSUnit_Grad:         aResult.AppendLiteral("grad"); break;
+    case eCSSUnit_Radian:       aResult.AppendLiteral("rad");  break;
+
+    case eCSSUnit_Hertz:        aResult.AppendLiteral("Hz");   break;
+    case eCSSUnit_Kilohertz:    aResult.AppendLiteral("kHz");  break;
+
+    case eCSSUnit_Seconds:      aResult.Append(PRUnichar('s'));    break;
+    case eCSSUnit_Milliseconds: aResult.AppendLiteral("ms");   break;
+  }
+}
+
 nsCSSValue::URL::URL(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
                      nsIPrincipal* aOriginPrincipal)
   : mURI(aURI),
     mString(aString),
     mReferrer(aReferrer),
     mOriginPrincipal(aOriginPrincipal)
 {
   NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -215,16 +215,22 @@ public:
   nsCSSValue&  operator=(const nsCSSValue& aCopy);
   PRBool      operator==(const nsCSSValue& aOther) const;
 
   PRBool operator!=(const nsCSSValue& aOther) const
   {
     return !(*this == aOther);
   }
 
+  /**
+   * Serialize |this| as a specified value for |aProperty| and append
+   * it to |aResult|.
+   */
+  void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const;
+
   nsCSSUnit GetUnit() const { return mUnit; }
   PRBool    IsLengthUnit() const
     { return eCSSUnit_Inch <= mUnit && mUnit <= eCSSUnit_Pixel; }
   PRBool    IsFixedLengthUnit() const  
     { return eCSSUnit_Inch <= mUnit && mUnit <= eCSSUnit_Pica; }
   PRBool    IsRelativeLengthUnit() const  
     { return eCSSUnit_EM <= mUnit && mUnit <= eCSSUnit_Pixel; }
   PRBool    IsAngularUnit() const