bug 511339 - part 1 - implement -moz-font-feature-settings and -moz-font-language-override in CSS. r=dbaron
authorJonathan Kew <jfkthame@gmail.com>
Tue, 13 Jul 2010 21:30:42 +0100
changeset 47359 0f7bc3357bc3d24d1f325c945a8d7b929c0a9b53
parent 47358 2f5efa678ed82d97a911042ba94bbea91248a08a
child 47360 1027e04ad4ee459b548b8c9afa86f8d13ec59d51
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs511339
milestone2.0b2pre
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 511339 - part 1 - implement -moz-font-feature-settings and -moz-font-language-override in CSS. r=dbaron
content/canvas/src/nsCanvasRenderingContext2D.cpp
dom/interfaces/css/nsIDOMNSCSS2Properties.idl
gfx/src/nsFont.cpp
gfx/src/nsFont.h
gfx/src/thebes/nsThebesFontMetrics.cpp
layout/base/nsPresContext.cpp
layout/style/nsCSSDeclaration.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSProperty.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSRules.cpp
layout/style/nsCSSRules.h
layout/style/nsCSSStruct.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/test/property_database.js
layout/style/test/test_bug377947.html
layout/style/test/test_system_font_serialization.html
layout/svg/base/src/nsSVGGlyphFrame.cpp
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -2350,17 +2350,19 @@ nsCanvasRenderingContext2D::SetFont(cons
     gfxFontStyle style(fontStyle->mFont.style,
                        fontStyle->mFont.weight,
                        fontStyle->mFont.stretch,
                        NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
                        language,
                        fontStyle->mFont.sizeAdjust,
                        fontStyle->mFont.systemFont,
                        fontStyle->mFont.familyNameQuirks,
-                       printerFont);
+                       printerFont,
+                       fontStyle->mFont.featureSettings,
+                       fontStyle->mFont.languageOverride);
 
     CurrentState().fontGroup =
         gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
                                                     &style,
                                                     presShell->GetPresContext()->GetUserFontSet());
     NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
     CurrentState().font = font;
     return NS_OK;
--- a/dom/interfaces/css/nsIDOMNSCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMNSCSS2Properties.idl
@@ -35,17 +35,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMSVGCSS2Properties.idl"
 
