Bug 976787 part 5: Add grid-{column,row}-{start,end} and grid-auto-position to the style system. r=dholbert
authorSimon Sapin <simon.sapin@exyr.org>
Mon, 10 Mar 2014 15:54:17 -0700
changeset 183889 7aa032d90c9120737fbd0963f042e27e31616568
parent 183888 8db3c07e2bad36a11cf1ec622fd0c155fa305126
child 183890 753b8af2f2c05e24ea32a609b57404b9373fc689
push idunknown
push userunknown
push dateunknown
reviewersdholbert
bugs976787
milestone30.0a1
Bug 976787 part 5: Add grid-{column,row}-{start,end} and grid-auto-position to the style system. r=dholbert
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSValue.cpp
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
layout/style/test/test_garbage_at_end_of_declarations.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -500,16 +500,17 @@ CSS_KEY(small, small)
 CSS_KEY(small-caps, small_caps)
 CSS_KEY(small-caption, small_caption)
 CSS_KEY(smaller, smaller)
 CSS_KEY(soft, soft)
 CSS_KEY(soft-light, soft_light)
 CSS_KEY(solid, solid)
 CSS_KEY(space-around, space_around)
 CSS_KEY(space-between, space_between)
+CSS_KEY(span, span)
 CSS_KEY(square, square)
 CSS_KEY(stacked-fractions, stacked_fractions)
 CSS_KEY(start, start)
 CSS_KEY(static, static)
 CSS_KEY(status-bar, status_bar)
 CSS_KEY(step-end, step_end)
 CSS_KEY(step-start, step_start)
 CSS_KEY(sticky, sticky)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -638,16 +638,19 @@ protected:
   bool ParseGridTrackSize(nsCSSValue& aValue);
   bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
   bool ParseGridTrackList(nsCSSProperty aPropID);
   bool ParseGridTemplateAreasLine(const nsAutoString& aInput,
                                   nsTArray<nsCSSGridNamedArea>& aNamedAreas,
                                   uint32_t aRow,
                                   uint32_t& aColumns);
   bool ParseGridTemplateAreas();
+  bool ParseGridLine(nsCSSValue& aValue);
+  bool ParseGridAutoPosition();
+  bool ParseGridColumnRowStartEnd(nsCSSProperty aPropID);
 
   // for 'clip' and '-moz-image-region'
   bool ParseRect(nsCSSProperty aPropID);
   bool ParseColumns();
   bool ParseContent();
   bool ParseCounterData(nsCSSProperty aPropID);
   bool ParseCursor();
   bool ParseFont();
@@ -7179,16 +7182,152 @@ CSSParserImpl::ParseGridTemplateAreas()
     result.mTemplates.AppendElement(mToken.mIdent);
     row++;
   } while (!CheckEndProperty());
 
   AppendValue(eCSSProperty_grid_template_areas, value);
   return true;
 }
 
