Bug 976787 part 2: Add the grid-template-areas property to the style system. r=dholbert
authorSimon Sapin <simon.sapin@exyr.org>
Mon, 10 Mar 2014 15:54:14 -0700
changeset 190080 a2cb9d6adebc91198718041e04dd7a87240f62c3
parent 190079 96f8a98c9228031bc43325842ad475ba38c5369e
child 190081 a9fb5a9c8ada58a4bff8c4da681c1dee5e41d2aa
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs976787
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 976787 part 2: Add the grid-template-areas property to the style system. r=dholbert
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSScanner.cpp
layout/style/nsCSSScanner.h
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsComputedDOMStylePropertyList.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -631,16 +631,21 @@ protected:
   // For "flex-flow" shorthand property, defined in CSS Flexbox spec
   bool ParseFlexFlow();
 
   // CSS Grid
   bool ParseGridLineNames(nsCSSValue& aValue);
   bool ParseGridTrackBreadth(nsCSSValue& aValue);
   bool ParseGridTrackSize(nsCSSValue& aValue);
   bool ParseGridTrackList(nsCSSProperty aPropID);
+  bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
+                                  nsTArray<nsCSSGridNamedArea>& aNamedAreas,
+                                  uint32_t aRow,
+                                  uint32_t& aColumns);
+  bool ParseGridTemplateAreas();
 
   // for 'clip' and '-moz-image-region'
   bool ParseRect(nsCSSProperty aPropID);
   bool ParseColumns();
   bool ParseContent();
   bool ParseCounterData(nsCSSProperty aPropID);
   bool ParseCursor();
   bool ParseFont();
@@ -5757,17 +5762,17 @@ CSSParserImpl::ParseDeclaration(css::Dec
 
     if (!tk->IsSymbol('}')) {
       REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected);
       REPORT_UNEXPECTED(PEDeclSkipped);
       OUTPUT_ERROR();
 
       if (eCSSToken_AtKeyword == tk->mType) {
         SkipAtRule(checkForBraces);
-        return true;  // Not a declaration, but don’t skip until ';'
+        return true;  // Not a declaration, but don't skip until ';'
       }
     }
     // Not a declaration...
     UngetToken();
     return false;
   }
 
   // Don't report property parse errors if we're inside a failing @supports
@@ -6994,16 +6999,126 @@ CSSParserImpl::ParseGridTrackList(nsCSSP
   } while (!CheckEndProperty());
   MOZ_ASSERT(value.GetListValue() && value.GetListValue()->mNext &&
              value.GetListValue()->mNext->mNext,
              "<track-list> should have a minimum length of 3");
   AppendValue(aPropID, value);
   return true;
 }
 
+bool
+CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput,
+                                          nsTArray<nsCSSGridNamedArea>& aNamedAreas,
+                                          uint32_t aRow,
+                                          uint32_t& aColumns)
+{
+  nsCSSGridTemplateAreaScanner scanner(aInput);
+  nsCSSGridTemplateAreaToken token;
+  nsCSSGridNamedArea* currentArea = nullptr;
+  uint32_t column;
+  for (column = 1; scanner.Next(token); column++) {
+    if (token.isTrash) {
+      return false;
+    }
+    if (currentArea) {
+      if (token.mName == currentArea->mName) {
+        if (currentArea->mRowStart == aRow) {
+          // Next column in the first row of this named area.
+          currentArea->mColumnEnd++;
+        }
+        continue;
+      }
+      // We're exiting |currentArea|, so currentArea is ending at |column|.
+      // Make sure that this is consistent with currentArea on previous rows:
+      if (currentArea->mColumnEnd != column) {
+        NS_ASSERTION(currentArea->mRowStart != aRow,
+                     "Inconsistent column end for the first row of a named area.");
+        // Not a rectangle
+        return false;
+      }
+      currentArea = nullptr;
+    }
+    if (!token.mName.IsEmpty()) {
+      // Named cell that doesn't have a cell with the same name on its left.
+
+      // Check if this is the continuation of an existing named area:
+      for (uint32_t i = 0, end = aNamedAreas.Length(); i < end; i++) {
+        if (aNamedAreas[i].mName == token.mName) {
+          currentArea = &aNamedAreas[i];
+          if (currentArea->mColumnStart != column || currentArea->mRowEnd != aRow) {
+            // Existing named area, but not forming a rectangle
+            return false;
+          }
+          // Next row of an existing named area
+          currentArea->mRowEnd++;
+          break;
+        }
+      }
+      if (!currentArea) {
+        // New named area
+        currentArea = aNamedAreas.AppendElement();
+        currentArea->mName = token.mName;
+        // For column or row N (starting at 1),
+        // the start line is N, the end line is N + 1
+        currentArea->mColumnStart = column;
+        currentArea->mColumnEnd = column + 1;
+        currentArea->mRowStart = aRow;
+        currentArea->mRowEnd = aRow + 1;
+      }
+    }
+  }
+  if (currentArea && currentArea->mColumnEnd != column) {
+    NS_ASSERTION(currentArea->mRowStart != aRow,
+                 "Inconsistent column end for the first row of a named area.");
+    // Not a rectangle
+    return false;
+  }
+  aColumns = column;
+  return true;
+}
+
+bool
+CSSParserImpl::ParseGridTemplateAreas()
+{
+  nsCSSValue value;
+  if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
+    AppendValue(eCSSProperty_grid_template_areas, value);
+    return true;
+  }
+
+  nsCSSValueGridTemplateAreas& result = value.SetGridTemplateAreas();
+  uint32_t row = 1;
+  uint32_t firstRowColumns;
+  do {
+    if (!GetToken(true)) {
+      return false;
+    }
+    if (eCSSToken_String != mToken.mType) {
+      UngetToken();  // In case it's opening a block or function.
+      return false;
+    }
+    uint32_t columns;
+    if (!ParseGridTemplateAreasLine(mToken.mIdent, result.mNamedAreas,
+                                    row, columns)) {
+      return false;
+    }
+    if (row == 1) {
+      firstRowColumns = columns;
+    } else if (columns != firstRowColumns) {
+      return false;
+    }
+    result.mTemplates.AppendElement(mToken.mIdent);
+    row++;
+  } while (!CheckEndProperty());
+
+  AppendValue(eCSSProperty_grid_template_areas, value);
+  return true;
+}
+
+
 // <color-stop> : <color> [ <percentage> | <length> ]?
 bool
 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
 {
   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
   if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
     return false;
   }