-[scriptable, uuid(29B6104D-933F-4DD0-8FC8-BDEC0514469D)]
+[scriptable, uuid(C8FA5710-8E47-4E76-9EBD-87E2ACF4F46C)]
 interface nsIDOMNSCSS2Properties : nsIDOMSVGCSS2Properties
 {
            /* Non-DOM 2 extensions */
 
            /* Mozilla extension CSS properties */
            attribute DOMString        MozAppearance;
                                         // raises(DOMException) on setting
 
@@ -116,16 +116,22 @@ interface nsIDOMNSCSS2Properties : nsIDO
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozColumnGap;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozFloatEdge;
                                         // raises(DOMException) on setting
 
+           attribute DOMString        MozFontFeatureSettings;
+                                        // raises(DOMException) on setting
+
+           attribute DOMString        MozFontLanguageOverride;
+                                        // raises(DOMException) on setting
+
            attribute DOMString        MozForceBrokenImageIcon;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozImageRegion;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozMarginEnd;
                                         // raises(DOMException) on setting
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -37,60 +37,78 @@
 
 #include "nsFont.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 
 nsFont::nsFont(const char* aName, PRUint8 aStyle, PRUint8 aVariant,
                PRUint16 aWeight, PRInt16 aStretch, PRUint8 aDecoration,
-               nscoord aSize, float aSizeAdjust)
+               nscoord aSize, float aSizeAdjust,
+               const nsString* aFeatureSettings,
+               const nsString* aLanguageOverride)
 {
   NS_ASSERTION(aName && IsASCII(nsDependentCString(aName)),
                "Must only pass ASCII names here");
   name.AssignASCII(aName);
   style = aStyle;
   systemFont = PR_FALSE;
   variant = aVariant;
   familyNameQuirks = PR_FALSE;
   weight = aWeight;
   stretch = aStretch;
   decorations = aDecoration;
   size = aSize;
   sizeAdjust = aSizeAdjust;
+  if (aFeatureSettings) {
+    featureSettings = *aFeatureSettings;
+  }
+  if (aLanguageOverride) {
+    languageOverride = *aLanguageOverride;
+  }
 }
 
 nsFont::nsFont(const nsString& aName, PRUint8 aStyle, PRUint8 aVariant,
                PRUint16 aWeight, PRInt16 aStretch, PRUint8 aDecoration,
-               nscoord aSize, float aSizeAdjust)
+               nscoord aSize, float aSizeAdjust,
+               const nsString* aFeatureSettings,
+               const nsString* aLanguageOverride)
   : name(aName)
 {
   style = aStyle;
   systemFont = PR_FALSE;
   variant = aVariant;
   familyNameQuirks = PR_FALSE;
   weight = aWeight;
   stretch = aStretch;
   decorations = aDecoration;
   size = aSize;
   sizeAdjust = aSizeAdjust;
+  if (aFeatureSettings) {
+    featureSettings = *aFeatureSettings;
+  }
+  if (aLanguageOverride) {
+    languageOverride = *aLanguageOverride;
+  }
 }
 
 nsFont::nsFont(const nsFont& aOther)
   : name(aOther.name)
 {
   style = aOther.style;
   systemFont = aOther.systemFont;
   variant = aOther.variant;
   familyNameQuirks = aOther.familyNameQuirks;
   weight = aOther.weight;
   stretch = aOther.stretch;
   decorations = aOther.decorations;
   size = aOther.size;
   sizeAdjust = aOther.sizeAdjust;
+  featureSettings = aOther.featureSettings;
+  languageOverride = aOther.languageOverride;
 }
 
 nsFont::nsFont()
 {
 }
 
 nsFont::~nsFont()
 {
@@ -100,17 +118,19 @@ PRBool nsFont::BaseEquals(const nsFont& 
 {
   if ((style == aOther.style) &&
       (systemFont == aOther.systemFont) &&
       (familyNameQuirks == aOther.familyNameQuirks) &&
       (weight == aOther.weight) &&
       (stretch == aOther.stretch) &&
       (size == aOther.size) &&
       (sizeAdjust == aOther.sizeAdjust) &&
-      name.Equals(aOther.name, nsCaseInsensitiveStringComparator())) {
+      name.Equals(aOther.name, nsCaseInsensitiveStringComparator()) &&
+      (featureSettings == aOther.featureSettings) &&
+      (languageOverride == aOther.languageOverride)) {
     return PR_TRUE;
   }
   return PR_FALSE;
 }
 
 PRBool nsFont::Equals(const nsFont& aOther) const
 {
   if (BaseEquals(aOther) &&
@@ -128,16 +148,18 @@ nsFont& nsFont::operator=(const nsFont& 
   systemFont = aOther.systemFont;
   variant = aOther.variant;
   familyNameQuirks = aOther.familyNameQuirks;
   weight = aOther.weight;
   stretch = aOther.stretch;
   decorations = aOther.decorations;
   size = aOther.size;
   sizeAdjust = aOther.sizeAdjust;
+  featureSettings = aOther.featureSettings;
+  languageOverride = aOther.languageOverride;
   return *this;
 }
 
 static PRBool IsGenericFontFamily(const nsString& aFamily)
 {
   PRUint8 generic;
   nsFont::GetGenericID(aFamily, &generic);
   return generic != kGenericFont_NONE;
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -97,25 +97,37 @@ struct NS_GFX nsFont {
   nscoord size;
 
   // The aspect-value (ie., the ratio actualsize:actualxheight) that any
   // actual physical font created from this font structure must have when
   // rendering or measuring a string. A value of 0 means no adjustment
   // needs to be done.
   float sizeAdjust;
 
+  // Font features from CSS font-feature-settings
+  nsString featureSettings;
+
+  // Language system tag, to override document language;
+  // this is an OpenType "language system" tag represented as a 32-bit integer
+  // (see http://www.microsoft.com/typography/otspec/languagetags.htm).
+  nsString languageOverride;
+
   // Initialize the font struct with an ASCII name
   nsFont(const char* aName, PRUint8 aStyle, PRUint8 aVariant,
          PRUint16 aWeight, PRInt16 aStretch, PRUint8 aDecoration,
-         nscoord aSize, float aSizeAdjust=0.0f);
+         nscoord aSize, float aSizeAdjust=0.0f,
+         const nsString* aFeatureSettings = nsnull,
+         const nsString* aLanguageOverride = nsnull);
 
   // Initialize the font struct with a (potentially) unicode name
   nsFont(const nsString& aName, PRUint8 aStyle, PRUint8 aVariant,
          PRUint16 aWeight, PRInt16 aStretch, PRUint8 aDecoration,
-         nscoord aSize, float aSizeAdjust=0.0f);
+         nscoord aSize, float aSizeAdjust=0.0f,
+         const nsString* aFeatureSettings = nsnull,
+         const nsString* aLanguageOverride = nsnull);
 
   // Make a copy of the given font
   nsFont(const nsFont& aFont);
 
   nsFont();
   ~nsFont();
 
   PRBool operator==(const nsFont& aOther) const {
--- a/gfx/src/thebes/nsThebesFontMetrics.cpp
+++ b/gfx/src/thebes/nsThebesFontMetrics.cpp
@@ -78,17 +78,19 @@ nsThebesFontMetrics::Init(const nsFont& 
 
     gfxFloat size = gfxFloat(aFont.size) / mP2A;
 
     PRBool printerFont = mDeviceContext->IsPrinterSurface();
     mFontStyle = new gfxFontStyle(aFont.style, aFont.weight, aFont.stretch,
                                   size, aLanguage,
                                   aFont.sizeAdjust, aFont.systemFont,
                                   aFont.familyNameQuirks,
-                                  printerFont);
+                                  printerFont,
+                                  aFont.featureSettings,
+                                  aFont.languageOverride);
 
     mFontGroup =
         gfxPlatform::GetPlatform()->CreateFontGroup(aFont.name, mFontStyle, 
                                                     aUserFontSet);
     if (mFontGroup->FontListLength() < 1) 
         return NS_ERROR_UNEXPECTED;
 
     return NS_OK;
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1694,16 +1694,17 @@ InsertFontFaceRule(nsCSSFontFaceRule *aR
 
   nsAutoString fontfamily;
   nsCSSValue val;
 
   PRUint32 unit;
   PRUint32 weight = NS_STYLE_FONT_WEIGHT_NORMAL;
   PRUint32 stretch = NS_STYLE_FONT_STRETCH_NORMAL;
   PRUint32 italicStyle = FONT_STYLE_NORMAL;
+  nsString featureSettings, languageOverride;
 
   // set up family name
   aRule->GetDesc(eCSSFontDesc_Family, val);
   unit = val.GetUnit();
   if (unit == eCSSUnit_String) {
     val.GetStringValue(fontfamily);
   } else {
     NS_ASSERTION(unit == eCSSUnit_Null,
@@ -1744,16 +1745,40 @@ InsertFontFaceRule(nsCSSFontFaceRule *aR
     italicStyle = val.GetIntValue();
   } else if (unit == eCSSUnit_Normal) {
     italicStyle = FONT_STYLE_NORMAL;
   } else {
     NS_ASSERTION(unit == eCSSUnit_Null,
                  "@font-face style has unexpected unit");
   }
 
+  // set up font features
+  aRule->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Normal) {
+    // empty feature string
+  } else if (unit == eCSSUnit_String) {
+    val.GetStringValue(featureSettings);
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face font-feature-settings has unexpected unit");
+  }
+
+  // set up font language override
+  aRule->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
+  unit = val.GetUnit();
+  if (unit == eCSSUnit_Normal) {
+    // empty feature string
+  } else if (unit == eCSSUnit_String) {
+    val.GetStringValue(languageOverride);
+  } else {
+    NS_ASSERTION(unit == eCSSUnit_Null,
+                 "@font-face font-language-override has unexpected unit");
+  }
+
   // set up src array
   nsTArray<gfxFontFaceSrc> srcArray;
 
   aRule->GetDesc(eCSSFontDesc_Src, val);
   unit = val.GetUnit();
   if (unit == eCSSUnit_Array) {
     nsCSSValue::Array *srcArr = val.GetArrayValue();
     size_t numSrc = srcArr->Count();
@@ -1819,17 +1844,18 @@ InsertFontFaceRule(nsCSSFontFaceRule *aR
         break;
       }
      }
   } else {
     NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
   }
   
   if (!fontfamily.IsEmpty() && srcArray.Length() > 0) {
-    aFontSet->AddFontFace(fontfamily, srcArray, weight, stretch, italicStyle);
+    aFontSet->AddFontFace(fontfamily, srcArray, weight, stretch, italicStyle,
+                          featureSettings, languageOverride);
   }
 }
 
 gfxUserFontSet*
 nsPresContext::GetUserFontSetInternal()
 {
   // We want to initialize the user font set lazily the first time the
   // user asks for it, rather than building it too early and forcing
--- a/layout/style/nsCSSDeclaration.cpp
+++ b/layout/style/nsCSSDeclaration.cpp
@@ -1025,40 +1025,49 @@ nsCSSDeclaration::GetValue(nsCSSProperty
       const nsCSSValue &lh =
         *data->ValueStorageFor(eCSSProperty_line_height);
       const nsCSSValue &family =
         *data->ValueStorageFor(eCSSProperty_font_family);
       const nsCSSValue &stretch =
         *data->ValueStorageFor(eCSSProperty_font_stretch);
       const nsCSSValue &sizeAdjust =
         *data->ValueStorageFor(eCSSProperty_font_size_adjust);
+      const nsCSSValue &featureSettings =
+        *data->ValueStorageFor(eCSSProperty_font_feature_settings);
+      const nsCSSValue &languageOverride =
+        *data->ValueStorageFor(eCSSProperty_font_language_override);
 
       if (systemFont &&
           systemFont->GetUnit() != eCSSUnit_None &&
           systemFont->GetUnit() != eCSSUnit_Null) {
         if (style.GetUnit() != eCSSUnit_System_Font ||
             variant.GetUnit() != eCSSUnit_System_Font ||
             weight.GetUnit() != eCSSUnit_System_Font ||
             size.GetUnit() != eCSSUnit_System_Font ||
             lh.GetUnit() != eCSSUnit_System_Font ||
             family.GetUnit() != eCSSUnit_System_Font ||
             stretch.GetUnit() != eCSSUnit_System_Font ||
-            sizeAdjust.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);
       } else {
-        // The font-stretch and font-size-adjust
+        // 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) {
+            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);
           aValue.Append(PRUnichar(' '));
         }
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -5958,16 +5958,20 @@ CSSParserImpl::ParseSingleValueProperty(
   case eCSSProperty_float:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kFloatKTable);
   case eCSSProperty_float_edge:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kFloatEdgeKTable);
   case eCSSProperty_font_family:
     return ParseFamily(aValue);
+  case eCSSProperty_font_feature_settings:
+  case eCSSProperty_font_language_override:
+    return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_INHERIT |
+                                VARIANT_STRING, nsnull);
   case eCSSProperty_font_size:
     return ParseNonNegativeVariant(aValue,
                                    VARIANT_HKLP | VARIANT_SYSFONT |
                                      VARIANT_CALC,
                                    nsCSSProps::kFontSizeKTable);
   case eCSSProperty_font_size_adjust:
     return ParseVariant(aValue, VARIANT_HON | VARIANT_SYSFONT,
                         nsnull);
@@ -6236,16 +6240,20 @@ CSSParserImpl::ParseFontDescriptorValue(
 
     // These two are unique to @font-face and have their own special grammar.
   case eCSSFontDesc_Src:
     return ParseFontSrc(aValue);
 
   case eCSSFontDesc_UnicodeRange:
     return ParseFontRanges(aValue);
 
+  case eCSSFontDesc_FontFeatureSettings:
+  case eCSSFontDesc_FontLanguageOverride:
+    return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nsnull);
+
   case eCSSFontDesc_UNKNOWN:
   case eCSSFontDesc_COUNT:
     NS_NOTREACHED("bad nsCSSFontDesc code");
   }
   // explicitly do NOT have a default case to let the compiler
   // help find missing descriptors
   return PR_FALSE;
 }
@@ -7820,28 +7828,32 @@ CSSParserImpl::ParseFont()
         AppendValue(eCSSProperty_font_family, family);
         AppendValue(eCSSProperty_font_style, family);
         AppendValue(eCSSProperty_font_variant, family);
         AppendValue(eCSSProperty_font_weight, family);
         AppendValue(eCSSProperty_font_size, family);
         AppendValue(eCSSProperty_line_height, family);
         AppendValue(eCSSProperty_font_stretch, family);
         AppendValue(eCSSProperty_font_size_adjust, family);
+        AppendValue(eCSSProperty_font_feature_settings, family);
+        AppendValue(eCSSProperty_font_language_override, family);
       }
       else {
         AppendValue(eCSSProperty__x_system_font, family);
         nsCSSValue systemFont(eCSSUnit_System_Font);
         AppendValue(eCSSProperty_font_family, systemFont);
         AppendValue(eCSSProperty_font_style, systemFont);
         AppendValue(eCSSProperty_font_variant, systemFont);
         AppendValue(eCSSProperty_font_weight, systemFont);
         AppendValue(eCSSProperty_font_size, systemFont);
         AppendValue(eCSSProperty_line_height, systemFont);
         AppendValue(eCSSProperty_font_stretch, systemFont);
         AppendValue(eCSSProperty_font_size_adjust, systemFont);
+        AppendValue(eCSSProperty_font_feature_settings, systemFont);
+        AppendValue(eCSSProperty_font_language_override, systemFont);
       }
       return PR_TRUE;
     }
     return PR_FALSE;
   }
 
   // Get optional font-style, font-variant and font-weight (in any order)
   const PRInt32 numProps = 3;