+// Parse a <grid-line>.
+// If successful, set aValue to eCSSUnit_Auto,
+// or a eCSSUnit_List containing, in that order:
+//
+// * An optional eCSSUnit_Enumerated marking a "span" keyword.
+// * An optional eCSSUnit_Integer
+// * An optional eCSSUnit_Ident
+//
+// At least one of eCSSUnit_Integer or eCSSUnit_Ident is present.
+bool
+CSSParserImpl::ParseGridLine(nsCSSValue& aValue)
+{
+  //  <grid-line> =
+  //    auto |
+  //    <custom-ident> |
+  //    [ <integer> && <custom-ident>? ] |
+  //    [ span && [ <integer> || <custom-ident> ] ]
+  //
+  // Syntactically, this simplifies to:
+  //
+  //  <grid-line> =
+  //    auto |
+  //    [ span? && [ <integer> || <custom-ident> ] ]
+
+  if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
+    return true;
+  }
+
+  static const nsCSSKeyword kGridLineKeywords[] = {
+    eCSSKeyword_span,
+    eCSSKeyword_UNKNOWN  // End-of-array marker
+  };
+  bool hasSpan = false;
+  bool hasInteger = false;
+  bool hasIdent = false;
+  int32_t integer;
+  nsCSSValue ident;
+
+  if (!GetToken(true)) {
+    return false;
+  }
+  if (mToken.mType == eCSSToken_Ident &&
+      mToken.mIdent.LowerCaseEqualsLiteral("span")) {
+    hasSpan = true;
+    if (!GetToken(true)) {
+      return false;
+    }
+  }
+
+  do {
+    if (!hasIdent &&
+        mToken.mType == eCSSToken_Ident &&
+        ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) {
+      hasIdent = true;
+    } else if (!hasInteger &&
+               mToken.mType == eCSSToken_Number &&
+               mToken.mIntegerValid &&
+               mToken.mInteger != 0) {
+      hasInteger = true;
+      integer = mToken.mInteger;
+    } else {
+      UngetToken();
+      break;
+    }
+  } while (!(hasInteger && hasIdent) && GetToken(true));
+
+  // Require at least one of <integer> or <custom-ident>
+  if (!(hasInteger || hasIdent)) {
+    return false;
+  }
+
+  if (!hasSpan && GetToken(true)) {
+    if (mToken.mType == eCSSToken_Ident &&
+        mToken.mIdent.LowerCaseEqualsLiteral("span")) {
+      hasSpan = true;
+    } else {
+      UngetToken();
+    }
+  }
+
+  nsCSSValueList* item = aValue.SetListValue();
+  if (hasSpan) {
+    // Given "span", a negative <integer> is invalid.
+    if (hasInteger && integer < 0) {
+      return false;
+    }
+    // '1' here is a dummy value.
+    // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword.
+    item->mValue.SetIntValue(1, eCSSUnit_Enumerated);
+    item->mNext = new nsCSSValueList;
+    item = item->mNext;
+  }
+  if (hasInteger) {
+    item->mValue.SetIntValue(integer, eCSSUnit_Integer);
+    if (hasIdent) {
+      item->mNext = new nsCSSValueList;
+      item = item->mNext;
+    }
+  }
+  if (hasIdent) {
+    item->mValue = ident;
+  }
+  return true;
+}
+
+bool
+CSSParserImpl::ParseGridAutoPosition()
+{
+  nsCSSValue value;
+  if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
+    AppendValue(eCSSProperty_grid_auto_position, value);
+    return true;
+  }
+  nsCSSValue columnStartValue;
+  nsCSSValue rowStartValue;
+  if (ParseGridLine(columnStartValue) &&
+      ExpectSymbol('/', true) &&
+      ParseGridLine(rowStartValue)) {
+    value.SetPairValue(columnStartValue, rowStartValue);
+    AppendValue(eCSSProperty_grid_auto_position, value);
+    return true;
+  }
+  return false;
+}
+
+bool
+CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID)
+{
+  nsCSSValue value;
+  if (ParseVariant(value, VARIANT_INHERIT, nullptr) ||
+      ParseGridLine(value)) {
+    AppendValue(aPropID, value);
+    return true;
+  }
+  return false;
+}
 
 // <color-stop> : <color> [ <percentage> | <length> ]?
 bool
 CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient)
 {
   nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement();
   if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) {
     return false;
@@ -8204,16 +8343,23 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_grid_auto_columns:
   case eCSSProperty_grid_auto_rows:
     return ParseGridAutoColumnsRows(aPropID);
   case eCSSProperty_grid_template_areas:
     return ParseGridTemplateAreas();
   case eCSSProperty_grid_template_columns:
   case eCSSProperty_grid_template_rows:
     return ParseGridTrackList(aPropID);
+  case eCSSProperty_grid_auto_position:
+    return ParseGridAutoPosition();
+  case eCSSProperty_grid_column_start:
+  case eCSSProperty_grid_column_end:
+  case eCSSProperty_grid_row_start:
+  case eCSSProperty_grid_row_end:
+    return ParseGridColumnRowStartEnd(aPropID);
   case eCSSProperty_image_region:
     return ParseRect(eCSSProperty_image_region);
   case eCSSProperty_list_style:
     return ParseListStyle();
   case eCSSProperty_margin:
     return ParseMargin();
   case eCSSProperty_margin_end:
     return ParseDirectionalBoxProperty(eCSSProperty_margin_end,
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2036,16 +2036,26 @@ CSS_PROP_POSITION(
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_STORES_CALC,
     "layout.css.grid.enabled",
     0,
     kGridTrackBreadthKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_POSITION(
+    grid-auto-position,
+    grid_auto_position,
+    GridAutoPosition,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.grid.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_POSITION(
     grid-template-areas,
     grid_template_areas,
     GridTemplateAreas,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.grid.enabled",
     0,
     nullptr,
     CSS_PROP_NO_OFFSET,
@@ -2070,16 +2080,56 @@ CSS_PROP_POSITION(
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "layout.css.grid.enabled",
     0,
     kGridTrackBreadthKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_POSITION(
+    grid-column-start,
+    grid_column_start,
+    GridColumnStart,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.grid.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_POSITION(
+    grid-column-end,
+    grid_column_end,
+    GridColumnEnd,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.grid.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_POSITION(
+    grid-row-start,
+    grid_row_start,
+    GridRowStart,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.grid.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_POSITION(
+    grid-row-end,
+    grid_row_end,
+    GridRowEnd,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.grid.enabled",
+    0,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_POSITION(
     height,
     height,
     Height,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC |
         CSS_PROPERTY_UNITLESS_LENGTH_QUIRK |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1027,16 +1027,25 @@ nsCSSValue::AppendToString(nsCSSProperty
 
     case eCSSProperty_grid_auto_flow:
       nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
                                          NS_STYLE_GRID_AUTO_FLOW_NONE,
                                          NS_STYLE_GRID_AUTO_FLOW_DENSE,
                                          aResult);
       break;
 
+    case eCSSProperty_grid_auto_position:
+    case eCSSProperty_grid_column_start:
+    case eCSSProperty_grid_column_end:
+    case eCSSProperty_grid_row_start:
+    case eCSSProperty_grid_row_end:
+      // "span" is the only enumerated-unit value for these properties
+      aResult.AppendLiteral("span");
+      break;
+
     case eCSSProperty_touch_action:
       nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
                                          NS_STYLE_TOUCH_ACTION_PAN_X,
                                          NS_STYLE_TOUCH_ACTION_PAN_Y,
                                          aResult);
       break;
 
     default:
@@ -1307,16 +1316,20 @@ nsCSSValue::AppendToString(nsCSSProperty
 
       // functional values
       const nsCSSValueList *list = GetPairValue().mYValue.GetListValue();
       nsAutoTArray<gfxAlternateValue,8> altValues;
 
       nsStyleUtil::ComputeFunctionalAlternates(list, altValues);
       nsStyleUtil::SerializeFunctionalAlternates(altValues, out);
       aResult.Append(out);
+    } else if (eCSSProperty_grid_auto_position == aProperty) {
+      GetPairValue().mXValue.AppendToString(aProperty, aResult, aSerialization);
+      aResult.AppendLiteral(" / ");
+      GetPairValue().mYValue.AppendToString(aProperty, aResult, aSerialization);
     } else {
       GetPairValue().AppendToString(aProperty, aResult, aSerialization);
     }
   } else if (eCSSUnit_Triplet == unit) {
     GetTripletValue().AppendToString(aProperty, aResult, aSerialization);
   } else if (eCSSUnit_Rect == unit) {
     GetRectValue().AppendToString(aProperty, aResult, aSerialization);
   } else if (eCSSUnit_List == unit || eCSSUnit_ListDep == unit) {
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2419,16 +2419,94 @@ nsComputedDOMStyle::DoGetGridTemplateCol
 
 CSSValue*
 nsComputedDOMStyle::DoGetGridTemplateRows()
 {
   return GetGridTrackList(StylePosition()->mGridTemplateRows);
 }
 
 CSSValue*
+nsComputedDOMStyle::GetGridLine(const nsStyleGridLine& aGridLine)
+{
+  if (aGridLine.IsAuto()) {
+    nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+    val->SetIdent(eCSSKeyword_auto);
+    return val;
+  }
+
+  nsDOMCSSValueList* valueList = GetROCSSValueList(false);
+
+  if (aGridLine.mHasSpan) {
+    nsROCSSPrimitiveValue* span = new nsROCSSPrimitiveValue;
+    span->SetIdent(eCSSKeyword_span);
+    valueList->AppendCSSValue(span);
+  }
+
+  if (aGridLine.mInteger != 0) {
+    nsROCSSPrimitiveValue* integer = new nsROCSSPrimitiveValue;
+    integer->SetNumber(aGridLine.mInteger);
+    valueList->AppendCSSValue(integer);
+  }
+
+  if (!aGridLine.mLineName.IsEmpty()) {
+    nsROCSSPrimitiveValue* lineName = new nsROCSSPrimitiveValue;
+    nsString escapedLineName;
+    nsStyleUtil::AppendEscapedCSSIdent(aGridLine.mLineName, escapedLineName);
+    lineName->SetString(escapedLineName);
+    valueList->AppendCSSValue(lineName);
+  }
+
+  NS_ASSERTION(valueList->Length() > 0,
+               "Should have appended at least one value");
+  return valueList;
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetGridAutoPosition()
+{
+  nsDOMCSSValueList* valueList = GetROCSSValueList(false);
+
+  valueList->AppendCSSValue(
+    GetGridLine(StylePosition()->mGridAutoPositionColumn));
+
+  nsROCSSPrimitiveValue* slash = new nsROCSSPrimitiveValue;
+  slash->SetString(NS_LITERAL_STRING("/"));
+  valueList->AppendCSSValue(slash);
+
+  valueList->AppendCSSValue(
+    GetGridLine(StylePosition()->mGridAutoPositionRow));
+
+  return valueList;
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetGridColumnStart()
+{
+  return GetGridLine(StylePosition()->mGridColumnStart);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetGridColumnEnd()
+{
+  return GetGridLine(StylePosition()->mGridColumnEnd);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetGridRowStart()
+{
+  return GetGridLine(StylePosition()->mGridRowStart);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetGridRowEnd()
+{
+  return GetGridLine(StylePosition()->mGridRowEnd);
+}
+
+CSSValue*
 nsComputedDOMStyle::DoGetPaddingTop()
 {
   return GetPaddingWidthFor(NS_SIDE_TOP);
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetPaddingBottom()
 {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -188,16 +188,17 @@ private:
   mozilla::dom::CSSValue* GetMarginWidthFor(mozilla::css::Side aSide);
 
   mozilla::dom::CSSValue* GetSVGPaintFor(bool aFill);
 
   mozilla::dom::CSSValue* GetGridLineNames(const nsTArray<nsString>& aLineNames);
   mozilla::dom::CSSValue* GetGridTrackSize(const nsStyleCoord& aMinSize,
                                            const nsStyleCoord& aMaxSize);
   mozilla::dom::CSSValue* GetGridTrackList(const nsStyleGridTrackList& aTrackList);
+  mozilla::dom::CSSValue* GetGridLine(const nsStyleGridLine& aGridLine);
 
   bool GetLineHeightCoord(nscoord& aCoord);
 
   mozilla::dom::CSSValue* GetCSSShadowArray(nsCSSShadowArray* aArray,
                                             const nscolor& aDefaultColor,
                                             bool aIsBoxShadow);
 
   mozilla::dom::CSSValue* GetBackgroundList(uint8_t nsStyleBackground::Layer::* aMember,
@@ -261,19 +262,24 @@ private:
   mozilla::dom::CSSValue* DoGetFontVariantNumeric();
   mozilla::dom::CSSValue* DoGetFontVariantPosition();
   mozilla::dom::CSSValue* DoGetFontWeight();
 
   /* Grid properties */
   mozilla::dom::CSSValue* DoGetGridAutoFlow();
   mozilla::dom::CSSValue* DoGetGridAutoColumns();
   mozilla::dom::CSSValue* DoGetGridAutoRows();
+  mozilla::dom::CSSValue* DoGetGridAutoPosition();
   mozilla::dom::CSSValue* DoGetGridTemplateAreas();
   mozilla::dom::CSSValue* DoGetGridTemplateColumns();
   mozilla::dom::CSSValue* DoGetGridTemplateRows();
+  mozilla::dom::CSSValue* DoGetGridColumnStart();
+  mozilla::dom::CSSValue* DoGetGridColumnEnd();
+  mozilla::dom::CSSValue* DoGetGridRowStart();
+  mozilla::dom::CSSValue* DoGetGridRowEnd();
 
   /* Background properties */
   mozilla::dom::CSSValue* DoGetBackgroundAttachment();
   mozilla::dom::CSSValue* DoGetBackgroundColor();
   mozilla::dom::CSSValue* DoGetBackgroundImage();
   mozilla::dom::CSSValue* DoGetBackgroundPosition();
   mozilla::dom::CSSValue* DoGetBackgroundRepeat();
   mozilla::dom::CSSValue* DoGetBackgroundClip();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -127,17 +127,22 @@ COMPUTED_STYLE_PROP(font_variant_alterna
 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_auto_columns,             GridAutoColumns)
 COMPUTED_STYLE_PROP(grid_auto_flow,                GridAutoFlow)
+COMPUTED_STYLE_PROP(grid_auto_position,            GridAutoPosition)
 COMPUTED_STYLE_PROP(grid_auto_rows,                GridAutoRows)
+COMPUTED_STYLE_PROP(grid_column_end,               GridColumnEnd)
+COMPUTED_STYLE_PROP(grid_column_start,             GridColumnStart)
+COMPUTED_STYLE_PROP(grid_row_end,                  GridRowEnd)
+COMPUTED_STYLE_PROP(grid_row_start,                GridRowStart)
 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)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7249,16 +7249,58 @@ SetGridTemplateAreas(const nsCSSValue& a
 
   default:
     const nsCSSValueGridTemplateAreas& value = aValue.GetGridTemplateAreas();
     aResult.mNamedAreas = value.mNamedAreas;
     aResult.mTemplates = value.mTemplates;
   }
 }
 
+static void
+SetGridLine(const nsCSSValue& aValue,
+            nsStyleGridLine& aResult,
+            const nsStyleGridLine& aParentValue,
+            bool& aCanStoreInRuleTree)
+
+{
+  switch (aValue.GetUnit()) {
+  case eCSSUnit_Null:
+    break;
+
+  case eCSSUnit_Inherit:
+    aCanStoreInRuleTree = false;
+    aResult = aParentValue;
+    break;
+
+  case eCSSUnit_Initial:
+  case eCSSUnit_Unset:
+  case eCSSUnit_Auto:
+    aResult.SetAuto();
+    break;
+
+  default:
+    aResult.SetAuto();  // Reset any existing value.
+    const nsCSSValueList* item = aValue.GetListValue();
+    do {
+      if (item->mValue.GetUnit() == eCSSUnit_Enumerated) {
+        aResult.mHasSpan = true;
+      } else if (item->mValue.GetUnit() == eCSSUnit_Integer) {
+        aResult.mInteger = item->mValue.GetIntValue();
+      } else if (item->mValue.GetUnit() == eCSSUnit_Ident) {
+        item->mValue.GetStringValue(aResult.mLineName);
+      } else {
+        NS_ASSERTION(false, "Unexpected unit");
+      }
+      item = item->mNext;
+    } while (item);
+    MOZ_ASSERT(!aResult.IsAuto(),
+               "should have set something away from default value");
+  }
+}
+
 const void*
 nsRuleNode::ComputePositionData(void* aStartStruct,
                                 const nsRuleData* aRuleData,
                                 nsStyleContext* aContext,
                                 nsRuleNode* aHighestNode,
                                 const RuleDetail aRuleDetail,
                                 const bool aCanStoreInRuleTree)
 {
@@ -7473,16 +7515,67 @@ nsRuleNode::ComputePositionData(void* aS
                    pos->mGridTemplateRows, parentPos->mGridTemplateRows,
                    aContext, mPresContext, canStoreInRuleTree);
 
   // grid-tempate-areas
   SetGridTemplateAreas(*aRuleData->ValueForGridTemplateAreas(),
                        pos->mGridTemplateAreas, parentPos->mGridTemplateAreas,
                        canStoreInRuleTree);
 
+  // grid-auto-position
+  const nsCSSValue& gridAutoPosition = *aRuleData->ValueForGridAutoPosition();
+  switch (gridAutoPosition.GetUnit()) {
+    case eCSSUnit_Null:
+      break;
+    case eCSSUnit_Inherit:
+      canStoreInRuleTree = false;
+      pos->mGridAutoPositionColumn = parentPos->mGridAutoPositionColumn;
+      pos->mGridAutoPositionRow = parentPos->mGridAutoPositionRow;
+      break;
+    case eCSSUnit_Initial:
+    case eCSSUnit_Unset:
+      // '1 / 1'
+      pos->mGridAutoPositionColumn.SetToInteger(1);
+      pos->mGridAutoPositionRow.SetToInteger(1);
+      break;
+    default:
+      SetGridLine(gridAutoPosition.GetPairValue().mXValue,
+                  pos->mGridAutoPositionColumn,
+                  parentPos->mGridAutoPositionColumn,
+                  canStoreInRuleTree);
+      SetGridLine(gridAutoPosition.GetPairValue().mYValue,
+                  pos->mGridAutoPositionRow,
+                  parentPos->mGridAutoPositionRow,
+                  canStoreInRuleTree);
+  }
+
+  // grid-column-start
+  SetGridLine(*aRuleData->ValueForGridColumnStart(),
+              pos->mGridColumnStart,
+              parentPos->mGridColumnStart,
+              canStoreInRuleTree);
+
+  // grid-column-end
+  SetGridLine(*aRuleData->ValueForGridColumnEnd(),
+              pos->mGridColumnEnd,
+              parentPos->mGridColumnEnd,
+              canStoreInRuleTree);
+
+  // grid-row-start
+  SetGridLine(*aRuleData->ValueForGridRowStart(),
+              pos->mGridRowStart,
+              parentPos->mGridRowStart,
+              canStoreInRuleTree);
+
+  // grid-row-end
+  SetGridLine(*aRuleData->ValueForGridRowEnd(),
+              pos->mGridRowEnd,
+              parentPos->mGridRowEnd,
+              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
@@ -1247,44 +1247,60 @@ 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();
+  mGridAutoPositionColumn.SetToInteger(1);
+  mGridAutoPositionRow.SetToInteger(1);
   // mGridTemplateRows, mGridTemplateColumns, and mGridTemplateAreas
   // get their default constructors
   // which initialize them to empty arrays,
   // which represent the properties' initial value 'none'.
+
+  // mGrid{Column,Row}{Start,End} get their default constructor, 'auto'
 }
 
 nsStylePosition::~nsStylePosition(void)
 {
   MOZ_COUNT_DTOR(nsStylePosition);
 }
 
 nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
   : mGridTemplateColumns(aSource.mGridTemplateColumns)
   , mGridTemplateRows(aSource.mGridTemplateRows)
   , mGridTemplateAreas(aSource.mGridTemplateAreas)
+  , mGridAutoPositionColumn(aSource.mGridAutoPositionColumn)
+  , mGridAutoPositionRow(aSource.mGridAutoPositionRow)
+  , mGridColumnStart(aSource.mGridColumnStart)
+  , mGridColumnEnd(aSource.mGridColumnEnd)
+  , mGridRowStart(aSource.mGridRowStart)
+  , mGridRowEnd(aSource.mGridRowEnd)
 {
   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(mGridTemplateAreas),
+                sizeof(mGridTemplateAreas) +
+                sizeof(mGridAutoPositionColumn) +
+                sizeof(mGridAutoPositionRow) +
+                sizeof(mGridColumnStart) +
+                sizeof(mGridColumnEnd) +
+                sizeof(mGridRowStart) +
+                sizeof(mGridRowEnd),
                 "Unexpected size or offset in nsStylePosition");
   memcpy((nsStylePosition*) this,
          &aSource,
          offsetof(nsStylePosition, mGridTemplateColumns));
 }
 
 static bool
 IsAutonessEqual(const nsStyleSides& aSides1, const nsStyleSides& aSides2)
@@ -1327,29 +1343,39 @@ nsChangeHint nsStylePosition::CalcDiffer
   // - flex-wrap changes whether a flex container's children are wrapped, which
   //   impacts their sizing/positioning and hence impacts the container's size.
   if (mAlignItems != aOther.mAlignItems ||
       mFlexDirection != aOther.mFlexDirection ||
       mFlexWrap != aOther.mFlexWrap) {
     return NS_CombineHint(hint, nsChangeHint_AllReflowHints);
   }
 
-
   // Properties that apply to grid containers:
+  // FIXME: only for grid containers
+  // (ie. 'display: grid' or 'display: inline-grid')
   if (mGridTemplateColumns != aOther.mGridTemplateColumns ||
       mGridTemplateRows != aOther.mGridTemplateRows ||
       mGridTemplateAreas != aOther.mGridTemplateAreas ||
       mGridAutoColumnsMin != aOther.mGridAutoColumnsMin ||
       mGridAutoColumnsMax != aOther.mGridAutoColumnsMax ||
       mGridAutoRowsMin != aOther.mGridAutoRowsMin ||
       mGridAutoRowsMax != aOther.mGridAutoRowsMax ||
       mGridAutoFlow != aOther.mGridAutoFlow) {
     return NS_CombineHint(hint, nsChangeHint_AllReflowHints);
   }
 
+  // Properties that apply to grid items:
+  // FIXME: only for grid items
+  // (ie. parent frame is 'display: grid' or 'display: inline-grid')
+  if (mGridColumnStart != aOther.mGridColumnStart ||
+      mGridColumnEnd != aOther.mGridColumnEnd ||
+      mGridRowStart != aOther.mGridRowStart ||
+      mGridRowEnd != aOther.mGridRowEnd) {
+    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);
   }
 
   // Properties that apply only to multi-line flex containers:
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1179,16 +1179,72 @@ struct nsStyleGridTrackList {
 
   inline bool operator!=(const nsStyleGridTrackList& aOther) const {
     return mLineNameLists != aOther.mLineNameLists ||
            mMinTrackSizingFunctions != aOther.mMinTrackSizingFunctions ||
            mMaxTrackSizingFunctions != aOther.mMaxTrackSizingFunctions;
   }
 };
 
+struct nsStyleGridLine {
+  // http://dev.w3.org/csswg/css-grid/#typedef-grid-line
+  bool mHasSpan;
+  int32_t mInteger;  // 0 means not provided
+  nsString mLineName;  // Empty string means not provided.
+
+  nsStyleGridLine()
+    : mHasSpan(false)
+    , mInteger(0)
+    // mLineName get its default constructor, the empty string
+  {
+  }
+
+  nsStyleGridLine(const nsStyleGridLine& aOther)
+  {
+    (*this) = aOther;
+  }
+
+  void operator=(const nsStyleGridLine& aOther)
+  {
+    mHasSpan = aOther.mHasSpan;
+    mInteger = aOther.mInteger;
+    mLineName = aOther.mLineName;
+  }
+
+  bool operator!=(const nsStyleGridLine& aOther) const
+  {
+    return mHasSpan != aOther.mHasSpan ||
+           mInteger != aOther.mInteger ||
+           mLineName != aOther.mLineName;
+  }
+
+  void SetToInteger(uint32_t value)
+  {
+    mHasSpan = false;
+    mInteger = value;
+    mLineName.Truncate();
+  }
+
+  void SetAuto()
+  {
+    mHasSpan = false;
+    mInteger = 0;
+    mLineName.Truncate();
+  }
+
+  bool IsAuto() const
+  {
+    bool haveInitialValues =  mInteger == 0 && mLineName.IsEmpty();
+    MOZ_ASSERT(!(haveInitialValues && mHasSpan),
+               "should not have 'span' when other components are "
+               "at their initial values");
+    return haveInitialValues;
+  }
+};
+
 struct nsStylePosition {
   nsStylePosition(void);
   nsStylePosition(const nsStylePosition& aOther);
   ~nsStylePosition(void);
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->AllocateFromShell(sz);
   }
@@ -1236,16 +1292,25 @@ struct nsStylePosition {
   // 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;
 
+  // We represent the "grid-auto-position" property in two parts:
+  nsStyleGridLine mGridAutoPositionColumn;
+  nsStyleGridLine mGridAutoPositionRow;
+
+  nsStyleGridLine mGridColumnStart;
+  nsStyleGridLine mGridColumnEnd;
+  nsStyleGridLine mGridRowStart;
+  nsStyleGridLine mGridRowEnd;
+
   bool WidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mWidth); }
   bool MinWidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mMinWidth); }
   bool MaxWidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mMaxWidth); }
 
   // Note that these functions count 'auto' as depending on the
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4934,16 +4934,112 @@ if (SpecialPowers.getBoolPref("layout.cs
 			"'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'",
 		]
 	};
+
+	var gridLineOtherValues = [
+		"foo",
+		"2",
+		"2 foo",
+		"foo 2",
+		"-3",
+		"-3 bar",
+		"bar -3",
+		"span 2",
+		"2 span",
+		"span foo",
+		"foo span",
+		"span 2 foo",
+		"span foo 2",
+		"2 foo span",
+		"foo 2 span",
+	];
+	var gridLineInvalidValues = [
+		"",
+		"4th",
+		"span",
+		"inherit 2",
+		"2 inherit",
+		"20px",
+		"2 3",
+		"2.5",
+		"2.0",
+		"0",
+		"0 foo",
+		"span 0",
+		"2 foo 3",
+		"foo 2 foo",
+		"2 span foo",
+		"foo span 2",
+		"span -3",
+		"span -3 bar",
+		"span 2 span",
+		"span foo span",
+		"span 2 foo span",
+	];
+
+	gCSSProperties["grid-column-start"] = {
+		domProp: "gridColumnStart",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "auto" ],
+		other_values: gridLineOtherValues,
+		invalid_values: gridLineInvalidValues
+	};
+	gCSSProperties["grid-column-end"] = {
+		domProp: "gridColumnEnd",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "auto" ],
+		other_values: gridLineOtherValues,
+		invalid_values: gridLineInvalidValues
+	};
+	gCSSProperties["grid-row-start"] = {
+		domProp: "gridRowStart",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "auto" ],
+		other_values: gridLineOtherValues,
+		invalid_values: gridLineInvalidValues
+	};
+	gCSSProperties["grid-row-end"] = {
+		domProp: "gridRowEnd",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "auto" ],
+		other_values: gridLineOtherValues,
+		invalid_values: gridLineInvalidValues
+	};
+
+	var gridAutoPositionOtherValues = [];
+	gridLineOtherValues.concat([ "auto" ]).forEach(function(val) {
+		gridAutoPositionOtherValues.push(" foo / " + val);
+		gridAutoPositionOtherValues.push(val + "/2");
+	});
+	var gridAutoPositionInvalidValues = [
+		"foo, bar",
+		"foo / bar / baz",
+	];
+	gridLineInvalidValues.forEach(function(val) {
+		gridAutoPositionInvalidValues.push("span 3 / " + val);
+		gridAutoPositionInvalidValues.push(val + " / foo");
+	});
+	gCSSProperties["grid-auto-position"] = {
+		domProp: "gridAutoPosition",
+		inherited: false,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "1 / 1" ],
+		other_values: gridAutoPositionOtherValues,
+		invalid_values: gridAutoPositionInvalidValues
+	};
 }
 
 if (SpecialPowers.getBoolPref("layout.css.image-orientation.enabled")) {
 	gCSSProperties["image-orientation"] = {
 		domProp: "imageOrientation",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [
--- a/layout/style/test/test_garbage_at_end_of_declarations.html
+++ b/layout/style/test/test_garbage_at_end_of_declarations.html
@@ -87,17 +87,19 @@ function test_property(property)
         !(value in gAllowsExtra[property])) {
       return;
     }
     if (property in gAllowsExtraUnusual &&
         value in gAllowsExtraUnusual[property]) {
       return;
     }
 
-    gElement.setAttribute("style", property + ": " + value + " blah");
+    // Include non-identifier characters in the garbage
+    // in case |value| would also be valid with a <custom-ident> added.
+    gElement.setAttribute("style", property + ": " + value + " +blah/");
     if ("subproperties" in info) {
       for (idx in info.subproperties) {
         var subprop = info.subproperties[idx];
         is(gDeclaration.getPropertyValue(subprop), "",
            ["expected garbage ignored after '", property, ": ", value,
             "' when looking at subproperty '", subprop, "'"].join(""));
       }
     } else {