@@ -8008,16 +8123,18 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_filter:
     return ParseFilter();
   case eCSSProperty_flex:
     return ParseFlex();
   case eCSSProperty_flex_flow:
     return ParseFlexFlow();
   case eCSSProperty_font:
     return ParseFont();
+  case eCSSProperty_grid_template_areas:
+    return ParseGridTemplateAreas();
   case eCSSProperty_grid_template_columns:
   case eCSSProperty_grid_template_rows:
     return ParseGridTrackList(aPropID);
   case eCSSProperty_image_region:
     return ParseRect(eCSSProperty_image_region);
   case eCSSProperty_list_style:
     return ParseListStyle();
   case eCSSProperty_margin:
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2004,16 +2004,26 @@ CSS_PROP_UIRESET(
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE,
     "",
     VARIANT_HI,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None) // bug 58646
 CSS_PROP_POSITION(
+    grid-template-areas,
+    grid_template_areas,
+    GridTemplateAreas,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.grid.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_POSITION(
     grid-template-columns,
     grid_template_columns,
     GridTemplateColumns,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "layout.css.grid.enabled",
     0,
--- a/layout/style/nsCSSScanner.cpp
+++ b/layout/style/nsCSSScanner.cpp
@@ -1322,8 +1322,49 @@ nsCSSScanner::Next(nsCSSToken& aToken, b
     return true;
   }
 
   // Otherwise, a symbol (DELIM).
   aToken.mSymbol = ch;
   Advance();
   return true;
 }
+
+/* nsCSSGridTemplateAreaScanner methods. */
+
+nsCSSGridTemplateAreaScanner::nsCSSGridTemplateAreaScanner(const nsAString& aBuffer)
+  : mBuffer(aBuffer.BeginReading())
+  , mOffset(0)
+  , mCount(aBuffer.Length())
+{
+}
+
+bool
+nsCSSGridTemplateAreaScanner::Next(nsCSSGridTemplateAreaToken& aTokenResult)
+{
+  int32_t ch;
+  // Skip whitespace
+  do {
+    if (mOffset >= mCount) {
+      return false;
+    }
+    ch = mBuffer[mOffset];
+    mOffset++;
+  } while (IsWhitespace(ch));
+
+  if (IsOpenCharClass(ch, IS_IDCHAR)) {
+    // Named cell token
+    uint32_t start = mOffset - 1;  // offset of |ch|
+    while (mOffset < mCount && IsOpenCharClass(mBuffer[mOffset], IS_IDCHAR)) {
+      mOffset++;
+    }
+    aTokenResult.mName.Assign(&mBuffer[start], mOffset - start);
+    aTokenResult.isTrash = false;
+  } else if (ch == '.') {
+    // Null cell token
+    aTokenResult.mName.Truncate();
+    aTokenResult.isTrash = false;
+  } else {
+    // Trash token
+    aTokenResult.isTrash = true;
+  }
+  return true;
+}
--- a/layout/style/nsCSSScanner.h
+++ b/layout/style/nsCSSScanner.h
@@ -348,9 +348,31 @@ protected:
 
   // True if we are in SVG mode; false in "normal" CSS
   bool mSVGMode;
   bool mRecording;
   bool mSeenBadToken;
   bool mSeenVariableReference;
 };
 