@@ -7893,16 +7905,18 @@ CSSParserImpl::ParseFont()
       AppendValue(eCSSProperty_font_style, values[0]);
       AppendValue(eCSSProperty_font_variant, values[1]);
       AppendValue(eCSSProperty_font_weight, values[2]);
       AppendValue(eCSSProperty_font_size, size);
       AppendValue(eCSSProperty_line_height, lineHeight);
       AppendValue(eCSSProperty_font_stretch,
                   nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated));
       AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
+      AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal));
+      AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal));
       return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 PRBool
 CSSParserImpl::ParseFontWeight(nsCSSValue& aValue)
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -1426,16 +1426,38 @@ CSS_PROP_FONT(
     CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
     Font,
     mFamily,
     eCSSType_Value,
     nsnull,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_FONT(
+    -moz-font-feature-settings,
+    font_feature_settings,
+    MozFontFeatureSettings,
+    CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
+    Font,
+    mFontFeatureSettings,
+    eCSSType_Value,
+    nsnull,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_FONT(
+    -moz-font-language-override,
+    font_language_override,
+    MozFontLanguageOverride,
+    CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
+    Font,
+    mFontLanguageOverride,
+    eCSSType_Value,
+    nsnull,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_FONT(
     font-size,
     font_size,
     FontSize,
     CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE,
     Font,
     mSize,
     eCSSType_Value,
     kFontSizeKTable,
--- a/layout/style/nsCSSProperty.h
+++ b/layout/style/nsCSSProperty.h
@@ -96,12 +96,14 @@ enum nsCSSType {
 enum nsCSSFontDesc {
   eCSSFontDesc_UNKNOWN = -1,
   eCSSFontDesc_Family,
   eCSSFontDesc_Style,
   eCSSFontDesc_Weight,
   eCSSFontDesc_Stretch,
   eCSSFontDesc_Src,
   eCSSFontDesc_UnicodeRange,
+  eCSSFontDesc_FontFeatureSettings,
+  eCSSFontDesc_FontLanguageOverride,
   eCSSFontDesc_COUNT
 };
 
 #endif /* nsCSSProperty_h___ */
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -81,17 +81,19 @@ static nsStaticCaseInsensitiveNameTable*
 
 // Keep in sync with enum nsCSSFontDesc in nsCSSProperty.h.
 static const char* const kCSSRawFontDescs[] = {
   "font-family",
   "font-style",
   "font-weight",
   "font-stretch",
   "src",
-  "unicode-range"
+  "unicode-range",
+  "-moz-font-feature-settings",
+  "-moz-font-language-override"
 };
 
 struct PropertyAndCount {
   nsCSSProperty property;
   PRUint32 count;
 };
 
 static int
@@ -1854,16 +1856,18 @@ static const nsCSSProperty gFontSubpropT
   eCSSProperty_font_style,
   eCSSProperty_font_variant,
   eCSSProperty_font_weight,
   eCSSProperty_font_size,
   eCSSProperty_line_height,
   eCSSProperty_font_size_adjust, // XXX Added LDB.
   eCSSProperty_font_stretch, // XXX Added LDB.
   eCSSProperty__x_system_font,
+  eCSSProperty_font_feature_settings,
+  eCSSProperty_font_language_override,
   eCSSProperty_UNKNOWN
 };
 
 static const nsCSSProperty gListStyleSubpropTable[] = {
   eCSSProperty_list_style_type,
   eCSSProperty_list_style_image,
   eCSSProperty_list_style_position,
   eCSSProperty_UNKNOWN
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -1571,17 +1571,19 @@ AppendSerializedUnicodeRange(nsCSSValue 
 // Keep this in sync with enum nsCSSFontDesc in nsCSSProperty.h.
 nsCSSValue nsCSSFontFaceStyleDecl::* const
 nsCSSFontFaceStyleDecl::Fields[] = {
     &nsCSSFontFaceStyleDecl::mFamily,
     &nsCSSFontFaceStyleDecl::mStyle,
     &nsCSSFontFaceStyleDecl::mWeight,
     &nsCSSFontFaceStyleDecl::mStretch,
     &nsCSSFontFaceStyleDecl::mSrc,
-    &nsCSSFontFaceStyleDecl::mUnicodeRange
+    &nsCSSFontFaceStyleDecl::mUnicodeRange,
+    &nsCSSFontFaceStyleDecl::mFontFeatureSettings,
+    &nsCSSFontFaceStyleDecl::mFontLanguageOverride
 };
 
 DOMCI_DATA(CSSFontFaceStyleDecl, nsCSSFontFaceStyleDecl)
 
 // QueryInterface implementation for nsCSSFontFaceStyleDecl
 NS_INTERFACE_MAP_BEGIN(nsCSSFontFaceStyleDecl)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleDeclaration)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -229,16 +229,18 @@ public:
 protected:
   friend class nsCSSFontFaceRule;
   nsCSSValue mFamily;
   nsCSSValue mStyle;
   nsCSSValue mWeight;
   nsCSSValue mStretch;
   nsCSSValue mSrc;
   nsCSSValue mUnicodeRange;
+  nsCSSValue mFontFeatureSettings;
+  nsCSSValue mFontLanguageOverride;
 
   static nsCSSValue nsCSSFontFaceStyleDecl::* const Fields[];  
   inline nsCSSFontFaceRule* ContainingRule();
   inline const nsCSSFontFaceRule* ContainingRule() const;
 
 private:
   // NOT TO BE IMPLEMENTED
   // This object cannot be allocated on its own, only as part of
--- a/layout/style/nsCSSStruct.h
+++ b/layout/style/nsCSSStruct.h
@@ -278,16 +278,18 @@ struct nsCSSFont : public nsCSSStruct {
   nsCSSValue mSystemFont;
   nsCSSValue mFamily;
   nsCSSValue mStyle;
   nsCSSValue mVariant;
   nsCSSValue mWeight;
   nsCSSValue mSize;
   nsCSSValue mSizeAdjust; // NEW
   nsCSSValue mStretch; // NEW
+  nsCSSValue mFontFeatureSettings;
+  nsCSSValue mFontLanguageOverride;
 
 #ifdef MOZ_MATHML
   nsCSSValue mScriptLevel; // Integer values mean "relative", Number values mean "absolute" 
   nsCSSValue mScriptSizeMultiplier;
   nsCSSValue mScriptMinSize;
 #endif
 
 private:
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1314,16 +1314,50 @@ nsComputedDOMStyle::GetFontVariant(nsIDO
     nsCSSProps::ValueToKeywordEnum(GetStyleFont()->mFont.variant,
                                    nsCSSProps::kFontVariantKTable));
 
   NS_ADDREF(*aValue = val);
   return NS_OK;
 }
 
 nsresult
+nsComputedDOMStyle::GetMozFontFeatureSettings(nsIDOMCSSValue** aValue)
+{
+  nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+  NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY);
+
+  const nsStyleFont* font = GetStyleFont();
+  if (font->mFont.featureSettings.IsEmpty()) {
+    val->SetIdent(eCSSKeyword_normal);
+  } else {
+    nsString str;
+    nsStyleUtil::AppendEscapedCSSString(font->mFont.featureSettings, str);
+    val->SetString(str);
+  }
+  return CallQueryInterface(val, aValue);
+}
+
+nsresult
+nsComputedDOMStyle::GetMozFontLanguageOverride(nsIDOMCSSValue** aValue)
+{
+  nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+  NS_ENSURE_TRUE(val, NS_ERROR_OUT_OF_MEMORY);
+
+  const nsStyleFont* font = GetStyleFont();
+  if (font->mFont.languageOverride.IsEmpty()) {
+    val->SetIdent(eCSSKeyword_normal);
+  } else {
+    nsString str;
+    nsStyleUtil::AppendEscapedCSSString(font->mFont.languageOverride, str);
+    val->SetString(str);
+  }
+  return CallQueryInterface(val, aValue);
+}
+
+nsresult
 nsComputedDOMStyle::GetBackgroundList(PRUint8 nsStyleBackground::Layer::* aMember,
                                       PRUint32 nsStyleBackground::* aCount,
                                       const PRInt32 aTable[],
                                       nsIDOMCSSValue** aResult)
 {
   const nsStyleBackground* bg = GetStyleBackground();
 
   nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
@@ -4759,16 +4793,18 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(_moz_column_count,             ColumnCount),
     COMPUTED_STYLE_MAP_ENTRY(_moz_column_width,             ColumnWidth),
     COMPUTED_STYLE_MAP_ENTRY(_moz_column_gap,               ColumnGap),
     //// COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule,         ColumnRule),
     COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule_color,        ColumnRuleColor),
     COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule_width,        ColumnRuleWidth),
     COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule_style,        ColumnRuleStyle),
     COMPUTED_STYLE_MAP_ENTRY(float_edge,                    FloatEdge),
+    COMPUTED_STYLE_MAP_ENTRY(font_feature_settings,         MozFontFeatureSettings),
+    COMPUTED_STYLE_MAP_ENTRY(font_language_override,        MozFontLanguageOverride),
     COMPUTED_STYLE_MAP_ENTRY(force_broken_image_icon,  ForceBrokenImageIcon),
     COMPUTED_STYLE_MAP_ENTRY(image_region,                  ImageRegion),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomLeft, OutlineRadiusBottomLeft),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomRight,OutlineRadiusBottomRight),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topLeft,    OutlineRadiusTopLeft),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topRight,   OutlineRadiusTopRight),
     COMPUTED_STYLE_MAP_ENTRY(resize,                        Resize),
     COMPUTED_STYLE_MAP_ENTRY(stack_sizing,                  StackSizing),
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -178,16 +178,18 @@ private:
   nsresult GetTop(nsIDOMCSSValue** aValue);
   nsresult GetRight(nsIDOMCSSValue** aValue);
   nsresult GetBottom(nsIDOMCSSValue** aValue);
   nsresult GetStackSizing(nsIDOMCSSValue** aValue);
 
   /* Font properties */
   nsresult GetColor(nsIDOMCSSValue** aValue);
   nsresult GetFontFamily(nsIDOMCSSValue** aValue);
+  nsresult GetMozFontFeatureSettings(nsIDOMCSSValue** aValue);
+  nsresult GetMozFontLanguageOverride(nsIDOMCSSValue** aValue);
   nsresult GetFontSize(nsIDOMCSSValue** aValue);
   nsresult GetFontSizeAdjust(nsIDOMCSSValue** aValue);
   nsresult GetFontStretch(nsIDOMCSSValue** aValue);
   nsresult GetFontStyle(nsIDOMCSSValue** aValue);
   nsresult GetFontWeight(nsIDOMCSSValue** aValue);
   nsresult GetFontVariant(nsIDOMCSSValue** aValue);
 
   /* Background properties */
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3270,16 +3270,42 @@ nsRuleNode::SetFont(nsPresContext* aPres
     aCanStoreInRuleTree = PR_FALSE;
     aFont->mScriptLevel = aParentFont->mScriptLevel;
   }
   else if (eCSSUnit_Initial == aFontData.mScriptSizeMultiplier.GetUnit()) {
     aFont->mScriptLevel = 0;
   }
 #endif
 
+  // font-feature-settings
+  if (eCSSUnit_Inherit == aFontData.mFontFeatureSettings.GetUnit()) {
+    aCanStoreInRuleTree = PR_FALSE;
+    aFont->mFont.featureSettings = aParentFont->mFont.featureSettings;
+  } else if (eCSSUnit_Normal == aFontData.mFontFeatureSettings.GetUnit() ||
+             eCSSUnit_Initial == aFontData.mFontFeatureSettings.GetUnit()) {
+    aFont->mFont.featureSettings.Truncate();
+  } else if (eCSSUnit_System_Font == aFontData.mFontFeatureSettings.GetUnit()) {
+    aFont->mFont.featureSettings = systemFont.featureSettings;
+  } else if (eCSSUnit_String == aFontData.mFontFeatureSettings.GetUnit()) {
+    aFontData.mFontFeatureSettings.GetStringValue(aFont->mFont.featureSettings);
+  }
+
+  // font-language-override
+  if (eCSSUnit_Inherit == aFontData.mFontLanguageOverride.GetUnit()) {
+    aCanStoreInRuleTree = PR_FALSE;
+    aFont->mFont.languageOverride = aParentFont->mFont.languageOverride;
+  } else if (eCSSUnit_Normal == aFontData.mFontLanguageOverride.GetUnit() ||
+             eCSSUnit_Initial == aFontData.mFontLanguageOverride.GetUnit()) {
+    aFont->mFont.languageOverride.Truncate();
+  } else if (eCSSUnit_System_Font == aFontData.mFontLanguageOverride.GetUnit()) {
+    aFont->mFont.languageOverride = systemFont.languageOverride;
+  } else if (eCSSUnit_String == aFontData.mFontLanguageOverride.GetUnit()) {
+    aFontData.mFontLanguageOverride.GetStringValue(aFont->mFont.languageOverride);
+  }
+
   // font-size: enum, length, percent, inherit
   nscoord scriptLevelAdjustedParentSize = aParentFont->mSize;
 #ifdef MOZ_MATHML
   nscoord scriptLevelAdjustedUnconstrainedParentSize;
   scriptLevelAdjustedParentSize =
     ComputeScriptLevelSize(aFont, aParentFont, aPresContext,
                            &scriptLevelAdjustedUnconstrainedParentSize);
   NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -205,17 +205,19 @@ nsChangeHint nsStyleFont::CalcFontDiffer
 {
   if ((aFont1.size == aFont2.size) && 
       (aFont1.sizeAdjust == aFont2.sizeAdjust) && 
       (aFont1.style == aFont2.style) &&
       (aFont1.variant == aFont2.variant) &&
       (aFont1.familyNameQuirks == aFont2.familyNameQuirks) &&
       (aFont1.weight == aFont2.weight) &&
       (aFont1.stretch == aFont2.stretch) &&
-      (aFont1.name == aFont2.name)) {
+      (aFont1.name == aFont2.name) &&
+      (aFont1.featureSettings == aFont2.featureSettings) &&
+      (aFont1.languageOverride == aFont2.languageOverride)) {
     if ((aFont1.decorations == aFont2.decorations)) {
       return NS_STYLE_HINT_NONE;
     }
     return NS_STYLE_HINT_VISUAL;
   }
   return NS_STYLE_HINT_REFLOW;
 }
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -1466,30 +1466,46 @@ var gCSSProperties = {
 		initial_values: [ "none" ],
 		other_values: [ "left", "right" ],
 		invalid_values: []
 	},
 	"font": {
 		domProp: "font",
 		inherited: true,
 		type: CSS_TYPE_TRUE_SHORTHAND,
-		subproperties: [ "font-style", "font-variant", "font-weight", "font-size", "line-height", "font-family", "font-stretch", "font-size-adjust" ],
+		subproperties: [ "font-style", "font-variant", "font-weight", "font-size", "line-height", "font-family", "font-stretch", "font-size-adjust", "-moz-font-feature-settings", "-moz-font-language-override" ],
 		/* XXX could be sans-serif */
 		initial_values: [ "medium serif" ],
 		other_values: [ "large serif", "9px fantasy", "bold italic small-caps 24px/1.4 Times New Roman, serif", "caption", "icon", "menu", "message-box", "small-caption", "status-bar" ],
 		invalid_values: []
 	},
 	"font-family": {
 		domProp: "fontFamily",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "serif" ],
 		other_values: [ "sans-serif", "Times New Roman, serif", "'Times New Roman', serif", "cursive", "fantasy", "\"Times New Roman", "Times, \"Times New Roman" ],
 		invalid_values: [ "\"Times New\" Roman" ]
 	},
