Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 03 Dec 2016 13:38:35 -0800
changeset 325220 6bdef7ba8b4108a996b9f61ef9f81c5ea6c93017
parent 325201 11d06cafe634dfa7981cfdca62c0b34d04a22a84 (current diff)
parent 325219 5144592dd9180a21472159e4d813a7f61c90484e (diff)
child 325221 30e3b482acfabbdac19c19f58d003121cc20d9ad
child 325223 da6c574e481eec99427e376602b5172631a30ab3
child 325246 84818f50b4f0b9afad86824b14924b44dc7e96e3
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
Merge m-i to m-c, a=merge MozReview-Commit-ID: DhWG5cpaeyZ
layout/reftests/dom/xbl-children-4-ref.html
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -1232,19 +1232,22 @@ exports.CSS_PROPERTIES = {
     ]
   },
   "-moz-tab-size": {
     "isInherited": true,
     "subproperties": [
       "-moz-tab-size"
     ],
     "supports": [
+      6,
       7
     ],
     "values": [
+      "-moz-calc",
+      "calc",
       "inherit",
       "initial",
       "unset"
     ]
   },
   "-moz-text-size-adjust": {
     "isInherited": true,
     "subproperties": [
@@ -2901,16 +2904,17 @@ exports.CSS_PROPERTIES = {
       "font-style",
       "font-synthesis",
       "font-variant-alternates",
       "font-variant-caps",
       "font-variant-east-asian",
       "font-variant-ligatures",
       "font-variant-numeric",
       "font-variant-position",
+      "font-variation-settings",
       "font-weight",
       "-moz-force-broken-image-icon",
       "grid-auto-columns",
       "grid-auto-flow",
       "grid-auto-rows",
       "grid-column-end",
       "grid-column-gap",
       "grid-column-start",
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4054,16 +4054,21 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
     StrokeOptions strokeOpts;
     DrawOptions drawOpts;
     Style style = (mOp == CanvasRenderingContext2D::TextDrawOperation::FILL)
                     ? Style::FILL
                     : Style::STROKE;
     AdjustedTarget target(mCtx);
     RefPtr<gfxContext> thebes =
       gfxContext::CreatePreservingTransformOrNull(target);
+    if (!thebes) {
+      // If CreatePreservingTransformOrNull returns null, it will also have
+      // issued a gfxCriticalNote already, so here we'll just bail out.
+      return;
+    }
     gfxTextRun::DrawParams params(thebes);
 
     if (mState->StyleIsColor(style)) { // Color
       nscolor fontColor = mState->colorStyles[style];
       if (style == Style::FILL) {
         params.context->SetColor(Color::FromABGR(fontColor));
       } else {
         params.textStrokeColor = fontColor;
--- a/dom/xbl/XBLChildrenElement.cpp
+++ b/dom/xbl/XBLChildrenElement.cpp
@@ -49,17 +49,18 @@ XBLChildrenElement::ParseAttribute(int32
     mIncludes.Clear();
     nsCharSeparatedTokenizer tok(aValue, '|',
                                  nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
     while (tok.hasMoreTokens()) {
       mIncludes.AppendElement(NS_Atomize(tok.nextToken()));
     }
   }
 
-  return false;
+  return nsXMLElement::ParseAttribute(aNamespaceID, aAttribute,
+                                      aValue, aResult);
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAnonymousContentList, mParent)
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -70,16 +70,17 @@ bool nsFont::Equals(const nsFont& aOther
       (weight == aOther.weight) &&
       (stretch == aOther.stretch) &&
       (size == aOther.size) &&
       (sizeAdjust == aOther.sizeAdjust) &&
       (fontlist == aOther.fontlist) &&
       (kerning == aOther.kerning) &&
       (synthesis == aOther.synthesis) &&
       (fontFeatureSettings == aOther.fontFeatureSettings) &&
+      (fontVariationSettings == aOther.fontVariationSettings) &&
       (languageOverride == aOther.languageOverride) &&
       (variantAlternates == aOther.variantAlternates) &&
       (variantCaps == aOther.variantCaps) &&
       (variantEastAsian == aOther.variantEastAsian) &&
       (variantLigatures == aOther.variantLigatures) &&
       (variantNumeric == aOther.variantNumeric) &&
       (variantPosition == aOther.variantPosition) &&
       (variantWidth == aOther.variantWidth) &&
@@ -290,8 +291,17 @@ void nsFont::AddFontFeaturesToStyle(gfxF
   // add in features from font-feature-settings
   aStyle->featureSettings.AppendElements(fontFeatureSettings);
 
   // enable grayscale antialiasing for text
   if (smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
     aStyle->useGrayscaleAntialiasing = true;
   }
 }
+
+void nsFont::AddFontVariationsToStyle(gfxFontStyle *aStyle) const
+{
+  // TODO: add variation settings from specific CSS properties
+  // such as weight, width, optical-size
+
+  // Add in arbitrary values from font-variation-settings
+  aStyle->variationSettings.AppendElements(fontVariationSettings);
+}
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -5,16 +5,17 @@
 
 #ifndef nsFont_h___
 #define nsFont_h___
 
 #include <stdint.h>                     // for uint8_t, uint16_t
 #include <sys/types.h>                  // for int16_t
 #include "gfxFontFamilyList.h"
 #include "gfxFontFeatures.h"
+#include "gfxFontVariations.h"
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsStringFwd.h"                // for nsSubstring
 #include "nsString.h"               // for nsString
 #include "nsTArray.h"                   // for nsTArray
 
 struct gfxFontStyle;
 
@@ -96,16 +97,19 @@ struct nsFont {
   nsTArray<gfxAlternateValue> alternateValues;
 
   // -- object used to look these up once the font is matched
   RefPtr<gfxFontFeatureValueSet> featureValueLookup;
 
   // Font features from CSS font-feature-settings
   nsTArray<gfxFontFeature> fontFeatureSettings;
 
+  // Font variations from CSS font-variation-settings
+  nsTArray<gfxFontVariation> fontVariationSettings;
+
   // 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 with a fontlist
   nsFont(const mozilla::FontFamilyList& aFontlist, nscoord aSize);
 
@@ -132,16 +136,18 @@ struct nsFont {
 
   nsFont& operator=(const nsFont& aOther);
 
   void CopyAlternates(const nsFont& aOther);
 
   // Add featureSettings into style
   void AddFontFeaturesToStyle(gfxFontStyle *aStyle) const;
 
+  void AddFontVariationsToStyle(gfxFontStyle *aStyle) const;
+
 protected:
   void Init(); // helper method for initialization
 };
 
 #define NS_FONT_VARIANT_NORMAL            0
 #define NS_FONT_VARIANT_SMALL_CAPS        1
 
 #endif /* nsFont_h___ */
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -130,16 +130,17 @@ nsFontMetrics::nsFontMetrics(const nsFon
                        aFont.sizeAdjust,
                        aFont.systemFont,
                        mDeviceContext->IsPrinterContext(),
                        aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
                        aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
                        aFont.languageOverride);
 
     aFont.AddFontFeaturesToStyle(&style);
+    aFont.AddFontVariationsToStyle(&style);
 
     gfxFloat devToCssSize = gfxFloat(mP2A) /
         gfxFloat(mDeviceContext->AppUnitsPerCSSPixel());
     mFontGroup = gfxPlatform::GetPlatform()->
         CreateFontGroup(aFont.fontlist, &style, aParams.textPerf,
                         aParams.userFontSet, devToCssSize);
 }
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3883,47 +3883,47 @@ gfxFontStyle::ParseFontLanguageOverride(
   return result;
 }
 
 gfxFontStyle::gfxFontStyle() :
     language(nsGkAtoms::x_western),
     size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(-1.0f), baselineOffset(0.0f),
     languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
+    style(NS_FONT_STYLE_NORMAL),
+    variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
+    variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
     systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
-    style(NS_FONT_STYLE_NORMAL),
     allowSyntheticWeight(true), allowSyntheticStyle(true),
     noFallbackVariantFeatures(true),
-    explicitLanguage(false),
-    variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
-    variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
+    explicitLanguage(false)
 {
 }
 
 gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
                            gfxFloat aSize,
                            nsIAtom *aLanguage, bool aExplicitLanguage,
                            float aSizeAdjust, bool aSystemFont,
                            bool aPrinterFont,
                            bool aAllowWeightSynthesis,
                            bool aAllowStyleSynthesis,
                            const nsString& aLanguageOverride):
     language(aLanguage),
     size(aSize), sizeAdjust(aSizeAdjust), baselineOffset(0.0f),
     languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
     weight(aWeight), stretch(aStretch),
+    style(aStyle),
+    variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
+    variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
     systemFont(aSystemFont), printerFont(aPrinterFont),
     useGrayscaleAntialiasing(false),
-    style(aStyle),
     allowSyntheticWeight(aAllowWeightSynthesis),
     allowSyntheticStyle(aAllowStyleSynthesis),
     noFallbackVariantFeatures(true),
-    explicitLanguage(aExplicitLanguage),
-    variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
-    variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
+    explicitLanguage(aExplicitLanguage)
 {
     MOZ_ASSERT(!mozilla::IsNaN(size));
     MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
 
     if (weight > 900)
         weight = 900;
     if (weight < 100)
         weight = 100;
@@ -3937,37 +3937,16 @@ gfxFontStyle::gfxFontStyle(uint8_t aStyl
     }
 
     if (!language) {
         NS_WARNING("null language");
         language = nsGkAtoms::x_western;
     }
 }
 
-gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
-    language(aStyle.language),
-    featureValueLookup(aStyle.featureValueLookup),
-    size(aStyle.size), sizeAdjust(aStyle.sizeAdjust),
-    baselineOffset(aStyle.baselineOffset),
-    languageOverride(aStyle.languageOverride),
-    weight(aStyle.weight), stretch(aStyle.stretch),
-    systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
-    useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
-    style(aStyle.style),
-    allowSyntheticWeight(aStyle.allowSyntheticWeight),
-    allowSyntheticStyle(aStyle.allowSyntheticStyle),
-    noFallbackVariantFeatures(aStyle.noFallbackVariantFeatures),
-    explicitLanguage(aStyle.explicitLanguage),
-    variantCaps(aStyle.variantCaps),
-    variantSubSuper(aStyle.variantSubSuper)
-{
-    featureSettings.AppendElements(aStyle.featureSettings);
-    alternateValues.AppendElements(aStyle.alternateValues);
-}
-
 int8_t
 gfxFontStyle::ComputeWeight() const
 {
     int8_t baseWeight = (weight + 50) / 100;
 
     if (baseWeight < 0)
         baseWeight = 0;
     if (baseWeight > 9)
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_FONT_H
 #define GFX_FONT_H
 
 #include "gfxTypes.h"
 #include "gfxFontEntry.h"
+#include "gfxFontVariations.h"
 #include "nsString.h"
 #include "gfxPoint.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "gfxRect.h"
 #include "nsExpirationTracker.h"
@@ -76,17 +77,16 @@ class GlyphRenderingOptions;
 struct gfxFontStyle {
     gfxFontStyle();
     gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
                  gfxFloat aSize, nsIAtom *aLanguage, bool aExplicitLanguage,
                  float aSizeAdjust, bool aSystemFont,
                  bool aPrinterFont,
                  bool aWeightSynthesis, bool aStyleSynthesis,
                  const nsString& aLanguageOverride);
-    gfxFontStyle(const gfxFontStyle& aStyle);
 
     // the language (may be an internal langGroup code rather than an actual
     // language code) specified in the document or element's lang property,
     // or inferred from the charset
     RefPtr<nsIAtom> language;
 
     // Features are composed of (1) features from style rules (2) features
     // from feature setttings rules and (3) family-specific features.  (1) and
@@ -100,16 +100,19 @@ struct gfxFontStyle {
     // font matching occurs.
 
     // -- list of value tags for specific alternate features
     nsTArray<gfxAlternateValue> alternateValues;
 
     // -- object used to look these up once the font is matched
     RefPtr<gfxFontFeatureValueSet> featureValueLookup;
 
+    // opentype variation settings
+    nsTArray<gfxFontVariation> variationSettings;
+
     // The logical size of the font, in pixels
     gfxFloat 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 -1.0 means no adjustment
     // needs to be done; otherwise the value must be nonnegative.
     float sizeAdjust;
@@ -131,48 +134,48 @@ struct gfxFontStyle {
 
     // The weight of the font: 100, 200, ... 900.
     uint16_t weight;
 
     // The stretch of the font (the sum of various NS_FONT_STRETCH_*
     // constants; see gfxFontConstants.h).
     int8_t stretch;
 
+    // The style of font (normal, italic, oblique)
+    uint8_t style;
+
+    // caps variant (small-caps, petite-caps, etc.)
+    uint8_t variantCaps;
+
+    // sub/superscript variant
+    uint8_t variantSubSuper;
+
     // Say that this font is a system font and therefore does not
     // require certain fixup that we do for fonts from untrusted
     // sources.
     bool systemFont : 1;
 
     // Say that this font is used for print or print preview.
     bool printerFont : 1;
 
     // Used to imitate -webkit-font-smoothing: antialiased
     bool useGrayscaleAntialiasing : 1;
 
-    // The style of font (normal, italic, oblique)
-    uint8_t style : 2;
-
     // Whether synthetic styles are allowed
     bool allowSyntheticWeight : 1;
     bool allowSyntheticStyle : 1;
 
     // some variant features require fallback which complicates the shaping
     // code, so set up a bool to indicate when shaping with fallback is needed
     bool noFallbackVariantFeatures : 1;
 
     // whether the |language| field comes from explicit lang tagging in the
     // document, or was inferred from charset/system locale
     bool explicitLanguage : 1;
 
-    // caps variant (small-caps, petite-caps, etc.)
-    uint8_t variantCaps;
-
-    // sub/superscript variant
-    uint8_t variantSubSuper;
-
     // Return the final adjusted font size for the given aspect ratio.
     // Not meant to be called when sizeAdjust = -1.0.
     gfxFloat GetAdjustedSize(gfxFloat aspect) const {
         NS_ASSERTION(sizeAdjust >= 0.0, "Not meant to be called when sizeAdjust = -1.0");
         gfxFloat adjustedSize = std::max(NS_round(size*(sizeAdjust/aspect)), 1.0);
         return std::min(adjustedSize, FONT_MAX_SIZE);
     }
 
@@ -188,39 +191,37 @@ struct gfxFontStyle {
     // variantSubSuper field) using size and baselineOffset instead.
     void AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel);
 
     bool Equals(const gfxFontStyle& other) const {
         return
             (*reinterpret_cast<const uint64_t*>(&size) ==
              *reinterpret_cast<const uint64_t*>(&other.size)) &&
             (style == other.style) &&
+            (weight == other.weight) &&
+            (stretch == other.stretch) &&
             (variantCaps == other.variantCaps) &&
             (variantSubSuper == other.variantSubSuper) &&
             (allowSyntheticWeight == other.allowSyntheticWeight) &&
             (allowSyntheticStyle == other.allowSyntheticStyle) &&
             (systemFont == other.systemFont) &&
             (printerFont == other.printerFont) &&
             (useGrayscaleAntialiasing == other.useGrayscaleAntialiasing) &&
             (explicitLanguage == other.explicitLanguage) &&
-            (weight == other.weight) &&
-            (stretch == other.stretch) &&
             (language == other.language) &&
             (baselineOffset == other.baselineOffset) &&
             (*reinterpret_cast<const uint32_t*>(&sizeAdjust) ==
              *reinterpret_cast<const uint32_t*>(&other.sizeAdjust)) &&
             (featureSettings == other.featureSettings) &&
-            (languageOverride == other.languageOverride) &&
             (alternateValues == other.alternateValues) &&
-            (featureValueLookup == other.featureValueLookup);
+            (featureValueLookup == other.featureValueLookup) &&
+            (variationSettings == other.variationSettings) &&
+            (languageOverride == other.languageOverride);
     }
 
-    static void ParseFontFeatureSettings(const nsString& aFeatureString,
-                                         nsTArray<gfxFontFeature>& aFeatures);
-
     static uint32_t ParseFontLanguageOverride(const nsString& aLangTag);
 };
 
 struct gfxTextRange {
     enum {
         // flags for recording the kind of font-matching that was used
         kFontGroup      = 0x0001,
         kPrefsFallback  = 0x0002,
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxFontVariations.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FONT_VARIATIONS_H
+#define GFX_FONT_VARIATIONS_H
+
+// An OpenType variation tag and value pair
+struct gfxFontVariation {
+    uint32_t mTag;
+    float mValue;
+};
+
+inline bool
+operator==(const gfxFontVariation& a, const gfxFontVariation& b)
+{
+    return (a.mTag == b.mTag) && (a.mValue == b.mValue);
+}
+
+#endif
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -21,16 +21,17 @@ EXPORTS += [
     'gfxFontConstants.h',
     'gfxFontEntry.h',
     'gfxFontFamilyList.h',
     'gfxFontFeatures.h',
     'gfxFontInfoLoader.h',
     'gfxFontPrefLangList.h',
     'gfxFontTest.h',
     'gfxFontUtils.h',
+    'gfxFontVariations.h',
     'gfxGradientCache.h',
     'gfxImageSurface.h',
     'gfxLineSegment.h',
     'gfxMathTable.h',
     'gfxMatrix.h',
     'gfxPattern.h',
     'gfxPlatform.h',
     'gfxPoint.h',
--- a/layout/reftests/dom/reftest.list
+++ b/layout/reftests/dom/reftest.list
@@ -47,9 +47,9 @@
 # followed by appending a node that might not be done lazily
 == multipleappendwithxul.xhtml multipleappendwithxul-ref.xhtml
 == multipleappendwithinput.xhtml multipleappendwithinput-ref.xhtml
 == multipleappendwitheditable.xhtml multipleappendwitheditable-ref.xhtml
 
 == xbl-children-1.xhtml xbl-children-1-ref.xhtml
 == xbl-children-2.xhtml about:blank
 == xbl-children-3.xhtml xbl-children-3-ref.html
-== xbl-children-4.xhtml xbl-children-4-ref.html
+== xbl-children-4.xhtml about:blank
--- a/layout/reftests/dom/xbl-children-2.xhtml
+++ b/layout/reftests/dom/xbl-children-2.xhtml
@@ -1,26 +1,29 @@
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:xbl="http://www.mozilla.org/xbl">
 <head>
   <style>
-    .forced { display:block; }
+    .forced { display: block; }
+    .forced ~ p { display: none; }
   </style>
 </head>
 <body>
   <xbl:children>
     FAIL
   </xbl:children>
 
   <xbl:children class="forced">
     FAIL
   </xbl:children>
 
   <xbl:children />
 
+  <p>FAIL</p>
+
   <script>
     var third = document.body.children[2];
     third.appendChild(document.createTextNode("FAIL"));
 
     var fourth = document.createElementNS("http://www.mozilla.org/xbl", "children");
     fourth.appendChild(document.createTextNode("FAIL"));
     document.body.appendChild(fourth);
   </script>
deleted file mode 100644
--- a/layout/reftests/dom/xbl-children-4-ref.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-    <body>
-        <div>
-            <span>PASS</span>
-        </div>
-    </body>
-</html>
--- a/layout/reftests/dom/xbl-children-4.xhtml
+++ b/layout/reftests/dom/xbl-children-4.xhtml
@@ -15,15 +15,15 @@
             /* First, schedule a pending restyle of the whole tree. */
             var newSheet = document.createElementNS("http://www.w3.org/1999/xhtml", "style");
             newSheet.appendChild(document.createTextNode("#nosuchelement { }"));
             document.head.appendChild(newSheet);
 
             /* Now, append a frame to our children element, causing the pending restyle to descend into it. */
             var children = document.getElementsByTagName("xbl:children")[0];
             var span = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
-            span.appendChild(document.createTextNode("PASS"));
+            span.appendChild(document.createTextNode("FAIL"));
             children.appendChild(span);
             document.documentElement.removeAttribute("class");
         }
     </script>
 </body>
 </html>
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -1764,16 +1764,17 @@ StyleAnimationValue::ComputeDistance(nsC
           const nsCSSValue &v2 = list2->*(pairListValues[i]);
           nsCSSUnit unit =
             GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
           if (unit == eCSSUnit_Null) {
             return false;
           }
           double diffsquared = 0.0;
           switch (unit) {
+            case eCSSUnit_Number:
             case eCSSUnit_Pixel: {
               float diff = v1.GetFloatValue() - v2.GetFloatValue();
               diffsquared = diff * diff;
               break;
             }
             case eCSSUnit_Percent: {
               float diff = v1.GetPercentValue() - v2.GetPercentValue();
               diffsquared = diff * diff;
@@ -1917,16 +1918,32 @@ AppendToCSSValueList(UniquePtr<nsCSSValu
   if (!aHead) {
     aHead = Move(aValueToAppend);
     *aTail = aHead.get();
   } else {
     (*aTail) = (*aTail)->mNext = aValueToAppend.release();
   }
 }
 
+void
+AppendToCSSValuePairList(UniquePtr<nsCSSValuePairList>& aHead,
+                         UniquePtr<nsCSSValuePairList>&& aValueToAppend,
+                         nsCSSValuePairList** aTail)
+{
+  MOZ_ASSERT(!aHead == !*aTail,
+             "Can't have head w/o tail, & vice versa");
+
+  if (!aHead) {
+    aHead = Move(aValueToAppend);
+    *aTail = aHead.get();
+  } else {
+    (*aTail) = (*aTail)->mNext = aValueToAppend.release();
+  }
+}
+
 static UniquePtr<nsCSSValueList>
 AddWeightedShadowItems(double aCoeff1, const nsCSSValue &aValue1,
                        double aCoeff2, const nsCSSValue &aValue2,
                        ColorAdditionType aColorAdditionType)
 {
   // X, Y, Radius, Spread, Color, Inset
   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Array,
              "wrong unit");
@@ -2268,19 +2285,23 @@ AddCSSValuePairList(nsCSSPropertyID aPro
       const nsCSSValue& v2 = aList2->*(pairListValues[i]);
 
       nsCSSValue& vr = resultPtr->*(pairListValues[i]);
       nsCSSUnit unit =
         GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
       if (unit == eCSSUnit_Null) {
         return nullptr;
       }
-      if (!AddCSSValuePixelPercentCalc(restrictions, unit,
-                                       aCoeff1, v1,
-                                       aCoeff2, v2, vr)) {
+      if (unit == eCSSUnit_Number) {
+        AddCSSValueNumber(aCoeff1, v1,
+                          aCoeff2, v2,
+                          vr, restrictions);
+      } else if (!AddCSSValuePixelPercentCalc(restrictions, unit,
+                                              aCoeff1, v1,
+                                              aCoeff2, v2, vr)) {
         if (v1 != v2) {
           return nullptr;
         }
         vr = v1;
       }
     }
     aList1 = aList1->mNext;
     aList2 = aList2->mNext;
@@ -4494,16 +4515,44 @@ StyleAnimationValue::ExtractComputedValu
             result->mValue.SetNoneValue();
           }
 
           aComputedValue.SetTransformValue(
               new nsCSSValueSharedList(result.forget()));
           break;
         }
 
+        case eCSSProperty_font_variation_settings: {
+          auto font = static_cast<const nsStyleFont*>(styleStruct);
+          UniquePtr<nsCSSValuePairList> result;
+          if (!font->mFont.fontVariationSettings.IsEmpty()) {
+            // Make a new list that clones the current settings
+            nsCSSValuePairList* tail = nullptr;
+            for (auto v : font->mFont.fontVariationSettings) {
+              auto clone = MakeUnique<nsCSSValuePairList>();
+              // OpenType font tags are stored in nsFont as 32-bit unsigned
+              // values, but represented in CSS as 4-character ASCII strings,
+              // beginning with the high byte of the value. So to clone the
+              // tag here, we append each of its 4 bytes to a string.
+              nsAutoString tagString;
+              tagString.Append(char(v.mTag >> 24));
+              tagString.Append(char(v.mTag >> 16));
+              tagString.Append(char(v.mTag >> 8));
+              tagString.Append(char(v.mTag));
+              clone->mXValue.SetStringValue(tagString, eCSSUnit_String);
+              clone->mYValue.SetFloatValue(v.mValue, eCSSUnit_Number);
+              AppendToCSSValuePairList(result, Move(clone), &tail);
+            }
+            aComputedValue.SetAndAdoptCSSValuePairListValue(result.release());
+          } else {
+            aComputedValue.SetNormalValue();
+          }
+          break;
+        }
+
         default:
           MOZ_ASSERT(false, "missing property implementation");
           return false;
       };
       return true;
     case eStyleAnimType_Coord: {
       const nsStyleCoord& coord =
         StyleDataAtOffset<nsStyleCoord>(styleStruct, ssOffset);
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -1051,16 +1051,17 @@ protected:
   bool ParseFontVariantEastAsian(nsCSSValue& aValue);
   bool ParseFontVariantLigatures(nsCSSValue& aValue);
   bool ParseFontVariantNumeric(nsCSSValue& aValue);
   bool ParseFontVariant();
   bool ParseFontWeight(nsCSSValue& aValue);
   bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted);
   bool ParseFamily(nsCSSValue& aValue);
   bool ParseFontFeatureSettings(nsCSSValue& aValue);
+  bool ParseFontVariationSettings(nsCSSValue& aValue);
   bool ParseFontSrc(nsCSSValue& aValue);
   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
   bool ParseFontRanges(nsCSSValue& aValue);
   bool ParseListStyle();
   bool ParseListStyleType(nsCSSValue& aValue);
   bool ParseMargin();
   bool ParseClipPath(nsCSSValue& aValue);
   bool ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues = false);
@@ -12094,16 +12095,18 @@ CSSParserImpl::ParseSingleValuePropertyB
     case eCSSProperty_font_variant_east_asian:
       return ParseFontVariantEastAsian(aValue);
     case eCSSProperty_font_variant_ligatures:
       return ParseFontVariantLigatures(aValue);
     case eCSSProperty_font_variant_numeric:
       return ParseFontVariantNumeric(aValue);
     case eCSSProperty_font_feature_settings:
       return ParseFontFeatureSettings(aValue);
+    case eCSSProperty_font_variation_settings:
+      return ParseFontVariationSettings(aValue);
     case eCSSProperty_font_weight:
       return ParseFontWeight(aValue);
     case eCSSProperty_image_orientation:
       return ParseImageOrientation(aValue);
     case eCSSProperty_list_style_type:
       return ParseListStyleType(aValue);
     case eCSSProperty_scroll_snap_points_x:
       return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x);
@@ -15303,17 +15306,19 @@ ValidFontFeatureTag(const nsString& aTag
 bool
 CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue)
 {
   if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
                               nullptr)) {
     return true;
   }
 
-  nsCSSValuePairList *cur = aValue.SetPairListValue();
+  auto resultHead = MakeUnique<nsCSSValuePairList>();
+  nsCSSValuePairList* cur = resultHead.get();
+
   for (;;) {
     // feature tag
     if (!GetToken(true)) {
       return false;
     }
 
     if (mToken.mType != eCSSToken_String ||
         !ValidFontFeatureTag(mToken.mIdent)) {
@@ -15346,16 +15351,67 @@ CSSParserImpl::ParseFontFeatureSettings(
     if (!ExpectSymbol(',', true)) {
       break;
     }
 
     cur->mNext = new nsCSSValuePairList;
     cur = cur->mNext;
   }
 
+  aValue.AdoptPairListValue(Move(resultHead));
+
+  return true;
+}
+
+bool
+CSSParserImpl::ParseFontVariationSettings(nsCSSValue& aValue)
+{
+  if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL,
+                              nullptr)) {
+    return true;
+  }
+
+  auto resultHead = MakeUnique<nsCSSValuePairList>();
+  nsCSSValuePairList* cur = resultHead.get();
+
+  for (;;) {
+    // variation tag
+    if (!GetToken(true)) {
+      return false;
+    }
+
+    // variation tags are subject to the same validation as feature tags
+    if (mToken.mType != eCSSToken_String ||
+        !ValidFontFeatureTag(mToken.mIdent)) {
+      UngetToken();
+      return false;
+    }
+    cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String);
+
+    if (!GetToken(true)) {
+      return false;
+    }
+
+    if (mToken.mType == eCSSToken_Number) {
+      cur->mYValue.SetFloatValue(mToken.mNumber, eCSSUnit_Number);
+    } else {
+      UngetToken();
+      return false;
+    }
+
+    if (!ExpectSymbol(',', true)) {
+      break;
+    }
+
+    cur->mNext = new nsCSSValuePairList;
+    cur = cur->mNext;
+  }
+
+  aValue.AdoptPairListValue(Move(resultHead));
+
   return true;
 }
 
 bool
 CSSParserImpl::ParseListStyle()
 {
   // 'list-style' can accept 'none' for two different subproperties,
   // 'list-style-type' and 'list-style-image'.  In order to accept
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2032,16 +2032,30 @@ CSS_PROP_FONT(
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
     "",
     VARIANT_HMK,
     kFontVariantPositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_Discrete)
 CSS_PROP_FONT(
+    font-variation-settings,
+    font_variation_settings,
+    FontVariationSettings,
+    CSS_PROPERTY_PARSE_VALUE |
+        CSS_PROPERTY_VALUE_PARSER_FUNCTION |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+        CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
+        CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
+    "layout.css.font-variations.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_Custom)
+CSS_PROP_FONT(
     font-weight,
     font_weight,
     FontWeight,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_PARSER_FUNCTION |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
         // NOTE: This property has range restrictions on interpolation!
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1584,16 +1584,33 @@ nsComputedDOMStyle::DoGetFontFeatureSett
     nsStyleUtil::AppendFontFeatureSettings(font->mFont.fontFeatureSettings,
                                            result);
     val->SetString(result);
   }
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
+nsComputedDOMStyle::DoGetFontVariationSettings()
+{
+  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+
+  const nsStyleFont* font = StyleFont();
+  if (font->mFont.fontVariationSettings.IsEmpty()) {
+    val->SetIdent(eCSSKeyword_normal);
+  } else {
+    nsAutoString result;
+    nsStyleUtil::AppendFontVariationSettings(font->mFont.fontVariationSettings,
+                                             result);
+    val->SetString(result);
+  }
+  return val.forget();
+}
+
+already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetFontKerning()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.kerning,
                                    nsCSSProps::kFontKerningKTable));
   return val.forget();
 }
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -251,16 +251,17 @@ private:
   already_AddRefed<CSSValue> DoGetRight();
   already_AddRefed<CSSValue> DoGetBottom();
   already_AddRefed<CSSValue> DoGetStackSizing();
 
   /* Font properties */
   already_AddRefed<CSSValue> DoGetColor();
   already_AddRefed<CSSValue> DoGetFontFamily();
   already_AddRefed<CSSValue> DoGetFontFeatureSettings();
+  already_AddRefed<CSSValue> DoGetFontVariationSettings();
   already_AddRefed<CSSValue> DoGetFontKerning();
   already_AddRefed<CSSValue> DoGetFontLanguageOverride();
   already_AddRefed<CSSValue> DoGetFontSize();
   already_AddRefed<CSSValue> DoGetFontSizeAdjust();
   already_AddRefed<CSSValue> DoGetOsxFontSmoothing();
   already_AddRefed<CSSValue> DoGetFontStretch();
   already_AddRefed<CSSValue> DoGetFontStyle();
   already_AddRefed<CSSValue> DoGetFontSynthesis();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -139,16 +139,17 @@ COMPUTED_STYLE_PROP(font_style,         
 COMPUTED_STYLE_PROP(font_synthesis,                FontSynthesis)
 COMPUTED_STYLE_PROP(font_variant,                  FontVariant)
 COMPUTED_STYLE_PROP(font_variant_alternates,       FontVariantAlternates)
 COMPUTED_STYLE_PROP(font_variant_caps,             FontVariantCaps)
 COMPUTED_STYLE_PROP(font_variant_east_asian,       FontVariantEastAsian)
 COMPUTED_STYLE_PROP(font_variant_ligatures,        FontVariantLigatures)
 COMPUTED_STYLE_PROP(font_variant_numeric,          FontVariantNumeric)
 COMPUTED_STYLE_PROP(font_variant_position,         FontVariantPosition)
+COMPUTED_STYLE_PROP(font_variation_settings,       FontVariationSettings)
 COMPUTED_STYLE_PROP(font_weight,                   FontWeight)
 COMPUTED_STYLE_PROP(grid_auto_columns,             GridAutoColumns)
 COMPUTED_STYLE_PROP(grid_auto_flow,                GridAutoFlow)
 COMPUTED_STYLE_PROP(grid_auto_rows,                GridAutoRows)
 COMPUTED_STYLE_PROP(grid_column_end,               GridColumnEnd)
 COMPUTED_STYLE_PROP(grid_column_gap,               GridColumnGap)
 COMPUTED_STYLE_PROP(grid_column_start,             GridColumnStart)
 COMPUTED_STYLE_PROP(grid_row_end,                  GridRowEnd)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3960,43 +3960,78 @@ nsRuleNode::SetFont(nsPresContext* aPres
            defaultVariableFont->variantPosition,
            Unused, Unused, /* normal */ 0, systemFont.variantPosition);
 
   // font-feature-settings
   const nsCSSValue* featureSettingsValue =
     aRuleData->ValueForFontFeatureSettings();
 
   switch (featureSettingsValue->GetUnit()) {
-  case eCSSUnit_Null:
-    break;
-
-  case eCSSUnit_Normal:
-  case eCSSUnit_Initial:
-    aFont->mFont.fontFeatureSettings.Clear();
-    break;
-
-  case eCSSUnit_Inherit:
-  case eCSSUnit_Unset:
-    aConditions.SetUncacheable();
-    aFont->mFont.fontFeatureSettings = aParentFont->mFont.fontFeatureSettings;
-    break;
-
-  case eCSSUnit_System_Font:
-    aFont->mFont.fontFeatureSettings = systemFont.fontFeatureSettings;
-    break;
-
-  case eCSSUnit_PairList:
-  case eCSSUnit_PairListDep:
-    ComputeFontFeatures(featureSettingsValue->GetPairListValue(),
-                        aFont->mFont.fontFeatureSettings);
-    break;
-
-  default:
-    MOZ_ASSERT(false, "unexpected value unit");
-    break;
+    case eCSSUnit_Null:
+      break;
+
+    case eCSSUnit_Normal:
+    case eCSSUnit_Initial:
+      aFont->mFont.fontFeatureSettings.Clear();
+      break;
+
+    case eCSSUnit_Inherit:
+    case eCSSUnit_Unset:
+      aConditions.SetUncacheable();
+      aFont->mFont.fontFeatureSettings = aParentFont->mFont.fontFeatureSettings;
+      break;
+
+    case eCSSUnit_System_Font:
+      aFont->mFont.fontFeatureSettings = systemFont.fontFeatureSettings;
+      break;
+
+    case eCSSUnit_PairList:
+    case eCSSUnit_PairListDep:
+      ComputeFontFeatures(featureSettingsValue->GetPairListValue(),
+                          aFont->mFont.fontFeatureSettings);
+      break;
+
+    default:
+      MOZ_ASSERT(false, "unexpected value unit");
+      break;
+  }
+
+  // font-variation-settings
+  const nsCSSValue* variationSettingsValue =
+    aRuleData->ValueForFontVariationSettings();
+
+  switch (variationSettingsValue->GetUnit()) {
+    case eCSSUnit_Null:
+      break;
+
+    case eCSSUnit_Normal:
+    case eCSSUnit_Initial:
+      aFont->mFont.fontVariationSettings.Clear();
+      break;
+
+    case eCSSUnit_Inherit:
+    case eCSSUnit_Unset:
+      aConditions.SetUncacheable();
+      aFont->mFont.fontVariationSettings =
+        aParentFont->mFont.fontVariationSettings;
+      break;
+
+    case eCSSUnit_System_Font:
+      aFont->mFont.fontVariationSettings = systemFont.fontVariationSettings;
+      break;
+
+    case eCSSUnit_PairList:
+    case eCSSUnit_PairListDep:
+      ComputeFontVariations(variationSettingsValue->GetPairListValue(),
+                            aFont->mFont.fontVariationSettings);
+      break;
+
+    default:
+      MOZ_ASSERT(false, "unexpected value unit");
+      break;
   }
 
   // font-language-override
   const nsCSSValue* languageOverrideValue =
     aRuleData->ValueForFontLanguageOverride();
   if (eCSSUnit_Inherit == languageOverrideValue->GetUnit() ||
       eCSSUnit_Unset == languageOverrideValue->GetUnit()) {
     aConditions.SetUncacheable();
@@ -4121,46 +4156,90 @@ nsRuleNode::SetFont(nsPresContext* aPres
   if (eCSSUnit_System_Font == sizeAdjustValue->GetUnit()) {
     aFont->mFont.sizeAdjust = systemFont.sizeAdjust;
   } else
     SetFactor(*sizeAdjustValue, aFont->mFont.sizeAdjust,
               aConditions, aParentFont->mFont.sizeAdjust, -1.0f,
               SETFCT_NONE | SETFCT_UNSET_INHERIT);
 }
 
+static inline void
+AssertValidFontTag(const nsString& aString)
+{
+  // To be valid as a font feature tag, a string MUST be:
+  MOZ_ASSERT(aString.Length() == 4 &&              // (1) exactly 4 chars long
+             NS_IsAscii(aString.BeginReading()) && // (2) entirely ASCII
+             isprint(aString[0]) &&                // (3) all printable chars
+             isprint(aString[1]) &&
+             isprint(aString[2]) &&
+             isprint(aString[3]));
+}
+
 /* static */ void
 nsRuleNode::ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList,
                                 nsTArray<gfxFontFeature>& aFeatureSettings)
 {
   aFeatureSettings.Clear();
   for (const nsCSSValuePairList* p = aFeaturesList; p; p = p->mNext) {
-    gfxFontFeature feat = {0, 0};
+    gfxFontFeature feat;
 
     MOZ_ASSERT(aFeaturesList->mXValue.GetUnit() == eCSSUnit_String,
                "unexpected value unit");
 
     // tag is a 4-byte ASCII sequence
     nsAutoString tag;
     p->mXValue.GetStringValue(tag);
+    AssertValidFontTag(tag);
     if (tag.Length() != 4) {
       continue;
     }
     // parsing validates that these are ASCII chars
     // tags are always big-endian
     feat.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8)  | tag[3];
 
     // value
     NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Integer,
                  "should have found an integer unit");
     feat.mValue = p->mYValue.GetIntValue();
 
     aFeatureSettings.AppendElement(feat);
   }
 }
 
+/* static */ void
+nsRuleNode::ComputeFontVariations(const nsCSSValuePairList* aVariationsList,
+                                  nsTArray<gfxFontVariation>& aVariationSettings)
+{
+  aVariationSettings.Clear();
+  for (const nsCSSValuePairList* p = aVariationsList; p; p = p->mNext) {
+    gfxFontVariation var;
+
+    MOZ_ASSERT(aVariationsList->mXValue.GetUnit() == eCSSUnit_String,
+               "unexpected value unit");
+
+    // tag is a 4-byte ASCII sequence
+    nsAutoString tag;
+    p->mXValue.GetStringValue(tag);
+    AssertValidFontTag(tag);
+    if (tag.Length() != 4) {
+      continue;
+    }
+    // parsing validates that these are ASCII chars
+    // tags are always big-endian
+    var.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8)  | tag[3];
+
+    // value
+    NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Number,
+                 "should have found a number unit");
+    var.mValue = p->mYValue.GetFloatValue();
+
+    aVariationSettings.AppendElement(var);
+  }
+}
+
 // This should die (bug 380915).
 //
 // SetGenericFont:
 //  - backtrack to an ancestor with the same generic font name (possibly
 //    up to the root where default values come from the presentation context)
 //  - re-apply cascading rules from there without caching intermediate values
 /* static */ void
 nsRuleNode::SetGenericFont(nsPresContext* aPresContext,
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -1017,19 +1017,22 @@ public:
   }
 
   // Note that this will return false if we have cached conditional
   // style structs.
   bool NodeHasCachedUnconditionalData(const nsStyleStructID aSID) {
     return !!mStyleData.GetStyleData(aSID);
   }
 
-  static void ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList,
+  static void ComputeFontFeatures(const nsCSSValuePairList* aFeaturesList,
                                   nsTArray<gfxFontFeature>& aFeatureSettings);
 
+  static void ComputeFontVariations(const nsCSSValuePairList* aVariationsList,
+                                    nsTArray<gfxFontVariation>& aVariationSettings);
+
   static nscoord CalcFontPointSize(int32_t aHTMLSize, int32_t aBasePointSize,
                                    nsPresContext* aPresContext,
                                    nsFontSizeType aFontSizeType = eFontSize_HTML);
 
   static nscoord FindNextSmallerFontSize(nscoord aFontSize, int32_t aBasePointSize,
                                          nsPresContext* aPresContext,
                                          nsFontSizeType aFontSizeType = eFontSize_HTML);
 
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -11,16 +11,17 @@
 #include "nsContentUtils.h"
 #include "nsRuleNode.h"
 #include "nsROCSSPrimitiveValue.h"
 #include "nsStyleStruct.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIURI.h"
 #include "nsPrintfCString.h"
+#include <cctype>
 
 using namespace mozilla;
 
 //------------------------------------------------------------------------------
 // Font Algorithm Code
 //------------------------------------------------------------------------------
 
 // Compare two language strings
@@ -323,36 +324,45 @@ nsStyleUtil::AppendPaintOrderValue(uint8
       default:
         NS_NOTREACHED("unexpected paint-order component value");
     }
     aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
   }
 }
 
 /* static */ void
+nsStyleUtil::AppendFontTagAsString(uint32_t aTag, nsAString& aResult)
+{
+  // A font tag (for feature/variation settings) is a 4-char code interpreted
+  // as a bigendian 32-bit value and stored/processed as a uint32_t.
+  // To serialize it, we put the four bytes (which are all guaranteed to be
+  // printable ASCII values) into a string, starting from the high byte of the
+  // value, then append that to the result with CSS escaping and quotes.
+  nsAutoString tagStr;
+  for (int shiftAmount = 24; shiftAmount >= 0; shiftAmount -= 8) {
+    char c = (aTag >> shiftAmount) & 0xff;
+    MOZ_ASSERT(isascii(c) && isprint(c),
+               "parser should have restricted tag to printable ASCII chars");
+    tagStr.Append(c);
+  }
+  AppendEscapedCSSString(tagStr, aResult);
+}
+
+/* static */ void
 nsStyleUtil::AppendFontFeatureSettings(const nsTArray<gfxFontFeature>& aFeatures,
                                        nsAString& aResult)
 {
   for (uint32_t i = 0, numFeat = aFeatures.Length(); i < numFeat; i++) {
     const gfxFontFeature& feat = aFeatures[i];
 
     if (i != 0) {
-        aResult.AppendLiteral(", ");
+      aResult.AppendLiteral(", ");
     }
 
-    // output tag
-    char tag[7];
-    tag[0] = '"';
-    tag[1] = (feat.mTag >> 24) & 0xff;
-    tag[2] = (feat.mTag >> 16) & 0xff;
-    tag[3] = (feat.mTag >> 8) & 0xff;
-    tag[4] = feat.mTag & 0xff;
-    tag[5] = '"';
-    tag[6] = 0;
-    aResult.AppendASCII(tag);
+    AppendFontTagAsString(feat.mTag, aResult);
 
     // output value, if necessary
     if (feat.mValue == 0) {
       // 0 ==> off
       aResult.AppendLiteral(" off");
     } else if (feat.mValue > 1) {
       aResult.Append(' ');
       aResult.AppendInt(feat.mValue);
@@ -376,16 +386,55 @@ nsStyleUtil::AppendFontFeatureSettings(c
                   "improper value unit for font-feature-settings:");
 
   nsTArray<gfxFontFeature> featureSettings;
   nsRuleNode::ComputeFontFeatures(aSrc.GetPairListValue(), featureSettings);
   AppendFontFeatureSettings(featureSettings, aResult);
 }
 
 /* static */ void
+nsStyleUtil::AppendFontVariationSettings(const nsTArray<gfxFontVariation>& aVariations,
+                                         nsAString& aResult)
+{
+  for (uint32_t i = 0, numVars = aVariations.Length(); i < numVars; i++) {
+    const gfxFontVariation& var = aVariations[i];
+
+    if (i != 0) {
+      aResult.AppendLiteral(", ");
+    }
+
+    // output tag
+    AppendFontTagAsString(var.mTag, aResult);
+
+    // output value
+    aResult.Append(' ');
+    aResult.AppendFloat(var.mValue);
+  }
+}
+
+/* static */ void
+nsStyleUtil::AppendFontVariationSettings(const nsCSSValue& aSrc,
+                                         nsAString& aResult)
+{
+  nsCSSUnit unit = aSrc.GetUnit();
+
+  if (unit == eCSSUnit_Normal) {
+    aResult.AppendLiteral("normal");
+    return;
+  }
+
+  NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep,
+                  "improper value unit for font-variation-settings:");
+
+  nsTArray<gfxFontVariation> variationSettings;
+  nsRuleNode::ComputeFontVariations(aSrc.GetPairListValue(), variationSettings);
+  AppendFontVariationSettings(variationSettings, aResult);
+}
+
+/* static */ void
 nsStyleUtil::GetFunctionalAlternatesName(int32_t aFeature,
                                          nsAString& aFeatureName)
 {
   aFeatureName.Truncate();
   nsCSSKeyword key =
     nsCSSProps::ValueToKeywordEnum(aFeature,
                            nsCSSProps::kFontVariantAlternatesFuncsKTable);
 
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -58,22 +58,30 @@ public:
                                     int32_t aFirstMask,
                                     int32_t aLastMask,
                                     nsAString& aResult);
 
   static void AppendAngleValue(const nsStyleCoord& aValue, nsAString& aResult);
 
   static void AppendPaintOrderValue(uint8_t aValue, nsAString& aResult);
 
+  static void AppendFontTagAsString(uint32_t aTag, nsAString& aResult);
+
   static void AppendFontFeatureSettings(const nsTArray<gfxFontFeature>& aFeatures,
                                         nsAString& aResult);
 
   static void AppendFontFeatureSettings(const nsCSSValue& src,
                                         nsAString& aResult);
 
+  static void AppendFontVariationSettings(const nsTArray<gfxFontVariation>& aVariations,
+                                          nsAString& aResult);
+
+  static void AppendFontVariationSettings(const nsCSSValue& src,
+                                          nsAString& aResult);
+
   static void AppendUnicodeRange(const nsCSSValue& aValue, nsAString& aResult);
 
   static void AppendCSSNumber(float aNumber, nsAString& aResult)
   {
     aResult.AppendFloat(aNumber);
   }
 
   static void AppendStepsTimingFunction(nsTimingFunction::Type aType,
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -3027,17 +3027,18 @@ var gCSSProperties = {
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "normal" ],
     other_values: [
       "'liga' on", "'liga'", "\"liga\" 1", "'liga', 'clig' 1",
       "\"liga\" off", "\"liga\" 0", '"cv01" 3, "cv02" 4',
       '"cswh", "smcp" off, "salt" 4', '"cswh" 1, "smcp" off, "salt" 4',
       '"cswh" 0, \'blah\', "liga", "smcp" off, "salt" 4',
-      '"liga"        ,"smcp" 0         , "blah"'
+      '"liga"        ,"smcp" 0         , "blah"',
+      '"ab\\"c"', '"ab\\\\c"'
     ],
     invalid_values: [
       'liga', 'liga 1', 'liga normal', '"liga" normal', 'normal liga',
       'normal "liga"', 'normal, "liga"', '"liga=1"', "'foobar' on",
       '"blahblah" 0', '"liga" 3.14', '"liga" 1 3.14', '"liga" 1 normal',
       '"liga" 1 off', '"liga" on off', '"liga" , 0 "smcp"', '"liga" "smcp"'
     ]
   },
@@ -5681,16 +5682,41 @@ if (IsCSSPropertyPrefEnabled("layout.css
                       "digits 3 all", "digits foo", "digits all", "digits 3.0" ]
   };
   if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright-digits.enabled")) {
     gCSSProperties["text-combine-upright"].other_values.push(
       "digits", "digits 2", "digits 3", "digits 4", "digits     3");
   }
 }
 
+if (IsCSSPropertyPrefEnabled("layout.css.font-variations.enabled")) {
+  gCSSProperties["font-variation-settings"] = {
+    domProp: "fontVariationSettings",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "normal" ],
+    other_values: [
+      "'wdth' 0", "'wdth' -.1", "\"wdth\" 1", "'wdth' 2, 'wght' 3", "\"XXXX\" 0"
+    ],
+    invalid_values: [
+      "wdth", "wdth 1", // unquoted tags
+      "'wdth'", "'wdth' 'wght'", "'wdth', 'wght'", // missing values
+      "'' 1", "'wid' 1", "'width' 1", // incorrect tag lengths
+      "'wd\th' 1", // non-graphic character in tag
+      "'wdth' 1 'wght' 2", // missing comma between pairs
+      "'wdth' 1,", // trailing comma
+      "'wdth' 1 , , 'wght' 2", // extra comma
+      "'wdth', 1" // comma within pair
+    ],
+    unbalanced_values: [
+      "'wdth\" 1", "\"wdth' 1" // mismatched quotes
+    ]
+  }
+}
+
 if (IsCSSPropertyPrefEnabled("svg.paint-order.enabled")) {
   gCSSProperties["paint-order"] = {
     domProp: "paintOrder",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "normal" ],
     other_values: [ "fill", "fill stroke", "fill stroke markers", "stroke markers fill" ],
     invalid_values: [ "fill stroke markers fill", "fill normal" ]
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -300,16 +300,20 @@ if (SupportsMaskShorthand()) {
                                             /* test_length_percent_pair_unclamped */ ];
   supported_properties["mask-size"] = [ test_background_size_transition,
                                      // FIXME: We don't currently test clamping,
                                      // since mask-size uses calc() as an
                                      // intermediate form.
                                      /* test_length_percent_pair_clamped */ ];
 }
 
+if (IsCSSPropertyPrefEnabled("layout.css.font-variations.enabled")) {
+  supported_properties["font-variation-settings"] = [ test_font_variations_transition ];
+}
+
 var div = document.getElementById("display");
 var OMTAdiv = document.getElementById("transformTest");
 var cs = getComputedStyle(div, "");
 var OMTACs = getComputedStyle(OMTAdiv, "");
 var winUtils = SpecialPowers.getDOMWindowUtils(window);
 
 function computeMatrix(v) {
   div.style.setProperty("transform", v, "");
@@ -2447,16 +2451,40 @@ function test_transform_transition(prop)
     }
   }
 
   // FIXME: should perhaps test that no clamping occurs
 
   runOMTATest(runAsyncTests, SimpleTest.finish);
 }
 
+function test_font_variations_transition(prop) {
+  is(prop, "font-variation-settings", "only designed for one property");
+
+  div.style.setProperty("transition-property", "none", "");
+  div.style.setProperty(prop, "\"wght\" 0, \"wdth\" 1.5", "");
+  is(cs.getPropertyValue(prop), "\"wght\" 0, \"wdth\" 1.5",
+     "font-variation-settings property " + prop + ": computed value before transition");
+  div.style.setProperty("transition-property", prop, "");
+  div.style.setProperty(prop, "\"wght\" 2, \"wdth\" 0.5", "");
+  is(cs.getPropertyValue(prop), "\"wght\" 0.5, \"wdth\" 1.25",
+     "font-variation-settings property " + prop + ": interpolation of font-variation-settings");
+  check_distance(prop, "\"wght\" 0, \"wdth\" 1.5", "\"wght\" 0.5, \"wdth\" 1.25", "\"wght\" 2, \"wdth\" 0.5");
+
+  div.style.setProperty("transition-property", "none", "");
+  div.style.setProperty(prop, "\"wght\" 2, \"wdth\" 0.5", "");
+  is(cs.getPropertyValue(prop), "\"wght\" 2, \"wdth\" 0.5",
+     "font-variation-settings property " + prop + ": computed value before transition");
+  div.style.setProperty("transition-property", prop, "");
+  div.style.setProperty(prop, "\"wght\" 0, \"wdth\" 1.5", "");
+  is(cs.getPropertyValue(prop), "\"wght\" 1.5, \"wdth\" 0.75",
+     "font-variation-settings property " + prop + ": interpolation of font-variation-settings");
+  check_distance(prop, "\"wght\" 2, \"wdth\" 0.5", "\"wght\" 1.5, \"wdth\" 0.75", "\"wght\" 0, \"wdth\" 1.5");
+}
+
 function runAsyncTests() {
   // These tests check the value on the compositor 2/3rds of the way through
   // the transition.
   // For the transform tests we simply compare the value on the compositor
   // with the computed value, but for the opacity test we check the absolute
   // value as well.
   OMTAdiv.style.setProperty("transition-duration", "300s", "");
   OMTAdiv.style.setProperty("transition-timing-function", "linear", "");
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2542,16 +2542,19 @@ pref("layout.css.float-logical-values.en
 #endif
 
 // Is support for the CSS4 image-orientation property enabled?
 pref("layout.css.image-orientation.enabled", true);
 
 // Is support for the font-display @font-face descriptor enabled?
 pref("layout.css.font-display.enabled", false);
 
+// Is support for variation fonts enabled?
+pref("layout.css.font-variations.enabled", false);
+
 // Are sets of prefixed properties supported?
 pref("layout.css.prefixes.border-image", true);
 pref("layout.css.prefixes.transforms", true);
 pref("layout.css.prefixes.transitions", true);
 pref("layout.css.prefixes.animations", true);
 pref("layout.css.prefixes.box-sizing", true);
 pref("layout.css.prefixes.font-features", true);
 pref("layout.css.prefixes.gradients", true);
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -45,26 +45,29 @@ window[chromehidden~="toolbar"] .chromec
 
 .uri-element {
   direction: ltr !important;
 }
 
 /****** elements that have no visual representation ******/
 
 script, data,
-xbl|children,
 commands, commandset, command,
 broadcasterset, broadcaster, observes,
 keyset, key, toolbarpalette, toolbarset,
 template, rule, conditions, action,
 bindings, binding, content, member, triple,
 treechildren, treeitem, treeseparator, treerow, treecell {
   display: none;
 }
 
+xbl|children {
+  display: none !important;
+}
+
 /********** focus rules **********/
 
 button,
 checkbox,
 colorpicker[type="button"],
 datepicker[type="grid"],
 menulist,
 radiogroup,
index 220cc1d1e5760e90b0769b1a96a01c3d42923115..82c8d1f874ab7f5a8e3a95f76212706c7b81c388
GIT binary patch
literal 452
zc$@*m0XzPQP)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e0004tNkl<ZcmZ9J
z18^Q;5Qew5ZQHi(4yy6gw%rbE$F}WZZ2J(~$<I0FL)hnh>vy4xfiMGB!bWtgL~jG&
zKV}{}oMW%yWI+}*Ot$r*!^o)zb9`S&8b#!N3!G!W$^!2zHv7JS>0QyjFH}WU+w$@p
zR_*D(<-u8)<9JWNE;F%tz|q5!RwZZ{^BXV{21CCM`^v*4&50wZGnkQWE$gwlAsMwd
z2`CdHJ~pT4*yB!CY8YK<d3e<4&HG_&S64$8??&x%C<Hdjg3ax<dr@pZ=z~q@7<l0X
z>#nMb0Go)zlpQdl|Cat+CTxdEPtH9)3sX}}RD`654Wz`6%P_w@T*K=4AD7e2E}e$C
zC4q(tMbfaak~GP>$Niv<Y8cB~XpUiP_uAa9h@edlIB`U1Ho4wbs1kV3y$4~)_WeH3
zgl0M>z;xdi#@ZY^>L43_UwM8Z?uW6ir<T+FUW#ql+?t#GC*+OLAnA~K8uVZWSr)H|
uvc9b_+XgLbCN=!O?oDXjE703O=%5`+PtvEfyCeSq0000<MNUMnLSTY*?$!zb
index b1b35718685e37b42b5a8de0840229cb991dc878..b5663444e4d27547c6181ac0bd8de742d7de6949
GIT binary patch
literal 1005
zc$@+40}}j+P)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg000BDNkl<ZcmcK5
z1AHq>6bA59x7W3|eYTnE+O}<5y|!)Jwr#t%y>aq_J#S{y?5~^p;onrrc)r<z|4R*E
z;2Zb_{vcn!kiQu6PK9~fN;VIP*vG!n<#Retr^{#GMDE`_#8v{{P%d|;pd0hmsaOJa
z>W%qMLEsK$@E_bx7J-CbmVF0No11xi!(bhYXi#X<k&s0UZU_EQiomVI^epl?!^z19
z#~qolZ?t<z>`~<O?7+4cXK1oW)9mIU5C|n;_KfCqSm@2x!R>M$P3#P{!0Dr~TZ@<U
zCH?yRP&e%=BB$Fo8kk~ppU}ymLP7SOmm5{_-l6>3TZ0EwHI=$HKl3)3ka|KV@mg3_
z58~%8;Ec#=b24u@2ZI%WpxTOl$>&XLQ9Y>WC*C*aqZpU>B>vQ~N#G1()q1s7C2<DC
zn+u9;s;$Iw<C4$8S7hIv!IJeibrrGE2kEBTN`%yH8H%XMG-|L=^x=Y+@HLd|v-GRa
z*3+_EhMLpKMC?P%zK=Yk=FRqy2BSNIYiS|>vW_Ve5&O)0@eTW$G;mKZ-!uB;<ORJT
z0PMgKO5-r3J@VLB@D1>O(d8p<9VU-iKZvYtRVbYmc>PfK-A?4`^5GZY{Xqld9}3@r
z2Ur#F5!{87==E?_&I4hibnu6`0c)WjD?UE9w0hsx*XC^-PMe@R@Mb%rxB+1bU0Z@Z
zP@c}g?jhjwP2GBp;1j3@4!v4mngwnE;Bsmz3WO<`7je&Ms7ZsevcDoulW}XYJW7?6
ze9kGTZ|g;UUTy?m`q{F+$RSU>h)m>u#E8AXfDQP+--U(ppKTseJxG;E3-Z%(JvUvO
zlYCB<OnT1~wowRDhEveb_3kWI#TO0q&Jm59z&CUPJl|CV;FC22&W39;OhFPw`TUHl
zbM<l*GwP+n@>12BHOYH&X*P|rQxFKeZ!SR8Wc^TgWq(x?ig3DKt)Ui}*!)~?_o!VX
zHCf`r#2;sQ1Y-JCTgl_qD8jyZv-RM3aHgfaLnn6rp>FzBM0up>I1?TdI~&x9v4<Np
zR>X*(4C<7e2jt|{1P}P4UGw;3^no(5Gx^T~b6_kDRQBDMn=IF1VQ+BWIpXGm?EAD6
zPIq7|Fi?WP17m3cJ&Pi-M{Olrhta1YL3$_E1ECbm#Ln_abHB*RA}-D9+@fpCW0O<~
z#=j8h+$VOXy_e2|zgW_+1nM;YV)1}-c{>Gd9ul>meJfmTZ==Z7Mk;$(=5h@XU9JJ-
bo&U$*9(D$Jugrix00000NkvXXu0mjf8?^Xm
index ee65b2d494b867ba2e4b75f78bccff37942bd1d7..4092dfb55da44ca9bc0a447c1a80ced5cf6a34b7
GIT binary patch
literal 485
zc$@*{0UG{^P)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e00053Nkl<ZcmZ8e
zL%dc&6u%k!WnO2lb6r2%wr$(?d-uc4d|K7AUAA4jxJm1N|IW>IX6D!<{Q|7UM(o5v
zIy;G5CH)K7^yEl!ZSdSF?<sG{Y-#nABiQt}dFyCusMS5#Ix#-hI(?;86Dl7~#n#`^
zqp2x#@9Qtuj`Zt75wVM!h;*r5a<Q{u)uW>!&X3hdLh8+`yhWYsOlP5Ndc@%Ok72ch
zjpa4%lSoATnUYgJjm!oeP1KGixk~D=QNqsf1y{#nO=Z8yG)~4J4qWxTXoy<CP6-E3
zdord?w<dHl>x@cpjYqoYN04lIaEKffp>c}fvY_rovXyM9qIUwhUlYjT0F{Vc(zxsi
z4BqQ_(2?dG9Eo+P!s>XKd9E<ZvP*1i>6ATPQ=f8?iqtzz8S^S-G~HB|HFn!gu6hx!
zd31510P%EE=lVmbV5mF6QoS!Du-e|lHePemmp4vkI$!cLtRzR(Or)FFVB4=<?&fu}
zuf6?Z-r16>zKo@=@eszUvHS1e*zr2Gwk>4-gkSc?EY`QYPQi}<|8qSKz@#E^Ft8u%
bf9bygRV?bPV>q$700000NkvXXu0mjf5RLQq
index 51fc15219548bacdd6494362c46c19df92915b03..a1b23f68370c1988382d91a5a8c980c1c961bf5b
GIT binary patch
literal 1146
zc$@)x1cm#FP)<h;3K|Lk000e1NJLTq001BW001Be1ONa4*>kdg000C*Nkl<ZcmcJQ
z1#lZj7=@i-^ib0_RPK-_LqTP@cx+}%m&nY_%*=RVI^FJ`%FN8n+zyv<AS8La>obp`
z=x3Dmz8`ll=70LYvG^Kn!pHC_qGRN@Ysf1x+iANO4@AsrdUbo&d|Z%&9E=NQ%~!Rl
z=@BzNus2|T<jg>El84nfoqq91fE+UZO7aX8N6z5=y8({8#HA@rn9;3jzW;-P_XqBu
z$O(Sg_v%ouU4SCsQQG2596quW;5vV%6k@~8uJ;BmH&?p~mEwhBg-5eJO9kK8XQ`4V
zX(EhM)pUH_c7SW#RtB-*`zLS>&g~sc(H`%!_(dQB2{ew&E@GtsKuUrx3%+J6VE<Es
z05Y}D`f(AqV(Yy<qw{aq@?jAW8(79@am3!O<OO&n!lwkhe<NVu1#`Nc3%}ZrbBLYj
za?w)d*QF1RNd&Mi5#+n!*vveqXDAoUc;7nUz+edzZgt^2YNm>wy}xd;t71{y1fV6P
zsvk6FtDHVij1Q~<BIaZ-tH0N3n5pb-%w57t;+qli@b#)3zW|fH5pyIa5<@<5Y#@Wv
znecA~tM<w6r&|QlwCyCoP}|^KipJ`J47#1m{jcxlkyXVqHUDw0Zy+9Iduj69iOd0g
z*#st;aD6wv#sHXPMS#7F-{t1mi{~tE(q%wrz7rr*!?sH$ar#+1HW`3p2_X?=`CqLl
z@JN7Yd#lkzD504-lBt{m;zRhD0XW4YV@1Sm%<EeaN9;q&<D<BomTDoBz^B##+Zc)H
zcT1pKsql!vn84^HUTL))SO*+SB>!BWv<L;rqj-?KoRbLB)s{sO(&`)BG&<E~Swacg
zY6&KrQv_?f=KpZuo8AuR+XJ8U>5~G=4CVO8r|ADhVDm?11Mp%6tLL~@n|MvfumFvE
zc{DSFbFmFq<J^ZEa>%{#N%xC=q{L-Fll{vAd)vDu*jhKu=^ewXV~oA8h|{S(LF;zB
z*R-~e`AdyOfjGeFMt%}mBAcBF%dXN{9<lm;g*bL~m2XWC&}uuCpXx|_>$;Zef9jOr
z#tx-~p6M4M=}aJUtFg*N_<+(z(-ffWdYdG;Q5SMjiA`DoB&pTzJf}#SthC_+8;<iu
z)<Q-_lWZT~40xh?Vy?z*6p^5v^CfCYApu%E7uogWOm)>;1vD`U#DiDMHUcgeeBAhG
zKRuarxT#vVJ((=jTLt*4tykL3_crl}zO8j)Qx0;}X7Tc<+}q`NY|J?wuW{(!CVcaD
zm+H}bo9GgW;N?v@On0%U%y-xDm(9frtBcfrbf;_G^iHXT%$`LyN8i8$wXm^lmK-!F
z2WBQK7U`W~@xD`vO}jl<jzlw@U1=&4CK#PwpAw+cq2@Hz*%i&eNA`ZSnW9-w)OC&L
zxDz!V@hDNtad(YBQ5VfJV($&0I)G1O9;eeDkncSC>%iU**MFox0TD70dzX?3c>n+a
M07*qoM6N<$f`2VG@c;k-