+// Token for the grid-template-areas micro-syntax
+// http://dev.w3.org/csswg/css-grid/#propdef-grid-template-areas
+struct MOZ_STACK_CLASS nsCSSGridTemplateAreaToken {
+  nsAutoString mName;  // Empty for a null cell, non-empty for a named cell
+  bool isTrash;  // True for a trash token, mName is ignored in this case.
+};
+
+// Scanner for the grid-template-areas micro-syntax
+class nsCSSGridTemplateAreaScanner {
+public:
+  nsCSSGridTemplateAreaScanner(const nsAString& aBuffer);
+
+  // Get the next token.  Return false on EOF.
+  // aTokenResult is filled in with the data for the token.
+  bool Next(nsCSSGridTemplateAreaToken& aTokenResult);
+
+private:
+  const char16_t *mBuffer;
+  uint32_t mOffset;
+  uint32_t mCount;
+};
+
 #endif /* nsCSSScanner_h___ */
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -173,16 +173,20 @@ nsCSSValue::nsCSSValue(const nsCSSValue&
   }
   else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList = aCopy.mValue.mPairList;
     mValue.mPairList->AddRef();
   }
   else if (eCSSUnit_PairListDep == mUnit) {
     mValue.mPairListDependent = aCopy.mValue.mPairListDependent;
   }
+  else if (eCSSUnit_GridTemplateAreas == mUnit) {
+    mValue.mGridTemplateAreas = aCopy.mValue.mGridTemplateAreas;
+    mValue.mGridTemplateAreas->AddRef();
+  }
   else {
     NS_ABORT_IF_FALSE(false, "unknown unit");
   }
 }
 
 nsCSSValue& nsCSSValue::operator=(const nsCSSValue& aCopy)
 {
   if (this != &aCopy) {
@@ -245,16 +249,19 @@ bool nsCSSValue::operator==(const nsCSSV
       return *mValue.mList == *aOther.mValue.mList;
     }
     else if (eCSSUnit_SharedList == mUnit) {
       return *mValue.mSharedList == *aOther.mValue.mSharedList;
     }
     else if (eCSSUnit_PairList == mUnit) {
       return *mValue.mPairList == *aOther.mValue.mPairList;
     }
+    else if (eCSSUnit_GridTemplateAreas == mUnit) {
+      return *mValue.mGridTemplateAreas == *aOther.mValue.mGridTemplateAreas;
+    }
     else {
       return mValue.mFloat == aOther.mValue.mFloat;
     }
   }
   return false;
 }
 
 double nsCSSValue::GetAngleValueInRadians() const