+	"-moz-font-feature-settings": {
+		domProp: "MozFontFeatureSettings",
+		inherited: true,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "normal" ],
+		other_values: [ "'liga=1'", "\"liga=1\"", "'foo,bar=\"hello\"'" ],
+		invalid_values: [ "liga=1", "foo,bar=\"hello\"" ]
+	},
+	"-moz-font-language-override": {
+		domProp: "MozFontLanguageOverride",
+		inherited: true,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "normal" ],
+		other_values: [ "'TRK'", "\"TRK\"", "'N\\'Ko'" ],
+		invalid_values: [ "TRK" ]
+	},
 	"font-size": {
 		domProp: "fontSize",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "medium",
 			"1rem",
 			"-moz-calc(1rem)",
 			"-moz-calc(0.75rem + 200% - 125% + 0.25rem - 75%)"
--- a/layout/style/test/test_bug377947.html
+++ b/layout/style/test/test_bug377947.html
@@ -53,17 +53,19 @@ is(s.getPropertyValue("font"), "",
    "font shorthand should start off empty");
 var all_but_one = {
   "font-family": "serif",
   "font-style": "normal",
   "font-variant": "normal",
   "font-weight": "bold",
   "font-size": "small",
   "font-size-adjust": "none", // has to be default value
-  "font-stretch": "normal" // has to be default value
+  "font-stretch": "normal", // has to be default value
+  "-moz-font-feature-settings": "normal", // has to be default value
+  "-moz-font-language-override": "normal" // has to be default value
 };
 for (var prop in all_but_one) {
   s.setProperty(prop, all_but_one[prop], "");
 }
 is(s.getPropertyValue("font"), "",
    "font shorthand should be empty when some subproperties specified");
 s.setProperty("line-height", "1.5", "");
 isnot(s.getPropertyValue("font"), "",
--- a/layout/style/test/test_system_font_serialization.html
+++ b/layout/style/test/test_system_font_serialization.html
@@ -43,15 +43,15 @@ e.setAttribute("style", "font: menu; fon
 is(e.style.cssText, "font: menu;", "serialize system font alone");
 is(e.style.font, "menu", "font getter returns value");
 
 e.setAttribute("style", "font: menu; font-weight: -moz-use-system-font ! important");
 is(e.style.cssText, "font: menu; font-weight: -moz-use-system-font ! important;", "serialize system font and subproperty that is important");
 is(e.style.font, "", "font getter returns nothing");
 
 e.setAttribute("style", "font: inherit; font-family: Helvetica;");
-is(e.style.cssText, "font-style: inherit; font-variant: inherit; font-weight: inherit; font-size: inherit; line-height: inherit; font-size-adjust: inherit; font-stretch: inherit; font-family: Helvetica;", "don't serialize system font for font:inherit");
+is(e.style.cssText, "font-style: inherit; font-variant: inherit; font-weight: inherit; font-size: inherit; line-height: inherit; font-size-adjust: inherit; font-stretch: inherit; -moz-font-feature-settings: inherit; -moz-font-language-override: inherit; font-family: Helvetica;", "don't serialize system font for font:inherit");
 is(e.style.font, "", "font getter returns nothing");
 
 </script>
 </pre>
 </body>
 </html>
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -1496,17 +1496,19 @@ nsSVGGlyphFrame::EnsureTextRun(float *aD
 
     const nsFont& font = fontData->mFont;
     PRBool printerFont = (presContext->Type() == nsPresContext::eContext_PrintPreview ||
                           presContext->Type() == nsPresContext::eContext_Print);
     gfxFontStyle fontStyle(font.style, font.weight, font.stretch, textRunSize,
                            mStyleContext->GetStyleVisibility()->mLanguage,
                            font.sizeAdjust, font.systemFont,
                            font.familyNameQuirks,
-                           printerFont);
+                           printerFont,
+                           font.featureSettings,
+                           font.languageOverride);
 
     nsRefPtr<gfxFontGroup> fontGroup =
       gfxPlatform::GetPlatform()->CreateFontGroup(font.name, &fontStyle, presContext->GetUserFontSet());
 
     PRUint32 flags = gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX |
       nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(), GetStyleText(), GetStyleFont());
 
     // XXX We should use a better surface here! But then we'd have to