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 idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs511339
milestone2.0b2pre
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