@@ -331,16 +338,18 @@ void nsCSSValue::DoReset()
   } else if (eCSSUnit_Rect == mUnit) {
     mValue.mRect->Release();
   } else if (eCSSUnit_List == mUnit) {
     mValue.mList->Release();
   } else if (eCSSUnit_SharedList == mUnit) {
     mValue.mSharedList->Release();
   } else if (eCSSUnit_PairList == mUnit) {
     mValue.mPairList->Release();
+  } else if (eCSSUnit_GridTemplateAreas == mUnit) {
+    mValue.mGridTemplateAreas->Release();
   }
   mUnit = eCSSUnit_Null;
 }
 
 void nsCSSValue::SetIntValue(int32_t aValue, nsCSSUnit aUnit)
 {
   NS_ABORT_IF_FALSE(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated ||
                     aUnit == eCSSUnit_EnumColor, "not an int value");
@@ -582,16 +591,25 @@ void nsCSSValue::SetDependentPairListVal
 {
   Reset();
   if (aList) {
     mUnit = eCSSUnit_PairListDep;
     mValue.mPairListDependent = aList;
   }
 }
 
+nsCSSValueGridTemplateAreas& nsCSSValue::SetGridTemplateAreas()
+{
+  Reset();
+  mUnit = eCSSUnit_GridTemplateAreas;
+  mValue.mGridTemplateAreas = new nsCSSValueGridTemplateAreas;
+  mValue.mGridTemplateAreas->AddRef();
+  return *mValue.mGridTemplateAreas;
+}
+
 void nsCSSValue::SetAutoValue()
 {
   Reset();
   mUnit = eCSSUnit_Auto;
 }
 
 void nsCSSValue::SetInheritValue()
 {
@@ -1302,16 +1320,18 @@ nsCSSValue::AppendToString(nsCSSProperty
     switch (aProperty) {
       case eCSSProperty_font_feature_settings:
         nsStyleUtil::AppendFontFeatureSettings(*this, aResult);
         break;
       default:
         GetPairListValue()->AppendToString(aProperty, aResult, aSerialization);
         break;
     }
+  } else if (eCSSUnit_GridTemplateAreas == unit) {
+    GetGridTemplateAreas().AppendToString(aProperty, aResult, aSerialization);
   }
 
   switch (unit) {
     case eCSSUnit_Null:         break;
     case eCSSUnit_Auto:         aResult.AppendLiteral("auto");     break;
     case eCSSUnit_Inherit:      aResult.AppendLiteral("inherit");  break;
     case eCSSUnit_Initial:      aResult.AppendLiteral("initial");  break;
     case eCSSUnit_Unset:        aResult.AppendLiteral("unset");    break;
@@ -1363,16 +1383,17 @@ nsCSSValue::AppendToString(nsCSSProperty
     case eCSSUnit_Pair:         break;
     case eCSSUnit_Triplet:      break;
     case eCSSUnit_Rect:         break;
     case eCSSUnit_List:         break;
     case eCSSUnit_ListDep:      break;
     case eCSSUnit_SharedList:   break;
     case eCSSUnit_PairList:     break;
     case eCSSUnit_PairListDep:  break;
+    case eCSSUnit_GridTemplateAreas:     break;
 
     case eCSSUnit_Inch:         aResult.AppendLiteral("in");   break;
     case eCSSUnit_Millimeter:   aResult.AppendLiteral("mm");   break;
     case eCSSUnit_PhysicalMillimeter: aResult.AppendLiteral("mozmm");   break;
     case eCSSUnit_Centimeter:   aResult.AppendLiteral("cm");   break;
     case eCSSUnit_Point:        aResult.AppendLiteral("pt");   break;
     case eCSSUnit_Pica:         aResult.AppendLiteral("pc");   break;
 
@@ -1504,16 +1525,21 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
     case eCSSUnit_PairList:
       n += mValue.mPairList->SizeOfIncludingThis(aMallocSizeOf);
       break;
 
     // PairListDep: not measured because it's non-owning.
     case eCSSUnit_PairListDep:
       break;
 
+    // GridTemplateAreas
+    case eCSSUnit_GridTemplateAreas:
+      n += mValue.mGridTemplateAreas->SizeOfIncludingThis(aMallocSizeOf);
+      break;
+
     // Int: nothing extra to measure.
     case eCSSUnit_Integer:
     case eCSSUnit_Enumerated:
     case eCSSUnit_EnumColor:
       break;
 
     // Integer Color: nothing extra to measure.
     case eCSSUnit_RGBColor:
@@ -2330,8 +2356,33 @@ static_assert(NS_CORNER_TOP_LEFT == 0 &&
 
 /* static */ const nsCSSCornerSizes::corner_type
 nsCSSCornerSizes::corners[4] = {
   &nsCSSCornerSizes::mTopLeft,
   &nsCSSCornerSizes::mTopRight,
   &nsCSSCornerSizes::mBottomRight,
   &nsCSSCornerSizes::mBottomLeft,
 };
+
+void
+nsCSSValueGridTemplateAreas::AppendToString(nsCSSProperty aProperty,
+                                       nsAString& aResult,
+                                       nsCSSValue::Serialization aValueSerialization) const
+{
+  uint32_t length = mTemplates.Length();
+  if (length == 0) {
+    aResult.AppendLiteral("none");
+  } else {
+    nsStyleUtil::AppendEscapedCSSString(mTemplates[0], aResult);
+    for (uint32_t i = 1; i < length; i++) {
+      aResult.Append(char16_t(' '));
+      nsStyleUtil::AppendEscapedCSSString(mTemplates[i], aResult);
+    }
+  }
+}
+
+size_t
+nsCSSValueGridTemplateAreas::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = mNamedAreas.SizeOfExcludingThis(aMallocSizeOf);
+  n += mTemplates.SizeOfIncludingThis(aMallocSizeOf);
+  return n;
+}
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -201,16 +201,18 @@ enum nsCSSUnit {
   eCSSUnit_ListDep      = 54,     // (nsCSSValueList*) same as List
                                   //   but does not own the list
   eCSSUnit_SharedList   = 55,     // (nsCSSValueSharedList*) same as list
                                   //   but reference counted and shared
   eCSSUnit_PairList     = 56,     // (nsCSSValuePairList*) list of value pairs
   eCSSUnit_PairListDep  = 57,     // (nsCSSValuePairList*) same as PairList
                                   //   but does not own the list
 
+  eCSSUnit_GridTemplateAreas   = 60,   // (nsCSSValueGridTemplateAreas*) for grid-template-areas
+
   eCSSUnit_Integer      = 70,     // (int) simple value
   eCSSUnit_Enumerated   = 71,     // (int) value has enumerated meaning
 
   eCSSUnit_EnumColor           = 80,   // (int) enumerated color (kColorKTable)
   eCSSUnit_RGBColor            = 81,   // (nscolor) an opaque RGBA value specified as rgb()
   eCSSUnit_RGBAColor           = 82,   // (nscolor) an RGBA value specified as rgba()
   eCSSUnit_HexColor            = 83,   // (nscolor) an opaque RGBA value specified as #rrggbb
   eCSSUnit_ShortHexColor       = 84,   // (nscolor) an opaque RGBA value specified as #rgb
@@ -272,16 +274,17 @@ struct nsCSSRect;
 struct nsCSSRect_heap;
 struct nsCSSValueList;
 struct nsCSSValueList_heap;
 struct nsCSSValueSharedList;
 struct nsCSSValuePairList;
 struct nsCSSValuePairList_heap;
 struct nsCSSValueTriplet;
 struct nsCSSValueTriplet_heap;
+struct nsCSSValueGridTemplateAreas;
 class nsCSSValueFloatColor;
 
 class nsCSSValue {
 public:
   struct Array;
   friend struct Array;
 
   friend struct mozilla::css::URLValue;
@@ -492,16 +495,19 @@ public:
   inline const nsCSSValueList* GetListValue() const;
 
   inline nsCSSValuePairList* GetPairListValue();
   inline const nsCSSValuePairList* GetPairListValue() const;
 
   inline nsCSSValueTriplet& GetTripletValue();
   inline const nsCSSValueTriplet& GetTripletValue() const;
 
+  inline nsCSSValueGridTemplateAreas& GetGridTemplateAreas();
+  inline const nsCSSValueGridTemplateAreas& GetGridTemplateAreas() const;
+
   mozilla::css::URLValue* GetURLStructValue() const
   {
     // Not allowing this for Image values, because if the caller takes
     // a ref to them they won't be able to delete them properly.
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL, "not a URL value");
     return mValue.mURL;
   }
 
@@ -570,16 +576,17 @@ public:
   void SetDummyValue();
   void SetDummyInheritValue();
 
   // These are a little different - they allocate storage for you and
   // return a handle.
   nsCSSRect& SetRectValue();
   nsCSSValueList* SetListValue();
   nsCSSValuePairList* SetPairListValue();
+  nsCSSValueGridTemplateAreas& SetGridTemplateAreas();
 
   void StartImageLoad(nsIDocument* aDocument) const;  // Only pretend const
 
   // Initializes as a function value with the specified function id.
   Array* InitFunction(nsCSSKeyword aFunctionId, uint32_t aNumArgs);
   // Checks if this is a function value with the specified function id.
   bool EqualsFunction(nsCSSKeyword aFunctionId) const;
 
@@ -612,16 +619,17 @@ protected:
     nsCSSValuePair_heap* mPair;
     nsCSSRect_heap* mRect;
     nsCSSValueTriplet_heap* mTriplet;
     nsCSSValueList_heap* mList;
     nsCSSValueList* mListDependent;
     nsCSSValueSharedList* mSharedList;
     nsCSSValuePairList_heap* mPairList;
     nsCSSValuePairList* mPairListDependent;
+    nsCSSValueGridTemplateAreas* mGridTemplateAreas;
     nsCSSValueFloatColor* mFloatColor;
   } mValue;
 };
 
 struct nsCSSValue::Array {
 
   // return |Array| with reference count of zero
   static Array* Create(size_t aItemCount) {
@@ -1147,16 +1155,32 @@ nsCSSValue::GetPairListValue() const
   if (mUnit == eCSSUnit_PairList)
     return mValue.mPairList;
   else {
     NS_ABORT_IF_FALSE (mUnit == eCSSUnit_PairListDep, "not a pairlist value");
     return mValue.mPairListDependent;
   }
 }
 
+inline nsCSSValueGridTemplateAreas&
+nsCSSValue::GetGridTemplateAreas()
+{
+  NS_ABORT_IF_FALSE (mUnit == eCSSUnit_GridTemplateAreas,
+                     "not a grid-template-areas value");
+  return *mValue.mGridTemplateAreas;
+}
+
+inline const nsCSSValueGridTemplateAreas&
+nsCSSValue::GetGridTemplateAreas() const
+{
+  NS_ABORT_IF_FALSE (mUnit == eCSSUnit_GridTemplateAreas,
+                     "not a grid-template-areas value");
+  return *mValue.mGridTemplateAreas;
+}
+
 struct nsCSSValueGradientStop {
 public:
   nsCSSValueGradientStop();
   // needed to keep bloat logs happy when we use the TArray
   // in nsCSSValueGradient
   nsCSSValueGradientStop(const nsCSSValueGradientStop& aOther);
   ~nsCSSValueGradientStop();
 
@@ -1420,10 +1444,50 @@ struct nsCSSCornerSizes {
   nsCSSValue mBottomRight;
   nsCSSValue mBottomLeft;
 
 protected:
   typedef nsCSSValue nsCSSCornerSizes::*corner_type;
   static const corner_type corners[4];
 };
 
+struct nsCSSGridNamedArea {
+  nsString mName;
+  uint32_t mColumnStart;
+  uint32_t mColumnEnd;
+  uint32_t mRowStart;
+  uint32_t mRowEnd;
+};
+
+struct nsCSSValueGridTemplateAreas {
+  nsTArray<nsCSSGridNamedArea> mNamedAreas; // Parsed value
+  nsTArray<nsString> mTemplates;  // Original <string> values, for serialization
+
+  nsCSSValueGridTemplateAreas()
+  {
+  }
+
+  nsCSSValueGridTemplateAreas(const nsCSSValueGridTemplateAreas& aOther)
+    : mNamedAreas(aOther.mNamedAreas)
+    , mTemplates(aOther.mTemplates)
+  {
+  }
+
+  void AppendToString(nsCSSProperty aProperty, nsAString& aResult,
+                      nsCSSValue::Serialization aValueSerialization) const;
+
+  bool operator==(const nsCSSValueGridTemplateAreas& aOther) const
+  {
+    return mTemplates == aOther.mTemplates;
+  }
+
+  bool operator!=(const nsCSSValueGridTemplateAreas& aOther) const
+  {
+    return !(*this == aOther);
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(nsCSSValueGridTemplateAreas)
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+};
+
 #endif /* nsCSSValue_h___ */
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2266,16 +2266,38 @@ nsComputedDOMStyle::DoGetBackgroundSize(
         break;
       }
     }
   }
 
   return valueList;
 }
 
+CSSValue*
+nsComputedDOMStyle::DoGetGridTemplateAreas()
+{
+  const nsTArray<nsString>& templates =
+    StylePosition()->mGridTemplateAreas.mTemplates;
+  if (templates.IsEmpty()) {
+    nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
+    val->SetIdent(eCSSKeyword_none);
+    return val;
+  }
+
+  nsDOMCSSValueList *valueList = GetROCSSValueList(false);
+  for (uint32_t i = 0; i < templates.Length(); i++) {
+    nsAutoString str;
+    nsStyleUtil::AppendEscapedCSSString(templates[i], str);
+    nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
+    val->SetString(str);
+    valueList->AppendCSSValue(val);
+  }
+  return valueList;
+}
+
 // aLineNames must not be empty
 CSSValue*
 nsComputedDOMStyle::GetGridLineNames(const nsTArray<nsString>& aLineNames)
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
   nsAutoString lineNamesString;
   uint32_t i_end = aLineNames.Length();
   MOZ_ASSERT(i_end > 0, "GetGridLineNames called with an empty array");
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -258,16 +258,17 @@ private:
   mozilla::dom::CSSValue* DoGetFontVariantCaps();
   mozilla::dom::CSSValue* DoGetFontVariantEastAsian();
   mozilla::dom::CSSValue* DoGetFontVariantLigatures();
   mozilla::dom::CSSValue* DoGetFontVariantNumeric();
   mozilla::dom::CSSValue* DoGetFontVariantPosition();
   mozilla::dom::CSSValue* DoGetFontWeight();
 
   /* Grid properties */
+  mozilla::dom::CSSValue* DoGetGridTemplateAreas();
   mozilla::dom::CSSValue* DoGetGridTemplateColumns();
   mozilla::dom::CSSValue* DoGetGridTemplateRows();
 
   /* Background properties */
   mozilla::dom::CSSValue* DoGetBackgroundAttachment();
   mozilla::dom::CSSValue* DoGetBackgroundColor();
   mozilla::dom::CSSValue* DoGetBackgroundImage();
   mozilla::dom::CSSValue* DoGetBackgroundPosition();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -125,16 +125,17 @@ COMPUTED_STYLE_PROP(font_synthesis,     
 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_weight,                   FontWeight)
+COMPUTED_STYLE_PROP(grid_template_areas,           GridTemplateAreas)
 COMPUTED_STYLE_PROP(grid_template_columns,         GridTemplateColumns)
 COMPUTED_STYLE_PROP(grid_template_rows,            GridTemplateRows)
 COMPUTED_STYLE_PROP(height,                        Height)
 COMPUTED_STYLE_PROP(image_orientation,             ImageOrientation)
 COMPUTED_STYLE_PROP(ime_mode,                      IMEMode)
 COMPUTED_STYLE_PROP(justify_content,               JustifyContent)
 COMPUTED_STYLE_PROP(left,                          Left)
 COMPUTED_STYLE_PROP(letter_spacing,                LetterSpacing)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7172,16 +7172,46 @@ SetGridTrackList(const nsCSSValue& aValu
                aResult.mMinTrackSizingFunctions.Length() ==
                aResult.mMaxTrackSizingFunctions.Length() &&
                aResult.mMinTrackSizingFunctions.Length() + 1 ==
                aResult.mLineNameLists.Length(),
                "Inconstistent array lengths for nsStyleGridTrackList");
   }
 }
 
+static void
+SetGridTemplateAreas(const nsCSSValue& aValue,
+                     nsCSSValueGridTemplateAreas& aResult,
+                     const nsCSSValueGridTemplateAreas& aParentValue,
+                     bool& aCanStoreInRuleTree)
+{
+  switch (aValue.GetUnit()) {
+  case eCSSUnit_Null:
+    break;
+
+  case eCSSUnit_Inherit:
+    aCanStoreInRuleTree = false;
+    aResult.mNamedAreas = aParentValue.mNamedAreas;
+    aResult.mTemplates = aParentValue.mTemplates;
+    break;
+
+  case eCSSUnit_Initial:
+  case eCSSUnit_Unset:
+  case eCSSUnit_None:
+    aResult.mNamedAreas.Clear();
+    aResult.mTemplates.Clear();
+    break;
+
+  default:
+    const nsCSSValueGridTemplateAreas& value = aValue.GetGridTemplateAreas();
+    aResult.mNamedAreas = value.mNamedAreas;
+    aResult.mTemplates = value.mTemplates;
+  }
+}
+
 const void*
 nsRuleNode::ComputePositionData(void* aStartStruct,
                                 const nsRuleData* aRuleData,
                                 nsStyleContext* aContext,
                                 nsRuleNode* aHighestNode,
                                 const RuleDetail aRuleDetail,
                                 const bool aCanStoreInRuleTree)
 {
@@ -7355,16 +7385,21 @@ nsRuleNode::ComputePositionData(void* aS
                    pos->mGridTemplateColumns, parentPos->mGridTemplateColumns,
                    aContext, mPresContext, canStoreInRuleTree);
 
   // grid-template-rows
   SetGridTrackList(*aRuleData->ValueForGridTemplateRows(),
                    pos->mGridTemplateRows, parentPos->mGridTemplateRows,
                    aContext, mPresContext, canStoreInRuleTree);
 
+  // grid-tempate-areas
+  SetGridTemplateAreas(*aRuleData->ValueForGridTemplateAreas(),
+                       pos->mGridTemplateAreas, parentPos->mGridTemplateAreas,
+                       canStoreInRuleTree);
+
   // z-index
   const nsCSSValue* zIndexValue = aRuleData->ValueForZIndex();
   if (! SetCoord(*zIndexValue, pos->mZIndex, parentPos->mZIndex,
                  SETCOORD_IA | SETCOORD_INITIAL_AUTO | SETCOORD_UNSET_INITIAL,
                  aContext, nullptr, canStoreInRuleTree)) {
     if (eCSSUnit_Inherit == zIndexValue->GetUnit()) {
       // handle inherit, because it's ok to inherit 'auto' here
       canStoreInRuleTree = false;
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1234,40 +1234,44 @@ nsStylePosition::nsStylePosition(void)
   mAlignSelf = NS_STYLE_ALIGN_SELF_AUTO;
   mFlexDirection = NS_STYLE_FLEX_DIRECTION_ROW;
   mFlexWrap = NS_STYLE_FLEX_WRAP_NOWRAP;
   mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
   mOrder = NS_STYLE_ORDER_INITIAL;
   mFlexGrow = 0.0f;
   mFlexShrink = 1.0f;
   mZIndex.SetAutoValue();
-  // mGridTemplateRows and mGridTemplateColumns get their default constructors
-  // which initializes them to empty arrays,
-  // which represent the property’s initial value 'none'
+  // mGridTemplateRows, mGridTemplateColumns, and mGridTemplateAreas
+  // get their default constructors
+  // which initialize them to empty arrays,
+  // which represent the properties' initial value 'none'.
 }
 
 nsStylePosition::~nsStylePosition(void)
 {
   MOZ_COUNT_DTOR(nsStylePosition);
 }
 
 nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
   : mGridTemplateColumns(aSource.mGridTemplateColumns)
   , mGridTemplateRows(aSource.mGridTemplateRows)
+  , mGridTemplateAreas(aSource.mGridTemplateAreas)
 {
   MOZ_COUNT_CTOR(nsStylePosition);
   // If you add any memcpy'able member vars,
   // they should be declared before mGridTemplateColumns.
   // If you add any non-memcpy'able member vars,
   // they should be declared after mGridTemplateColumns,
   // and you should invoke their copy constructor in the init list above
   // and update this static-assert to include their "sizeof()"
   static_assert(sizeof(nsStylePosition) ==
                 offsetof(nsStylePosition, mGridTemplateColumns) +
-                sizeof(mGridTemplateColumns) + sizeof(mGridTemplateRows),
+                sizeof(mGridTemplateColumns) +
+                sizeof(mGridTemplateRows) +
+                sizeof(mGridTemplateAreas),
                 "Unexpected size or offset in nsStylePosition");
   memcpy((nsStylePosition*) this,
          &aSource,
          offsetof(nsStylePosition, mGridTemplateColumns));
 }
 
 static bool
 IsAutonessEqual(const nsStyleSides& aSides1, const nsStyleSides& aSides2)
@@ -1313,17 +1317,18 @@ nsChangeHint nsStylePosition::CalcDiffer
       mFlexDirection != aOther.mFlexDirection ||
       mFlexWrap != aOther.mFlexWrap) {
     return NS_CombineHint(hint, nsChangeHint_AllReflowHints);
   }
 
 
   // Properties that apply to grid containers:
   if (mGridTemplateColumns != aOther.mGridTemplateColumns ||
-      mGridTemplateRows != aOther.mGridTemplateRows) {
+      mGridTemplateRows != aOther.mGridTemplateRows ||
+      mGridTemplateAreas != aOther.mGridTemplateAreas) {
     return NS_CombineHint(hint, nsChangeHint_AllReflowHints);
   }
 
 
   // Changing justify-content on a flexbox might affect the positioning of its
   // children, but it won't affect any sizing.
   if (mJustifyContent != aOther.mJustifyContent) {
     NS_UpdateHint(hint, nsChangeHint_NeedReflow);
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1229,16 +1229,17 @@ struct nsStylePosition {
   float         mFlexShrink;            // [reset] float
   nsStyleCoord  mZIndex;                // [reset] integer, auto
   // NOTE: Fields so far can be memcpy()'ed, while following fields
   // need to have their copy constructor called when we're being copied.
   // See nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
   // in nsStyleStruct.cpp
   nsStyleGridTrackList mGridTemplateColumns;
   nsStyleGridTrackList mGridTemplateRows;
+  nsCSSValueGridTemplateAreas mGridTemplateAreas;
 
   bool WidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mWidth); }
   bool MinWidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mMinWidth); }
   bool MaxWidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mMaxWidth); }
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4845,16 +4845,37 @@ if (SpecialPowers.getBoolPref("layout.cs
 	gCSSProperties["grid-template-rows"] = {
 		domProp: "gridTemplateRows",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: gCSSProperties["grid-template-columns"].initial_values,
 		other_values: gCSSProperties["grid-template-columns"].other_values,
 		invalid_values: gCSSProperties["grid-template-columns"].invalid_values
 	};
+	gCSSProperties["grid-template-areas"] = {
+		domProp: "gridTemplateAreas",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "none" ],
+		other_values: [
+			"''",
+			"'' ''",
+			"'1a-é_ .' \"b .\"",
+			"' Z\t\\aZ' 'Z Z'",
+			" '. . a b'  '..a b' ",
+		],
+		invalid_values: [
+			"'a b' 'a/b'",
+			"'a . a'",
+			"'. a a' 'a a a'",
+			"'a a .' 'a a a'",
+			"'a a' 'a .'",
+			"'a a'\n'..'\n'a a'",
+		]
+	};
 }
 
 if (SpecialPowers.getBoolPref("layout.css.image-orientation.enabled")) {
 	gCSSProperties["image-orientation"] = {
 		domProp: "imageOrientation",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [