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
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