Bug 280443 p2 - parse font family lists into fontlist structs. r=heycam
authorJohn Daggett <jdaggett@mozilla.com>
Fri, 06 Jun 2014 15:09:23 +0900
changeset 207342 014f475ca0ec0334c7f29e9240a958d626acb521
parent 207341 c079286e72886570435c65e9cd5bc0c3321deeb7
child 207343 e39cfafa8517ce7810685f6a99fce6fad919d5f4
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs280443
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 280443 p2 - parse font family lists into fontlist structs. r=heycam
content/html/content/src/HTMLFontElement.cpp
content/mathml/content/src/nsMathMLElement.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSParser.h
layout/style/nsCSSRules.cpp
layout/style/nsCSSRules.h
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleSet.cpp
layout/style/nsStyleUtil.cpp
layout/style/nsStyleUtil.h
--- a/content/html/content/src/HTMLFontElement.cpp
+++ b/content/html/content/src/HTMLFontElement.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HTMLFontElement.h"
 #include "mozilla/dom/HTMLFontElementBinding.h"
 #include "nsAttrValueInlines.h"
 #include "nsMappedAttributes.h"
 #include "nsRuleData.h"
 #include "nsContentUtils.h"
+#include "nsCSSParser.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Font)
 
 namespace mozilla {
 namespace dom {
 
 HTMLFontElement::~HTMLFontElement()
 {
@@ -57,17 +58,19 @@ HTMLFontElement::MapAttributesIntoRule(c
 {
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
     // face: string list
     nsCSSValue* family = aData->ValueForFontFamily();
     if (family->GetUnit() == eCSSUnit_Null) {
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::face);
       if (value && value->Type() == nsAttrValue::eString &&
           !value->IsEmptyString()) {
-        family->SetStringValue(value->GetStringValue(), eCSSUnit_Families);
+        nsCSSParser parser;
+        parser.ParseFontFamilyListString(value->GetStringValue(),
+                                         nullptr, 0, *family);
       }
     }
 
     // size: int
     nsCSSValue* fontSize = aData->ValueForFontSize();
     if (fontSize->GetUnit() == eCSSUnit_Null) {
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::size);
       if (value && value->Type() == nsAttrValue::eInteger) {
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -7,16 +7,17 @@
 #include "nsMathMLElement.h"
 #include "base/compiler_specific.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsGkAtoms.h"
 #include "nsCRT.h"
 #include "nsLayoutStylesheetCache.h"
 #include "nsRuleData.h"
 #include "nsCSSValue.h"
+#include "nsCSSParser.h"
 #include "nsMappedAttributes.h"
 #include "nsStyleConsts.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
@@ -647,17 +648,19 @@ nsMathMLElement::MapMathMLAttributesInto
     nsCSSValue* fontFamily = aData->ValueForFontFamily();
     if (value) {
       WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(),
                      nsGkAtoms::mathvariant_->GetUTF16String(),
                      aData->mPresContext->Document());
     }
     if (value && value->Type() == nsAttrValue::eString &&
         fontFamily->GetUnit() == eCSSUnit_Null) {
-      fontFamily->SetStringValue(value->GetStringValue(), eCSSUnit_Families);
+      nsCSSParser parser;
+      parser.ParseFontFamilyListString(value->GetStringValue(),
+                                       nullptr, 0, *fontFamily);
     }
 
     // fontstyle
     //
     // "Specified the font style to use for the token. Deprecated in favor of
     //  mathvariant."
     //
     // values: "normal" | "italic"
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -69,16 +69,17 @@ CSS_KEY(-moz-eventreerow, _moz_eventreer
 CSS_KEY(-moz-ethiopic-halehame, _moz_ethiopic_halehame)
 CSS_KEY(-moz-ethiopic-numeric, _moz_ethiopic_numeric)
 CSS_KEY(-moz-ethiopic-halehame-am, _moz_ethiopic_halehame_am)
 CSS_KEY(-moz-ethiopic-halehame-ti-er, _moz_ethiopic_halehame_ti_er)
 CSS_KEY(-moz-ethiopic-halehame-ti-et, _moz_ethiopic_halehame_ti_et)
 CSS_KEY(-moz-field, _moz_field)
 CSS_KEY(-moz-fieldtext, _moz_fieldtext)
 CSS_KEY(-moz-fit-content, _moz_fit_content)
+CSS_KEY(-moz-fixed, _moz_fixed)
 CSS_KEY(-moz-grabbing, _moz_grabbing)
 CSS_KEY(-moz-grab, _moz_grab)
 CSS_KEY(-moz-grid-group, _moz_grid_group)
 CSS_KEY(-moz-grid-line, _moz_grid_line)
 CSS_KEY(-moz-grid, _moz_grid)
 CSS_KEY(-moz-groupbox, _moz_groupbox)
 CSS_KEY(-moz-gujarati, _moz_gujarati)
 CSS_KEY(-moz-gurmukhi, _moz_gurmukhi)
@@ -233,16 +234,17 @@ CSS_KEY(continuous, continuous)
 CSS_KEY(contrast, contrast)
 CSS_KEY(copy, copy)
 CSS_KEY(contextual, contextual)
 CSS_KEY(cover, cover)
 CSS_KEY(crop, crop)
 CSS_KEY(cross, cross)
 CSS_KEY(crosshair, crosshair)
 CSS_KEY(currentcolor, currentcolor)
+CSS_KEY(cursive, cursive)
 CSS_KEY(darken, darken)
 CSS_KEY(dashed, dashed)
 CSS_KEY(dense, dense)
 CSS_KEY(decimal, decimal)
 CSS_KEY(decimal-leading-zero, decimal_leading_zero)
 CSS_KEY(default, default)
 CSS_KEY(deg, deg)
 CSS_KEY(diagonal-fractions, diagonal_fractions)
@@ -270,16 +272,17 @@ CSS_KEY(embed, embed)
 CSS_KEY(enabled, enabled)
 CSS_KEY(end, end)
 CSS_KEY(ex, ex)
 CSS_KEY(exclusion, exclusion)
 CSS_KEY(expanded, expanded)
 CSS_KEY(extra-condensed, extra_condensed)
 CSS_KEY(extra-expanded, extra_expanded)
 CSS_KEY(ew-resize, ew_resize)
+CSS_KEY(fantasy, fantasy)
 CSS_KEY(farthest-side, farthest_side)
 CSS_KEY(farthest-corner, farthest_corner)
 CSS_KEY(fill, fill)
 CSS_KEY(fixed, fixed)
 CSS_KEY(flat, flat)
 CSS_KEY(flex, flex)
 CSS_KEY(flex-end, flex_end)
 CSS_KEY(flex-start, flex_start)
@@ -482,16 +485,17 @@ CSS_KEY(select-after, select_after)
 CSS_KEY(select-all, select_all)
 CSS_KEY(select-before, select_before)
 CSS_KEY(select-menu, select_menu)
 CSS_KEY(select-same, select_same)
 CSS_KEY(semi-condensed, semi_condensed)
 CSS_KEY(semi-expanded, semi_expanded)
 CSS_KEY(separate, separate)
 CSS_KEY(sepia, sepia)
+CSS_KEY(serif, serif)
 CSS_KEY(show, show)
 CSS_KEY(sideways, sideways)
 CSS_KEY(simp-chinese-formal, simp_chinese_formal)
 CSS_KEY(simp-chinese-informal, simp_chinese_informal)
 CSS_KEY(simplified, simplified)
 CSS_KEY(skew, skew)
 CSS_KEY(skewx, skewx)
 CSS_KEY(skewy, skewy)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -42,16 +42,17 @@
 #include "nsAutoPtr.h"
 #include "CSSCalc.h"
 #include "nsMediaFeatures.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsRuleData.h"
 #include "mozilla/CSSVariableValues.h"
 #include "mozilla/dom/URL.h"
+#include "gfxFontFamilyList.h"
 
 using namespace mozilla;
 
 typedef nsCSSProps::KTableValue KTableValue;
 
 const uint32_t
 nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = {
 #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
@@ -155,16 +156,21 @@ public:
                          const nsAString& aPropValue,
                          nsIURI* aSheetURL,
                          nsIURI* aBaseURL,
                          nsIPrincipal* aSheetPrincipal,
                          css::Declaration* aDeclaration,
                          bool* aChanged,
                          bool aIsImportant);
 
+  bool ParseFontFamilyListString(const nsSubstring& aBuffer,
+                                 nsIURI* aURL, // for error reporting
+                                 uint32_t aLineNumber, // for error reporting
+                                 nsCSSValue& aValue);
+
   bool ParseColorString(const nsSubstring& aBuffer,
                         nsIURI* aURL, // for error reporting
                         uint32_t aLineNumber, // for error reporting
                         nsCSSValue& aValue);
 
   nsresult ParseSelectorString(const nsSubstring& aSelectorString,
                                nsIURI* aURL, // for error reporting
                                uint32_t aLineNumber, // for error reporting
@@ -712,17 +718,17 @@ protected:
   bool ParseFontVariantAlternates(nsCSSValue& aValue);
   bool ParseBitmaskValues(nsCSSValue& aValue,
                           const KTableValue aKeywordTable[],
                           const int32_t aMasks[]);
   bool ParseFontVariantEastAsian(nsCSSValue& aValue);
   bool ParseFontVariantLigatures(nsCSSValue& aValue);
   bool ParseFontVariantNumeric(nsCSSValue& aValue);
   bool ParseFontWeight(nsCSSValue& aValue);
-  bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword);
+  bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
   bool ParseFamily(nsCSSValue& aValue);
   bool ParseFontFeatureSettings(nsCSSValue& aValue);
   bool ParseFontSrc(nsCSSValue& aValue);
   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
   bool ParseFontRanges(nsCSSValue& aValue);
   bool ParseListStyle();
   bool ParseMargin();
   bool ParseMarks(nsCSSValue& aValue);
@@ -1582,16 +1588,33 @@ CSSParserImpl::ParseColorString(const ns
 
   // Parse a color, and check that there's nothing else after it.
   bool colorParsed = ParseColor(aValue) && !GetToken(true);
   OUTPUT_ERROR();
   ReleaseScanner();
   return colorParsed;
 }
 
+bool
+CSSParserImpl::ParseFontFamilyListString(const nsSubstring& aBuffer,
+                                         nsIURI* aURI, // for error reporting
+                                         uint32_t aLineNumber, // for error reporting
+                                         nsCSSValue& aValue)
+{
+  nsCSSScanner scanner(aBuffer, aLineNumber);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
+  InitScanner(scanner, reporter, aURI, aURI, nullptr);
+
+  // Parse a font family list, and check that there's nothing else after it.
+  bool familyParsed = ParseFamily(aValue) && !GetToken(true);
+  OUTPUT_ERROR();
+  ReleaseScanner();
+  return familyParsed;
+}
+
 nsresult
 CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString,
                                    nsIURI* aURI, // for error reporting
                                    uint32_t aLineNumber, // for error reporting
                                    nsCSSSelectorList **aSelectorList)
 {
   nsCSSScanner scanner(aSelectorString, aLineNumber);
   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI);
@@ -3302,37 +3325,36 @@ CSSParserImpl::ParseFontDescriptor(nsCSS
 bool
 CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
                                           void* aData)
 {
   nsRefPtr<nsCSSFontFeatureValuesRule>
                valuesRule(new nsCSSFontFeatureValuesRule());
 
   // parse family list
-  nsCSSValue familyValue;
-
-  if (!ParseFamily(familyValue) ||
-      familyValue.GetUnit() != eCSSUnit_Families)
+  nsCSSValue fontlistValue;
+
+  if (!ParseFamily(fontlistValue) ||
+      fontlistValue.GetUnit() != eCSSUnit_FontFamilyList)
   {
     REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily);
     return false;
   }
 
   // add family to rule
-  nsAutoString familyList;
-  bool hasGeneric;
-  familyValue.GetStringValue(familyList);
-  valuesRule->SetFamilyList(familyList, hasGeneric);
+  const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue();
 
   // family list has generic ==> parse error
-  if (hasGeneric) {
+  if (fontlist->HasGeneric()) {
     REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList);
     return false;
   }
 
+  valuesRule->SetFamilyList(*fontlist);
+
   // open brace
   if (!ExpectSymbol('{', true)) {
     REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart);
     return false;
   }
 
   // list of sets of feature values, each set bound to a specific
   // feature-type (e.g. swash, annotation)
@@ -9318,64 +9340,40 @@ CSSParserImpl::ParseSingleValueProperty(
       return ParseVariant(aValue, variant, kwtable);
     case CSS_PROPERTY_VALUE_NONNEGATIVE:
       return ParseNonNegativeVariant(aValue, variant, kwtable);
     case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
       return ParseOneOrLargerVariant(aValue, variant, kwtable);
   }
 }
 
-// nsFont::EnumerateFamilies callback for ParseFontDescriptorValue
-struct MOZ_STACK_CLASS ExtractFirstFamilyData {
-  nsAutoString mFamilyName;
-  bool mGood;
-  ExtractFirstFamilyData() : mFamilyName(), mGood(false) {}
-};
-
-static bool
-ExtractFirstFamily(const nsString& aFamily,
-                   bool aGeneric,
-                   void* aData)
-{
-  ExtractFirstFamilyData* realData = (ExtractFirstFamilyData*) aData;
-  if (aGeneric || realData->mFamilyName.Length() > 0) {
-    realData->mGood = false;
-    return false;
-  }
-  realData->mFamilyName.Assign(aFamily);
-  realData->mGood = true;
-  return true;
-}
-
 // font-descriptor: descriptor ':' value ';'
 // caller has advanced mToken to point at the descriptor
 bool
 CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID,
                                         nsCSSValue& aValue)
 {
   switch (aDescID) {
     // These four are similar to the properties of the same name,
     // possibly with more restrictions on the values they can take.
   case eCSSFontDesc_Family: {
-    if (!ParseFamily(aValue) ||
-        aValue.GetUnit() != eCSSUnit_Families)
-      return false;
-
-    // the style parameters to the nsFont constructor are ignored,
-    // because it's only being used to call EnumerateFamilies
-    nsAutoString valueStr;
-    aValue.GetStringValue(valueStr);
-    nsFont font(valueStr, 0, 0, 0, 0, 0, 0);
-    ExtractFirstFamilyData dat;
-
-    font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
-    if (!dat.mGood)
-      return false;
-
-    aValue.SetStringValue(dat.mFamilyName, eCSSUnit_String);
+    nsCSSValue value;
+    if (!ParseFamily(value) ||
+        value.GetUnit() != eCSSUnit_FontFamilyList)
+      return false;
+
+    // name can only be a single, non-generic name
+    const FontFamilyList* f = value.GetFontFamilyListValue();
+    const nsTArray<FontFamilyName>& fontlist = f->GetFontlist();
+
+    if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) {
+      return false;
+    }
+
+    aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String);
     return true;
   }
 
   case eCSSFontDesc_Style:
     // property is VARIANT_HMK|VARIANT_SYSFONT
     return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
                         nsCSSProps::kFontStyleKTable);
 
@@ -11579,24 +11577,27 @@ CSSParserImpl::ParseFontWeight(nsCSSValu
       }
     }
     return true;
   }
   return false;
 }
 
 bool
-CSSParserImpl::ParseOneFamily(nsAString& aFamily, bool& aOneKeyword)
+CSSParserImpl::ParseOneFamily(nsAString& aFamily,
+                              bool& aOneKeyword,
+                              bool& aQuoted)
 {
   if (!GetToken(true))
     return false;
 
   nsCSSToken* tk = &mToken;
 
   aOneKeyword = false;
+  aQuoted = false;
   if (eCSSToken_Ident == tk->mType) {
     aOneKeyword = true;
     aFamily.Append(tk->mIdent);
     for (;;) {
       if (!GetToken(false))
         break;
 
       if (eCSSToken_Ident == tk->mType) {
@@ -11613,102 +11614,145 @@ CSSParserImpl::ParseOneFamily(nsAString&
       } else if (eCSSToken_Whitespace != tk->mType) {
         UngetToken();
         break;
       }
     }
     return true;
 
   } else if (eCSSToken_String == tk->mType) {
-    aFamily.Append(tk->mSymbol); // replace the quotes
+    aQuoted = true;
     aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes?
-    aFamily.Append(tk->mSymbol);
     return true;
 
   } else {
     UngetToken();
     return false;
   }
 }
 
+
+static bool
+AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList)
+{
+  switch (aKeyword) {
+    case eCSSKeyword_serif:
+      aFamilyList->Append(FontFamilyName(eFamily_serif));
+      return true;
+    case eCSSKeyword_sans_serif:
+      aFamilyList->Append(FontFamilyName(eFamily_sans_serif));
+      return true;
+    case eCSSKeyword_monospace:
+      aFamilyList->Append(FontFamilyName(eFamily_monospace));
+      return true;
+    case eCSSKeyword_cursive:
+      aFamilyList->Append(FontFamilyName(eFamily_cursive));
+      return true;
+    case eCSSKeyword_fantasy:
+      aFamilyList->Append(FontFamilyName(eFamily_fantasy));
+      return true;
+    case eCSSKeyword__moz_fixed:
+      aFamilyList->Append(FontFamilyName(eFamily_moz_fixed));
+      return true;
+    default:
+      break;
+  }
+
+  return false;
+}
+
 bool
 CSSParserImpl::ParseFamily(nsCSSValue& aValue)
 {
+  nsRefPtr<FontFamilyList> familyList = new FontFamilyList();
   nsAutoString family;
-  bool single;
+  bool single, quoted;
 
   // keywords only have meaning in the first position
-  if (!ParseOneFamily(family, single))
+  if (!ParseOneFamily(family, single, quoted))
     return false;
 
   // check for keywords, but only when keywords appear by themselves
   // i.e. not in compounds such as font-family: default blah;
+  bool foundGeneric = false;
   if (single) {
     nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
-    if (keyword == eCSSKeyword_inherit) {
-      aValue.SetInheritValue();
-      return true;
-    }
-    // 605231 - don't parse unquoted 'default' reserved keyword
-    if (keyword == eCSSKeyword_default) {
-      return false;
-    }
-    if (keyword == eCSSKeyword_initial) {
-      aValue.SetInitialValue();
-      return true;
-    }
-    if (keyword == eCSSKeyword_unset &&
-        nsLayoutUtils::UnsetValueEnabled()) {
-      aValue.SetUnsetValue();
-      return true;
-    }
-    if (keyword == eCSSKeyword__moz_use_system_font &&
-        !IsParsingCompoundProperty()) {
-      aValue.SetSystemFontValue();
-      return true;
-    }
+    switch (keyword) {
+      case eCSSKeyword_inherit:
+        aValue.SetInheritValue();
+        return true;
+      case eCSSKeyword_default:
+        // 605231 - don't parse unquoted 'default' reserved keyword
+        return false;
+      case eCSSKeyword_initial:
+        aValue.SetInitialValue();
+        return true;
+      case eCSSKeyword_unset:
+        if (nsLayoutUtils::UnsetValueEnabled()) {
+          aValue.SetUnsetValue();
+          return true;
+        }
+        break;
+      case eCSSKeyword__moz_use_system_font:
+        if (!IsParsingCompoundProperty()) {
+          aValue.SetSystemFontValue();
+          return true;
+        }
+        break;
+      default:
+        foundGeneric = AppendGeneric(keyword, familyList);
+    }
+  }
+
+  if (!foundGeneric) {
+    familyList->Append(
+      FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName)));
   }
 
   for (;;) {
     if (!ExpectSymbol(',', true))
       break;
 
-    family.Append(char16_t(','));
-
     nsAutoString nextFamily;
-    if (!ParseOneFamily(nextFamily, single))
+    if (!ParseOneFamily(nextFamily, single, quoted))
       return false;
 
     // at this point unquoted keywords are not allowed
     // as font family names but can appear within names
+    foundGeneric = false;
     if (single) {
       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
       switch (keyword) {
         case eCSSKeyword_inherit:
         case eCSSKeyword_initial:
         case eCSSKeyword_default:
         case eCSSKeyword__moz_use_system_font:
           return false;
         case eCSSKeyword_unset:
           if (nsLayoutUtils::UnsetValueEnabled()) {
             return false;
           }
-          // fall through
+          break;
         default:
+          foundGeneric = AppendGeneric(keyword, familyList);
           break;
       }
     }
 
-    family.Append(nextFamily);
-  }
-
-  if (family.IsEmpty()) {
-    return false;
-  }
-  aValue.SetStringValue(family, eCSSUnit_Families);
+    if (!foundGeneric) {
+      familyList->Append(
+        FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName)));
+    }
+  }
+
+  if (familyList->IsEmpty()) {
+    return false;
+  }
+
+  aValue.SetFontFamilyListValue(familyList);
   return true;
 }
 
 // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
 // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
 // local-src: 'local(' ( string | ident ) ')'
 
 bool
@@ -11731,36 +11775,43 @@ CSSParserImpl::ParseFontSrc(nsCSSValue& 
                mToken.mIdent.LowerCaseEqualsLiteral("local")) {
       // css3-fonts does not specify a formal grammar for local().
       // The text permits both unquoted identifiers and quoted
       // strings.  We resolve this ambiguity in the spec by
       // assuming that the appropriate production is a single
       // <family-name>, possibly surrounded by whitespace.
 
       nsAutoString family;
-      bool single;
-      if (!ParseOneFamily(family, single)) {
+      bool single, quoted;
+      if (!ParseOneFamily(family, single, quoted)) {
         SkipUntil(')');
         return false;
       }
       if (!ExpectSymbol(')', true)) {
         SkipUntil(')');
         return false;
       }
 
-      // the style parameters to the nsFont constructor are ignored,
-      // because it's only being used to call EnumerateFamilies
-      nsFont font(family, 0, 0, 0, 0, 0, 0);
-      ExtractFirstFamilyData dat;
-
-      font.EnumerateFamilies(ExtractFirstFamily, (void*) &dat);
-      if (!dat.mGood)
-        return false;
-
-      cur.SetStringValue(dat.mFamilyName, eCSSUnit_Local_Font);
+      // reject generics
+      if (single) {
+        nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family);
+        switch (keyword) {
+          case eCSSKeyword_serif:
+          case eCSSKeyword_sans_serif:
+          case eCSSKeyword_monospace:
+          case eCSSKeyword_cursive:
+          case eCSSKeyword_fantasy:
+          case eCSSKeyword__moz_fixed:
+            return false;
+          default:
+            break;
+        }
+      }
+
+      cur.SetStringValue(family, eCSSUnit_Local_Font);
       values.AppendElement(cur);
     } else {
       // We don't know what to do with this token; unget it and error out
       UngetToken();
       return false;
     }
 
     if (!ExpectSymbol(',', true))
@@ -14191,16 +14242,26 @@ nsCSSParser::ParseMediaList(const nsSubs
                             nsMediaList*       aMediaList,
                             bool               aHTMLMode)
 {
   static_cast<CSSParserImpl*>(mImpl)->
     ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode);
 }
 
 bool
+nsCSSParser::ParseFontFamilyListString(const nsSubstring& aBuffer,
+                                       nsIURI*            aURI,
+                                       uint32_t           aLineNumber,
+                                       nsCSSValue&        aValue)
+{
+  return static_cast<CSSParserImpl*>(mImpl)->
+    ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue);
+}
+
+bool
 nsCSSParser::ParseColorString(const nsSubstring& aBuffer,
                               nsIURI*            aURI,
                               uint32_t           aLineNumber,
                               nsCSSValue&        aValue)
 {
   return static_cast<CSSParserImpl*>(mImpl)->
     ParseColorString(aBuffer, aURI, aLineNumber, aValue);
 }
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -149,16 +149,25 @@ public:
   void ParseMediaList(const nsSubstring& aBuffer,
                       nsIURI*            aURL,
                       uint32_t           aLineNumber,
                       nsMediaList*       aMediaList,
                       bool               aHTMLMode);
 
   /**
    * Parse aBuffer into a nsCSSValue |aValue|. Will return false
+   * if aBuffer is not a valid font family list.
+   */
+  bool ParseFontFamilyListString(const nsSubstring& aBuffer,
+                                 nsIURI*            aURL,
+                                 uint32_t           aLineNumber,
+                                 nsCSSValue&        aValue);
+
+  /**
+   * Parse aBuffer into a nsCSSValue |aValue|. Will return false
    * if aBuffer is not a valid CSS color specification.
    * One can use nsRuleNode::ComputeColor to compute an nscolor from
    * the returned nsCSSValue.
    */
   bool ParseColorString(const nsSubstring& aBuffer,
                         nsIURI*            aURL,
                         uint32_t           aLineNumber,
                         nsCSSValue&        aValue);
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -1922,29 +1922,16 @@ NS_INTERFACE_MAP_BEGIN(nsCSSFontFeatureV
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFeatureValuesRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT(nsCSSFontFeatureValuesRule, Rule)
 
 static void
-FamilyListToString(const nsTArray<nsString>& aFamilyList, nsAString& aOutStr)
-{
-  uint32_t i, n = aFamilyList.Length();
-
-  for (i = 0; i < n; i++) {
-    nsStyleUtil::AppendEscapedCSSString(aFamilyList[i], aOutStr);
-    if (i != n - 1) {
-      aOutStr.AppendLiteral(", ");
-    }
-  }
-}
-
-static void
 FeatureValuesToString(
   const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aFeatureValues,
   nsAString& aOutStr)
 {
   uint32_t i, n;
 
   // append values
   n = aFeatureValues.Length();
@@ -1975,23 +1962,23 @@ FeatureValuesToString(
       aOutStr.Append(';');
     }
     aOutStr.AppendLiteral(" }\n");
   }
 }
 
 static void
 FontFeatureValuesRuleToString(
-  const nsTArray<nsString>& aFamilyList,
+  const mozilla::FontFamilyList& aFamilyList,
   const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aFeatureValues,
   nsAString& aOutStr)
 {
   aOutStr.AssignLiteral("@font-feature-values ");
   nsAutoString familyListStr, valueTextStr;
-  FamilyListToString(aFamilyList, familyListStr);
+  nsStyleUtil::AppendEscapedCSSFontFamilyList(aFamilyList, familyListStr);
   aOutStr.Append(familyListStr);
   aOutStr.AppendLiteral(" {\n");
   FeatureValuesToString(aFeatureValues, valueTextStr);
   aOutStr.Append(valueTextStr);
   aOutStr.Append('}');
 }
 
 #ifdef DEBUG
@@ -2053,19 +2040,19 @@ nsCSSFontFeatureValuesRule::GetParentSty
 
 NS_IMETHODIMP
 nsCSSFontFeatureValuesRule::GetParentRule(nsIDOMCSSRule** aParentRule)
 {
   return Rule::GetParentRule(aParentRule);
 }
 
 NS_IMETHODIMP
-nsCSSFontFeatureValuesRule::GetFontFamily(nsAString& aFontFamily)
+nsCSSFontFeatureValuesRule::GetFontFamily(nsAString& aFamilyListStr)
 {
-  FamilyListToString(mFamilyList, aFontFamily);
+  nsStyleUtil::AppendEscapedCSSFontFamilyList(mFamilyList, aFamilyListStr);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSFontFeatureValuesRule::SetFontFamily(const nsAString& aFontFamily)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -2101,23 +2088,20 @@ struct MakeFamilyArray {
     return true;
   }
 
   nsTArray<nsString>& familyArray;
   bool hasGeneric;
 };
 
 void
-nsCSSFontFeatureValuesRule::SetFamilyList(const nsAString& aFamilyList,
-                                          bool& aContainsGeneric)
+nsCSSFontFeatureValuesRule::SetFamilyList(
+  const mozilla::FontFamilyList& aFamilyList)
 {
-  nsFont font(aFamilyList, 0, 0, 0, 0, 0, 0);
-  MakeFamilyArray families(mFamilyList);
-  font.EnumerateFamilies(MakeFamilyArray::AddFamily, (void*) &families);
-  aContainsGeneric = families.hasGeneric;
+  mFamilyList = aFamilyList;
 }
 
 void
 nsCSSFontFeatureValuesRule::AddValueList(int32_t aVariantAlternate,
                      nsTArray<gfxFontFeatureValueSet::ValueList>& aValueList)
 {
   uint32_t i, len = mFeatureValues.Length();
   bool foundAlternate = false;
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -313,18 +313,18 @@ public:
   virtual already_AddRefed<mozilla::css::Rule> Clone() const MOZ_OVERRIDE;
 
   // nsIDOMCSSRule interface
   NS_DECL_NSIDOMCSSRULE
 
   // nsIDOMCSSFontFaceRule interface
   NS_DECL_NSIDOMCSSFONTFEATUREVALUESRULE
 
-  const nsTArray<nsString>& GetFamilyList() { return mFamilyList; }
-  void SetFamilyList(const nsAString& aFamilyList, bool& aContainsGeneric);
+  const mozilla::FontFamilyList& GetFamilyList() { return mFamilyList; }
+  void SetFamilyList(const mozilla::FontFamilyList& aFamilyList);
 
   void AddValueList(int32_t aVariantAlternate,
                     nsTArray<gfxFontFeatureValueSet::ValueList>& aValueList);
 
   const nsTArray<gfxFontFeatureValueSet::FeatureValues>& GetFeatureValues()
   {
     return mFeatureValues;
   }
@@ -336,17 +336,17 @@ public:
     // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
     bool fontFeaturesEnabled =
       nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
 
     return fontFeaturesEnabled;
   }
 
 protected:
-  nsTArray<nsString> mFamilyList;
+  mozilla::FontFamilyList mFamilyList;
   nsTArray<gfxFontFeatureValueSet::FeatureValues> mFeatureValues;
 };
 
 namespace mozilla {
 namespace css {
 
 class CharsetRule MOZ_FINAL : public Rule,
                               public nsIDOMCSSCharsetRule
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -106,16 +106,23 @@ nsCSSValue::nsCSSValue(nsCSSValueTokenSt
 
 nsCSSValue::nsCSSValue(mozilla::css::GridTemplateAreasValue* aValue)
   : mUnit(eCSSUnit_GridTemplateAreas)
 {
   mValue.mGridTemplateAreas = aValue;
   mValue.mGridTemplateAreas->AddRef();
 }
 
+nsCSSValue::nsCSSValue(FontFamilyList* aValue)
+  : mUnit(eCSSUnit_FontFamilyList)
+{
+  mValue.mFontFamilyList = aValue;
+  mValue.mFontFamilyList->AddRef();
+}
+
 nsCSSValue::nsCSSValue(const nsCSSValue& aCopy)
   : mUnit(aCopy.mUnit)
 {
   if (mUnit <= eCSSUnit_DummyInherit) {
     // nothing to do, but put this important case first
   }
   else if (eCSSUnit_Percent <= mUnit) {
     mValue.mFloat = aCopy.mValue.mFloat;
@@ -184,16 +191,20 @@ nsCSSValue::nsCSSValue(const nsCSSValue&
   }
   else if (eCSSUnit_PairListDep == mUnit) {
     mValue.mPairListDependent = aCopy.mValue.mPairListDependent;
   }
   else if (eCSSUnit_GridTemplateAreas == mUnit) {
     mValue.mGridTemplateAreas = aCopy.mValue.mGridTemplateAreas;
     mValue.mGridTemplateAreas->AddRef();
   }
+  else if (eCSSUnit_FontFamilyList == mUnit) {
+    mValue.mFontFamilyList = aCopy.mValue.mFontFamilyList;
+    mValue.mFontFamilyList->AddRef();
+  }
   else {
     NS_ABORT_IF_FALSE(false, "unknown unit");
   }
 }
 
 nsCSSValue& nsCSSValue::operator=(const nsCSSValue& aCopy)
 {
   if (this != &aCopy) {
@@ -259,16 +270,19 @@ bool nsCSSValue::operator==(const nsCSSV
       return *mValue.mSharedList == *aOther.mValue.mSharedList;
     }
     else if (eCSSUnit_PairList == mUnit) {
       return *mValue.mPairList == *aOther.mValue.mPairList;
     }
     else if (eCSSUnit_GridTemplateAreas == mUnit) {
       return *mValue.mGridTemplateAreas == *aOther.mValue.mGridTemplateAreas;
     }
+    else if (eCSSUnit_FontFamilyList == mUnit) {
+      return *mValue.mFontFamilyList == *aOther.mValue.mFontFamilyList;
+    }
     else {
       return mValue.mFloat == aOther.mValue.mFloat;
     }
   }
   return false;
 }
 
 double nsCSSValue::GetAngleValueInRadians() const
@@ -347,16 +361,18 @@ void nsCSSValue::DoReset()
   } else if (eCSSUnit_List == mUnit) {
     mValue.mList->Release();
   } else if (eCSSUnit_SharedList == mUnit) {
     mValue.mSharedList->Release();
   } else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList->Release();
   } else if (eCSSUnit_GridTemplateAreas == mUnit) {
     mValue.mGridTemplateAreas->Release();
+  } else if (eCSSUnit_FontFamilyList == mUnit) {
+    mValue.mFontFamilyList->Release();
   }
   mUnit = eCSSUnit_Null;
 }
 
 void nsCSSValue::SetIntValue(int32_t aValue, nsCSSUnit aUnit)
 {
   NS_ABORT_IF_FALSE(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated ||
                     aUnit == eCSSUnit_EnumColor, "not an int value");
@@ -470,16 +486,24 @@ void nsCSSValue::SetTokenStreamValue(nsC
 void nsCSSValue::SetGridTemplateAreas(mozilla::css::GridTemplateAreasValue* aValue)
 {
   Reset();
   mUnit = eCSSUnit_GridTemplateAreas;
   mValue.mGridTemplateAreas = aValue;
   mValue.mGridTemplateAreas->AddRef();
 }
 
+void nsCSSValue::SetFontFamilyListValue(FontFamilyList* aValue)
+{
+  Reset();
+  mUnit = eCSSUnit_FontFamilyList;
+  mValue.mFontFamilyList = aValue;
+  mValue.mFontFamilyList->AddRef();
+}
+
 void nsCSSValue::SetPairValue(const nsCSSValuePair* aValue)
 {
   // pairs should not be used for null/inherit/initial values
   NS_ABORT_IF_FALSE(aValue &&
                     aValue->mXValue.GetUnit() != eCSSUnit_Null &&
                     aValue->mYValue.GetUnit() != eCSSUnit_Null &&
                     aValue->mXValue.GetUnit() != eCSSUnit_Inherit &&
                     aValue->mYValue.GetUnit() != eCSSUnit_Inherit &&
@@ -820,19 +844,16 @@ nsCSSValue::AppendToString(nsCSSProperty
   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_Steps) {
     switch (unit) {
       case eCSSUnit_Counter:  aResult.AppendLiteral("counter(");  break;
       case eCSSUnit_Counters: aResult.AppendLiteral("counters("); break;
@@ -1349,16 +1370,19 @@ nsCSSValue::AppendToString(nsCSSProperty
     const mozilla::css::GridTemplateAreasValue* areas = GetGridTemplateAreas();
     MOZ_ASSERT(!areas->mTemplates.IsEmpty(),
                "Unexpected empty array in GridTemplateAreasValue");
     nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[0], aResult);
     for (uint32_t i = 1; i < areas->mTemplates.Length(); i++) {
       aResult.Append(char16_t(' '));
       nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[i], aResult);
     }
+  } else if (eCSSUnit_FontFamilyList == unit) {
+    nsStyleUtil::AppendEscapedCSSFontFamilyList(*mValue.mFontFamilyList,
+                                                aResult);
   }
 
   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("initial");  break;
     case eCSSUnit_Unset:        aResult.AppendLiteral("unset");    break;
@@ -1366,19 +1390,19 @@ nsCSSValue::AppendToString(nsCSSProperty
     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:
       NS_ABORT_IF_FALSE(false, "should never serialize");
       break;
 
+    case eCSSUnit_FontFamilyList: break;
     case eCSSUnit_String:       break;
     case eCSSUnit_Ident:        break;
-    case eCSSUnit_Families:     break;
     case eCSSUnit_URL:          break;
     case eCSSUnit_Image:        break;
     case eCSSUnit_Element:      break;
     case eCSSUnit_Array:        break;
     case eCSSUnit_Attr:
     case eCSSUnit_Cubic_Bezier:
     case eCSSUnit_Steps:
     case eCSSUnit_Counter:
@@ -1469,17 +1493,16 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
     case eCSSUnit_All:
     case eCSSUnit_Dummy:
     case eCSSUnit_DummyInherit:
       break;
 
     // String
     case eCSSUnit_String:
     case eCSSUnit_Ident:
-    case eCSSUnit_Families:
     case eCSSUnit_Attr:
     case eCSSUnit_Local_Font:
     case eCSSUnit_Font_Format:
     case eCSSUnit_Element:
       n += mValue.mString->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
       break;
 
     // Array
@@ -1557,16 +1580,20 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
     case eCSSUnit_PairListDep:
       break;
 
     // GridTemplateAreas
     case eCSSUnit_GridTemplateAreas:
       n += mValue.mGridTemplateAreas->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
+    case eCSSUnit_FontFamilyList:
+      n += mValue.mFontFamilyList->SizeOfIncludingThis(aMallocSizeOf);
+      break;
+
     // Int: nothing extra to measure.
     case eCSSUnit_Integer:
     case eCSSUnit_Enumerated:
     case eCSSUnit_EnumColor:
       break;
 
     // Integer Color: nothing extra to measure.
     case eCSSUnit_RGBColor:
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -18,16 +18,17 @@
 #include "nsCSSProperty.h"
 #include "nsColor.h"
 #include "nsCoord.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsTArray.h"
 #include "nsStyleConsts.h"
+#include "gfxFontFamilyList.h"
 
 class imgRequestProxy;
 class nsCSSStyleSheet;
 class nsIDocument;
 class nsIPrincipal;
 class nsIURI;
 class nsPresContext;
 template <class T>
@@ -207,17 +208,16 @@ enum nsCSSUnit {
   eCSSUnit_All          = 8,      // (n/a) value is all
   eCSSUnit_Dummy        = 9,      // (n/a) a fake but specified value, used
                                   //       only in temporary values
   eCSSUnit_DummyInherit = 10,     // (n/a) a fake but specified value, used
                                   //       only in temporary values
 
   eCSSUnit_String       = 11,     // (char16_t*) a string value
   eCSSUnit_Ident        = 12,     // (char16_t*) a string value
-  eCSSUnit_Families     = 13,     // (char16_t*) a string value
   eCSSUnit_Attr         = 14,     // (char16_t*) a attr(string) value
   eCSSUnit_Local_Font   = 15,     // (char16_t*) a local font name
   eCSSUnit_Font_Format  = 16,     // (char16_t*) a font format name
   eCSSUnit_Element      = 17,     // (char16_t*) an element id
 
   eCSSUnit_Array        = 20,     // (nsCSSValue::Array*) a list of values
   eCSSUnit_Counter      = 21,     // (nsCSSValue::Array*) a counter(string,[string]) value
   eCSSUnit_Counters     = 22,     // (nsCSSValue::Array*) a counters(string,string[,string]) value
@@ -259,16 +259,18 @@ enum nsCSSUnit {
   eCSSUnit_ListDep      = 54,     // (nsCSSValueList*) same as List
                                   //   but does not own the list
   eCSSUnit_SharedList   = 55,     // (nsCSSValueSharedList*) same as list
                                   //   but reference counted and shared
   eCSSUnit_PairList     = 56,     // (nsCSSValuePairList*) list of value pairs
   eCSSUnit_PairListDep  = 57,     // (nsCSSValuePairList*) same as PairList
                                   //   but does not own the list
 
+  eCSSUnit_FontFamilyList = 58,   // (FontFamilyList*) value
+
   eCSSUnit_Integer      = 70,     // (int) simple value
   eCSSUnit_Enumerated   = 71,     // (int) value has enumerated meaning
 
   eCSSUnit_EnumColor           = 80,   // (int) enumerated color (kColorKTable)
   eCSSUnit_RGBColor            = 81,   // (nscolor) an opaque RGBA value specified as rgb()
   eCSSUnit_RGBAColor           = 82,   // (nscolor) an RGBA value specified as rgba()
   eCSSUnit_HexColor            = 83,   // (nscolor) an opaque RGBA value specified as #rrggbb
   eCSSUnit_ShortHexColor       = 84,   // (nscolor) an opaque RGBA value specified as #rgb
@@ -357,16 +359,17 @@ public:
   nsCSSValue(float aValue, nsCSSUnit aUnit);
   nsCSSValue(const nsString& aValue, nsCSSUnit aUnit);
   nsCSSValue(Array* aArray, nsCSSUnit aUnit);
   explicit nsCSSValue(mozilla::css::URLValue* aValue);
   explicit nsCSSValue(mozilla::css::ImageValue* aValue);
   explicit nsCSSValue(nsCSSValueGradient* aValue);
   explicit nsCSSValue(nsCSSValueTokenStream* aValue);
   explicit nsCSSValue(mozilla::css::GridTemplateAreasValue* aValue);
+  explicit nsCSSValue(mozilla::FontFamilyList* aValue);
   nsCSSValue(const nsCSSValue& aCopy);
   ~nsCSSValue() { Reset(); }
 
   nsCSSValue&  operator=(const nsCSSValue& aCopy);
   bool        operator==(const nsCSSValue& aOther) const;
 
   bool operator!=(const nsCSSValue& aOther) const
   {
@@ -535,16 +538,23 @@ public:
   }
 
   nsCSSValueSharedList* GetSharedListValue() const
   {
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_SharedList, "not a shared list value");
     return mValue.mSharedList;
   }
 
+  mozilla::FontFamilyList* GetFontFamilyListValue() const
+  {
+    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_FontFamilyList,
+                      "not a font family list value");
+    return mValue.mFontFamilyList;
+  }
+
   // bodies of these are below
   inline nsCSSValuePair& GetPairValue();
   inline const nsCSSValuePair& GetPairValue() const;
 
   inline nsCSSRect& GetRectValue();
   inline const nsCSSRect& GetRectValue() const;
 
   inline nsCSSValueList* GetListValue();
@@ -615,16 +625,17 @@ public:
                           float aComponent3,
                           float aAlpha, nsCSSUnit aUnit);
   void SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
   void SetURLValue(mozilla::css::URLValue* aURI);
   void SetImageValue(mozilla::css::ImageValue* aImage);
   void SetGradientValue(nsCSSValueGradient* aGradient);
   void SetTokenStreamValue(nsCSSValueTokenStream* aTokenStream);
   void SetGridTemplateAreas(mozilla::css::GridTemplateAreasValue* aValue);
+  void SetFontFamilyListValue(mozilla::FontFamilyList* aFontListValue);
   void SetPairValue(const nsCSSValuePair* aPair);
   void SetPairValue(const nsCSSValue& xValue, const nsCSSValue& yValue);
   void SetSharedListValue(nsCSSValueSharedList* aList);
   void SetDependentListValue(nsCSSValueList* aList);
   void SetDependentPairListValue(nsCSSValuePairList* aList);
   void SetTripletValue(const nsCSSValueTriplet* aTriplet);
   void SetTripletValue(const nsCSSValue& xValue, const nsCSSValue& yValue, const nsCSSValue& zValue);
   void SetAutoValue();
@@ -682,16 +693,17 @@ protected:
     nsCSSRect_heap* mRect;
     nsCSSValueTriplet_heap* mTriplet;
     nsCSSValueList_heap* mList;
     nsCSSValueList* mListDependent;
     nsCSSValueSharedList* mSharedList;
     nsCSSValuePairList_heap* mPairList;
     nsCSSValuePairList* mPairListDependent;
     nsCSSValueFloatColor* mFloatColor;
+    mozilla::FontFamilyList* mFontFamilyList;
   } mValue;
 };
 
 struct nsCSSValue::Array MOZ_FINAL {
 
   // return |Array| with reference count of zero
   static Array* Create(size_t aItemCount) {
     return new (aItemCount) Array(aItemCount);
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3331,21 +3331,21 @@ nsRuleNode::SetFont(nsPresContext* aPres
         systemFont.size =
           std::max(defaultVariableFont->size -
                  nsPresContext::CSSPointsToAppUnits(2), 0);
       }
 #endif
     }
   }
 
-  // font-family: string list, enum, inherit
+  // font-family: font family list, enum, inherit
   const nsCSSValue* familyValue = aRuleData->ValueForFontFamily();
   NS_ASSERTION(eCSSUnit_Enumerated != familyValue->GetUnit(),
                "system fonts should not be in mFamily anymore");
-  if (eCSSUnit_Families == familyValue->GetUnit()) {
+  if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) {
     // set the correct font if we are using DocumentFonts OR we are overriding for XUL
     // MJA: bug 31816
     if (aGenericFontID == kGenericFont_NONE) {
       // only bother appending fallback fonts if this isn't a fallback generic font itself
       if (!aFont->mFont.name.IsEmpty())
         aFont->mFont.name.Append((char16_t)',');
       // defaultVariableFont.name should always be "serif" or "sans-serif".
       aFont->mFont.name.Append(defaultVariableFont->name);
@@ -3860,28 +3860,16 @@ nsRuleNode::SetGenericFont(nsPresContext
     nsRuleNode::SetFont(aPresContext, context,
                         aGenericFontID, &ruleData, &parentFont, aFont,
                         false, dummy);
 
     parentFont = *aFont;
   }
 }
 
-static bool ExtractGeneric(const nsString& aFamily, bool aGeneric,
-                             void *aData)
-{
-  nsAutoString *data = static_cast<nsAutoString*>(aData);
-
-  if (aGeneric) {
-    *data = aFamily;
-    return false; // stop enumeration
-  }
-  return true;
-}
-
 const void*
 nsRuleNode::ComputeFontData(void* aStartStruct,
                             const nsRuleData* aRuleData,
                             nsStyleContext* aContext,
                             nsRuleNode* aHighestNode,
                             const RuleDetail aRuleDetail,
                             const bool aCanStoreInRuleTree)
 {
@@ -3910,42 +3898,67 @@ nsRuleNode::ComputeFontData(void* aStart
     useDocumentFonts = true;
   }
 
   // Figure out if we are a generic font
   uint8_t generic = kGenericFont_NONE;
   // XXXldb What if we would have had a string if we hadn't been doing
   // the optimization with a non-null aStartStruct?
   const nsCSSValue* familyValue = aRuleData->ValueForFontFamily();
-  if (eCSSUnit_Families == familyValue->GetUnit()) {
-    familyValue->GetStringValue(font->mFont.name);
-    // XXXldb Do we want to extract the generic for this if it's not only a
-    // generic?
-    nsFont::GetGenericID(font->mFont.name, &generic);
+  if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) {
+    font->mFont.name.Truncate();
+    const FontFamilyList* fontlist = familyValue->GetFontFamilyListValue();
+    nsStyleUtil::AppendEscapedCSSFontFamilyList(*fontlist, font->mFont.name);
+
+    // extract the first generic in the fontlist, if exists
+    FontFamilyType fontType = fontlist->FirstGeneric();
+
+    // if only a generic, set the generic type
+    if (fontlist->Length() == 1) {
+      switch (fontType) {
+        case eFamily_serif:
+          generic = kGenericFont_serif;
+          break;
+        case eFamily_sans_serif:
+          generic = kGenericFont_sans_serif;
+          break;
+        case eFamily_monospace:
+          generic = kGenericFont_monospace;
+          break;
+        case eFamily_cursive:
+          generic = kGenericFont_cursive;
+          break;
+        case eFamily_fantasy:
+          generic = kGenericFont_fantasy;
+          break;
+        case eFamily_moz_fixed:
+          generic = kGenericFont_moz_fixed;
+          break;
+        default:
+          break;
+      }
+    }
 
     // If we aren't allowed to use document fonts, then we are only entitled
     // to use the user's default variable-width font and fixed-width font
     if (!useDocumentFonts) {
-      // Extract the generic from the specified font family...
-      nsAutoString genericName;
-      if (!font->mFont.EnumerateFamilies(ExtractGeneric, &genericName)) {
-        // The specified font had a generic family.
-        font->mFont.name = genericName;
-        nsFont::GetGenericID(genericName, &generic);
-
-        // ... and only use it if it's -moz-fixed or monospace
-        if (generic != kGenericFont_moz_fixed &&
-            generic != kGenericFont_monospace) {
+
+      switch (fontType) {
+        case eFamily_monospace:
+          font->mFont.name.AssignLiteral("monospace");
+          generic = kGenericFont_monospace;
+          break;
+        case eFamily_moz_fixed:
+          font->mFont.name.AssignLiteral("-moz-fixed");
+          generic = kGenericFont_moz_fixed;
+          break;
+        default:
           font->mFont.name.Truncate();
           generic = kGenericFont_NONE;
-        }
-      } else {
-        // The specified font did not have a generic family.
-        font->mFont.name.Truncate();
-        generic = kGenericFont_NONE;
+          break;
       }
     }
   }
 
   // Now compute our font struct
   if (generic == kGenericFont_NONE) {
     // continue the normal processing
     nsRuleNode::SetFont(mPresContext, aContext, generic,
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1641,28 +1641,27 @@ nsStyleSet::GetFontFeatureValuesLookup()
     AppendFontFeatureValuesRules(PresContext(), rules);
 
     mFontFeatureValuesLookup = new gfxFontFeatureValueSet();
 
     uint32_t i, numRules = rules.Length();
     for (i = 0; i < numRules; i++) {
       nsCSSFontFeatureValuesRule *rule = rules[i];
 
-      const nsTArray<nsString>& familyList = rule->GetFamilyList();
+      const nsTArray<FontFamilyName>& familyList = rule->GetFamilyList().GetFontlist();
       const nsTArray<gfxFontFeatureValueSet::FeatureValues>&
         featureValues = rule->GetFeatureValues();
 
       // for each family
-      uint32_t f, numFam;
+      size_t f, numFam;
 
       numFam = familyList.Length();
       for (f = 0; f < numFam; f++) {
-        const nsString& family = familyList.ElementAt(f);
-        nsAutoString silly(family);
-        mFontFeatureValuesLookup->AddFontFeatureValues(silly, featureValues);
+        mFontFeatureValuesLookup->AddFontFeatureValues(familyList[f].mName,
+                                                       featureValues);
       }
     }
   }
 
   nsRefPtr<gfxFontFeatureValueSet> lookup = mFontFeatureValuesLookup;
   return lookup.forget();
 }
 
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -139,16 +139,44 @@ nsStyleUtil::AppendEscapedCSSIdent(const
       }
       aReturn.Append(ch);
     }
   }
   return true;
 }
 
 /* static */ void
+nsStyleUtil::AppendEscapedCSSFontFamilyList(
+  const mozilla::FontFamilyList& aFamilyList,
+  nsAString& aResult)
+{
+  const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
+  size_t i, len = fontlist.Length();
+  for (i = 0; i < len; i++) {
+    if (i != 0) {
+      aResult.Append(',');
+    }
+    const FontFamilyName& name = fontlist[i];
+    switch (name.mType) {
+      case eFamily_named:
+        // xxx - need to do appropriate escaping, done in later patch
+        // iterate over idents, escape each ident
+        aResult.Append(name.mName);
+        break;
+      case eFamily_named_quoted:
+        AppendEscapedCSSString(name.mName, aResult);
+        break;
+      default:
+        name.AppendToString(aResult);
+    }
+  }
+}
+
+
+/* static */ void
 nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty,
                                    int32_t aMaskedValue,
                                    int32_t aFirstMask,
                                    int32_t aLastMask,
                                    nsAString& aResult)
 {
   for (int32_t mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
     if (mask & aMaskedValue) {
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsStyleUtil_h___
 #define nsStyleUtil_h___
 
 #include "nsCoord.h"
 #include "nsCSSProperty.h"
 #include "nsString.h"
 #include "nsTArrayForwardDeclare.h"
+#include "gfxFontFamilyList.h"
 
 class nsCSSValue;
 class nsStringComparator;
 class nsStyleCoord;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 struct gfxFontFeature;
@@ -37,16 +38,20 @@ public:
   // Append the identifier given by |aIdent| to |aResult|, with
   // appropriate escaping so that it can be reparsed to the same
   // identifier.
   // Returns false if |aIdent| contains U+0000
   // Returns true for all other cases
   static bool AppendEscapedCSSIdent(const nsAString& aIdent,
                                     nsAString& aResult);
 
+  static void
+  AppendEscapedCSSFontFamilyList(const mozilla::FontFamilyList& aFamilyList,
+                                 nsAString& aResult);
+
   // Append a bitmask-valued property's value(s) (space-separated) to aResult.
   static void AppendBitmaskCSSValue(nsCSSProperty aProperty,
                                     int32_t aMaskedValue,
                                     int32_t aFirstMask,
                                     int32_t aLastMask,
                                     nsAString& aResult);
 
   static void AppendAngleValue(const nsStyleCoord& aValue, nsAString& aResult);