Bug 686281 - Mask CSS parsing and Mask DOM API. r?dbaron draft
authorCJKu <cku@mozilla.com>
Mon, 21 Dec 2015 11:32:34 +0800
changeset 316593 ce9d2ff7abb9a37660547cbdcc1145658151520a
parent 316592 b6f53848b47815741c753fcb836cd01d079487d2
child 316594 216f907d456102d2cdd9b2f11ca145ac90afe6a1
push id8575
push usercku@mozilla.com
push dateMon, 21 Dec 2015 03:33:13 +0000
reviewersdbaron
bugs686281
milestone45.0a1
Bug 686281 - Mask CSS parsing and Mask DOM API. r?dbaron
layout/style/Declaration.cpp
layout/style/Declaration.h
layout/style/nsCSSParser.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsComputedDOMStylePropertyList.h
layout/style/nsRuleNode.cpp
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -197,16 +197,196 @@ Declaration::GetValue(nsCSSProperty aPro
 
 void
 Declaration::GetAuthoredValue(nsCSSProperty aProperty, nsAString& aValue) const
 {
   GetValue(aProperty, aValue, nsCSSValue::eAuthorSpecified);
 }
 
 void
+Declaration::GetImageLayerValue(
+                   nsCSSCompressedDataBlock *data,
+                   nsAString& aValue,
+                   nsCSSValue::Serialization aSerialization,
+                   const nsCSSProperty aTable[]) const
+{
+  // We know from above that all subproperties were specified.
+  // However, we still can't represent that in the shorthand unless
+  // they're all lists of the same length.  So if they're different
+  // lengths, we need to bail out.
+  // We also need to bail out if an item has background-clip and
+  // background-origin that are different and not the default
+  // values.  (We omit them if they're both default.)
+
+  // Common CSS properties for both background & mask layer.
+  const nsCSSValueList *image =
+    data->ValueFor(aTable[nsStyleImageLayers::image])->GetListValue();
+  const nsCSSValuePairList *repeat =
+    data->ValueFor(aTable[nsStyleImageLayers::repeat])->GetPairListValue();
+  const nsCSSValueList *position =
+    data->ValueFor(aTable[nsStyleImageLayers::position])->GetListValue();
+  const nsCSSValueList *clip =
+    data->ValueFor(aTable[nsStyleImageLayers::clip])->GetListValue();
+  const nsCSSValueList *origin =
+    data->ValueFor(aTable[nsStyleImageLayers::origin])->GetListValue();
+  const nsCSSValuePairList *size =
+    data->ValueFor(aTable[nsStyleImageLayers::size])->GetPairListValue();
+
+  // Background layer property.
+  const nsCSSValueList *attachment =
+    (aTable[nsStyleImageLayers::attachment] ==  eCSSProperty_UNKNOWN)?
+      nullptr :
+      data->ValueFor(aTable[nsStyleImageLayers::attachment])->GetListValue();
+
+  // Mask layer properties.
+  const nsCSSValueList *composite =
+    (aTable[nsStyleImageLayers::composite] ==  eCSSProperty_UNKNOWN)?
+      nullptr :
+      data->ValueFor(aTable[nsStyleImageLayers::composite])->GetListValue();
+  const nsCSSValueList *mode =
+    (aTable[nsStyleImageLayers::maskMode] ==  eCSSProperty_UNKNOWN)?
+      nullptr :
+      data->ValueFor(aTable[nsStyleImageLayers::maskMode])->GetListValue();
+
+  for (;;) {
+    // Serialize background-color at the beginning of the last item.
+    if (!image->mNext) {
+      if (aTable[nsStyleImageLayers::color] !=  eCSSProperty_UNKNOWN) {
+        AppendValueToString(aTable[nsStyleImageLayers::color], aValue,
+                            aSerialization);
+        aValue.Append(char16_t(' '));
+      }
+    }
+
+    image->mValue.AppendToString(aTable[nsStyleImageLayers::image], aValue,
+                                 aSerialization);
+
+    aValue.Append(char16_t(' '));
+    repeat->mXValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue,
+                                   aSerialization);
+    if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
+      repeat->mYValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue,
+                                     aSerialization);
+    }
+
+    if (attachment) {
+      aValue.Append(char16_t(' '));
+          attachment->mValue.AppendToString(aTable[nsStyleImageLayers::attachment],
+                                            aValue, aSerialization);
+    }
+
+    aValue.Append(char16_t(' '));
+    position->mValue.AppendToString(aTable[nsStyleImageLayers::position],
+                                    aValue, aSerialization);
+
+    if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
+        size->mYValue.GetUnit() != eCSSUnit_Auto) {
+      aValue.Append(char16_t(' '));
+      aValue.Append(char16_t('/'));
+      aValue.Append(char16_t(' '));
+      size->mXValue.AppendToString(aTable[nsStyleImageLayers::size], aValue,
+                                   aSerialization);
+      aValue.Append(char16_t(' '));
+      size->mYValue.AppendToString(aTable[nsStyleImageLayers::size], aValue,
+                                   aSerialization);
+    }
+
+    MOZ_ASSERT(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
+               origin->mValue.GetUnit() == eCSSUnit_Enumerated,
+               "should not have inherit/initial within list");
+
+    if (clip->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_CLIP_BORDER ||
+        origin->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_ORIGIN_PADDING) {
+      MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
+                   aTable[nsStyleImageLayers::origin]] ==
+                 nsCSSProps::kImageLayerOriginKTable);
+      MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
+                   aTable[nsStyleImageLayers::clip]] ==
+                 nsCSSProps::kImageLayerOriginKTable);
+      static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER ==
+                    NS_STYLE_IMAGELAYER_ORIGIN_BORDER &&
+                    NS_STYLE_IMAGELAYER_CLIP_PADDING ==
+                    NS_STYLE_IMAGELAYER_ORIGIN_PADDING &&
+                    NS_STYLE_IMAGELAYER_CLIP_CONTENT ==
+                    NS_STYLE_IMAGELAYER_ORIGIN_CONTENT,
+                    "mask-clip and mask-origin style constants must agree");
+      aValue.Append(char16_t(' '));
+      origin->mValue.AppendToString(aTable[nsStyleImageLayers::origin], aValue,
+                                    aSerialization);
+
+      if (clip->mValue != origin->mValue) {
+        aValue.Append(char16_t(' '));
+        clip->mValue.AppendToString(aTable[nsStyleImageLayers::clip], aValue,
+                                    aSerialization);
+      }
+    }
+
+    if (composite) {
+      aValue.Append(char16_t(' '));
+      composite->mValue.AppendToString(aTable[nsStyleImageLayers::composite],
+                                       aValue, aSerialization);
+    }
+
+    if (mode) {
+      aValue.Append(char16_t(' '));
+      mode->mValue.AppendToString(aTable[nsStyleImageLayers::maskMode],
+                                  aValue, aSerialization);
+    }
+
+    image = image->mNext;
+    repeat = repeat->mNext;
+    position = position->mNext;
+    clip = clip->mNext;
+    origin = origin->mNext;
+    size = size->mNext;
+    attachment = attachment ? attachment->mNext : nullptr;
+    composite = composite ? composite->mNext : nullptr;
+    mode = mode ? mode->mNext : nullptr;
+
+    if (!image) {
+      // This layer is an background layer
+      if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
+        if (repeat || position || clip || origin || size || attachment) {
+          // Uneven length lists, so can't be serialized as shorthand.
+          aValue.Truncate();
+          return;
+        }
+      // This layer is an mask layer
+      } else {
+        if (repeat || position || clip || origin || size || composite || mode) {
+          // Uneven length lists, so can't be serialized as shorthand.
+          aValue.Truncate();
+          return;
+        }
+      }
+      break;
+    }
+
+    // This layer is an background layer
+    if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
+      if (!repeat || !position || !clip || !origin || !size || !attachment) {
+        // Uneven length lists, so can't be serialized as shorthand.
+        aValue.Truncate();
+        return;
+      }
+    // This layer is an mask layer
+    } else {
+      if (!repeat || !position || !clip || !origin || !size ||
+          !composite || !mode) {
+        // Uneven length lists, so can't be serialized as shorthand.
+        aValue.Truncate();
+        return;
+      }
+    }
+    aValue.Append(char16_t(','));
+    aValue.Append(char16_t(' '));
+  }
+}
+
+void
 Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue,
                       nsCSSValue::Serialization aSerialization) const
 {
   aValue.Truncate(0);
 
   // simple properties are easy.
   if (!nsCSSProps::IsShorthand(aProperty)) {
     AppendValueToString(aProperty, aValue, aSerialization);
@@ -311,20 +491,20 @@ Declaration::GetValue(nsCSSProperty aPro
     } else {
       // In all other cases, serialize to the empty string.
     }
     return;
   }
 
   nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
   switch (aProperty) {
-    case eCSSProperty_margin: 
-    case eCSSProperty_padding: 
-    case eCSSProperty_border_color: 
-    case eCSSProperty_border_style: 
+    case eCSSProperty_margin:
+    case eCSSProperty_padding:
+    case eCSSProperty_border_color:
+    case eCSSProperty_border_style:
     case eCSSProperty_border_width: {
       const nsCSSProperty* subprops =
         nsCSSProps::SubpropertyEntryFor(aProperty);
       MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[0]).Find("-top") !=
                  kNotFound, "first subprop must be top");
       MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[1]).Find("-right") !=
                  kNotFound, "second subprop must be right");
       MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") !=
@@ -465,134 +645,23 @@ Declaration::GetValue(nsCSSProperty aPro
           !(isMozUseTextColor ||
             (aValue.Append(char16_t(' ')),
              AppendValueToString(subprops[2], aValue, aSerialization)))) {
         aValue.Truncate();
       }
       break;
     }
     case eCSSProperty_background: {
-      // We know from above that all subproperties were specified.
-      // However, we still can't represent that in the shorthand unless
-      // they're all lists of the same length.  So if they're different
-      // lengths, we need to bail out.
-      // We also need to bail out if an item has background-clip and
-      // background-origin that are different and not the default
-      // values.  (We omit them if they're both default.)
-      const nsCSSValueList *image =
-        data->ValueFor(eCSSProperty_background_image)->
-        GetListValue();
-      const nsCSSValuePairList *repeat =
-        data->ValueFor(eCSSProperty_background_repeat)->
-        GetPairListValue();
-      const nsCSSValueList *attachment =
-        data->ValueFor(eCSSProperty_background_attachment)->
-        GetListValue();
-      const nsCSSValueList *position =
-        data->ValueFor(eCSSProperty_background_position)->
-        GetListValue();
-      const nsCSSValueList *clip =
-        data->ValueFor(eCSSProperty_background_clip)->
-        GetListValue();
-      const nsCSSValueList *origin =
-        data->ValueFor(eCSSProperty_background_origin)->
-        GetListValue();
-      const nsCSSValuePairList *size =
-        data->ValueFor(eCSSProperty_background_size)->
-        GetPairListValue();
-      for (;;) {
-        // Serialize background-color at the beginning of the last item.
-        if (!image->mNext) {
-          AppendValueToString(eCSSProperty_background_color, aValue,
-                              aSerialization);
-          aValue.Append(char16_t(' '));
-        }
-
-        image->mValue.AppendToString(eCSSProperty_background_image, aValue,
-                                     aSerialization);
-        aValue.Append(char16_t(' '));
-        repeat->mXValue.AppendToString(eCSSProperty_background_repeat, aValue,
-                                       aSerialization);
-        if (repeat->mYValue.GetUnit() != eCSSUnit_Null) {
-          repeat->mYValue.AppendToString(eCSSProperty_background_repeat, aValue,
-                                         aSerialization);
-        }
-        aValue.Append(char16_t(' '));
-        attachment->mValue.AppendToString(eCSSProperty_background_attachment,
-                                          aValue, aSerialization);
-        aValue.Append(char16_t(' '));
-        position->mValue.AppendToString(eCSSProperty_background_position,
-                                        aValue, aSerialization);
-        
-        if (size->mXValue.GetUnit() != eCSSUnit_Auto ||
-            size->mYValue.GetUnit() != eCSSUnit_Auto) {
-          aValue.Append(char16_t(' '));
-          aValue.Append(char16_t('/'));
-          aValue.Append(char16_t(' '));
-          size->mXValue.AppendToString(eCSSProperty_background_size, aValue,
-                                       aSerialization);
-          aValue.Append(char16_t(' '));
-          size->mYValue.AppendToString(eCSSProperty_background_size, aValue,
-                                       aSerialization);
-        }
-
-        MOZ_ASSERT(clip->mValue.GetUnit() == eCSSUnit_Enumerated &&
-                   origin->mValue.GetUnit() == eCSSUnit_Enumerated,
-                   "should not have inherit/initial within list");
-
-        if (clip->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_CLIP_BORDER ||
-            origin->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_ORIGIN_PADDING) {
-          MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
-                       eCSSProperty_background_origin] ==
-                     nsCSSProps::kImageLayerOriginKTable);
-          MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
-                       eCSSProperty_background_clip] ==
-                     nsCSSProps::kImageLayerOriginKTable);
-          static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER ==
-                        NS_STYLE_IMAGELAYER_ORIGIN_BORDER &&
-                        NS_STYLE_IMAGELAYER_CLIP_PADDING ==
-                        NS_STYLE_IMAGELAYER_ORIGIN_PADDING &&
-                        NS_STYLE_IMAGELAYER_CLIP_CONTENT ==
-                        NS_STYLE_IMAGELAYER_ORIGIN_CONTENT,
-                        "bg-clip and bg-origin style constants must agree");
-          aValue.Append(char16_t(' '));
-          origin->mValue.AppendToString(eCSSProperty_background_origin, aValue,
-                                        aSerialization);
-
-          if (clip->mValue != origin->mValue) {
-            aValue.Append(char16_t(' '));
-            clip->mValue.AppendToString(eCSSProperty_background_clip, aValue,
-                                        aSerialization);
-          }
-        }
-
-        image = image->mNext;
-        repeat = repeat->mNext;
-        attachment = attachment->mNext;
-        position = position->mNext;
-        clip = clip->mNext;
-        origin = origin->mNext;
-        size = size->mNext;
-
-        if (!image) {
-          if (repeat || attachment || position || clip || origin || size) {
-            // Uneven length lists, so can't be serialized as shorthand.
-            aValue.Truncate();
-            return;
-          }
-          break;
-        }
-        if (!repeat || !attachment || !position || !clip || !origin || !size) {
-          // Uneven length lists, so can't be serialized as shorthand.
-          aValue.Truncate();
-          return;
-        }
-        aValue.Append(char16_t(','));
-        aValue.Append(char16_t(' '));
-      }
+      GetImageLayerValue(data, aValue, aSerialization,
+                         nsStyleImageLayers::kBackgroundLayerTable);
+      break;
+    }
+    case eCSSProperty_mask: {
+      GetImageLayerValue(data, aValue, aSerialization,
+                         nsStyleImageLayers::kMaskLayerTable);
       break;
     }
     case eCSSProperty_font: {
       // systemFont might not be present; other values are guaranteed to be
       // available based on the shorthand check at the beginning of the
       // function, as long as the prop is enabled
       const nsCSSValue *systemFont =
         data->ValueFor(eCSSProperty__x_system_font);
--- a/layout/style/Declaration.h
+++ b/layout/style/Declaration.h
@@ -373,16 +373,21 @@ private:
   void AppendPropertyAndValueToString(nsCSSProperty aProperty,
                                       nsAutoString& aValue,
                                       nsAString& aResult) const;
   // helper for ToString that serializes a custom property declaration for
   // a variable with the specified name
   void AppendVariableAndValueToString(const nsAString& aName,
                                       nsAString& aResult) const;
 
+  void GetImageLayerValue(nsCSSCompressedDataBlock *data,
+                          nsAString& aValue,
+                          nsCSSValue::Serialization aSerialization,
+                          const nsCSSProperty aTable[]) const;
+
 public:
   /**
    * Returns the property at the given index in the ordered list of
    * declarations.  For custom properties, eCSSPropertyExtra_variable
    * is returned.
    */
   nsCSSProperty GetPropertyAt(uint32_t aIndex) const {
     uint32_t value = mOrder[aIndex];
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -833,43 +833,44 @@ protected:
   };
   PriorityParsingStatus ParsePriority();
 
 #ifdef MOZ_XUL
   bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs);
 #endif
 
   // Property specific parsing routines
-  bool ParseBackground();
+  bool ParseImageLayers(const nsCSSProperty aTable[]);
 
   struct ImageLayersShorthandParseState {
     nsCSSValue&  mColor;
     nsCSSValueList* mImage;
     nsCSSValuePairList* mRepeat;
-    nsCSSValueList* mAttachment;
+    nsCSSValueList* mAttachment;   // A property for background layer only
     nsCSSValueList* mClip;
     nsCSSValueList* mOrigin;
     nsCSSValueList* mPosition;
     nsCSSValuePairList* mSize;
-    nsCSSValueList* mComposite;
-    nsCSSValueList* mMode;
+    nsCSSValueList* mComposite;    // A property for mask layer only
+    nsCSSValueList* mMode;         // A property for mask layer only
     ImageLayersShorthandParseState(
         nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat,
         nsCSSValueList* aAttachment, nsCSSValueList* aClip,
         nsCSSValueList* aOrigin, nsCSSValueList* aPosition,
         nsCSSValuePairList* aSize, nsCSSValueList* aComposite,
         nsCSSValueList* aMode) :
         mColor(aColor), mImage(aImage), mRepeat(aRepeat),
         mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin),
         mPosition(aPosition), mSize(aSize), mComposite(aComposite),
         mMode(aMode) {};
   };
 
   bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const;
-  bool ParseBackgroundItem(ImageLayersShorthandParseState& aState);
+  bool ParseImageLayersItem(ImageLayersShorthandParseState& aState,
+                            const nsCSSProperty aTable[]);
 
   bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id
   bool ParseImageLayerRepeat(nsCSSProperty aPropID);
   bool ParseImageLayerRepeatValues(nsCSSValuePair& aValue);
   bool ParseImageLayerPosition(nsCSSProperty aPropID);
 
   // ParseBoxPositionValues parses the CSS 2.1 background-position syntax,
   // which is still used by some properties. See ParsePositionValue
@@ -11076,17 +11077,17 @@ CSSParserImpl::ParseProperty(nsCSSProper
   return result;
 }
 
 bool
 CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID)
 {
   switch (aPropID) {  // handle shorthand or multiple properties
   case eCSSProperty_background:
-    return ParseBackground();
+    return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable);
   case eCSSProperty_background_repeat:
     return ParseImageLayerRepeat(eCSSProperty_background_repeat);
   case eCSSProperty_background_position:
     return ParseImageLayerPosition(eCSSProperty_background_position);
   case eCSSProperty_background_size:
     return ParseImageLayerSize(eCSSProperty_background_size);
   case eCSSProperty_border:
     return ParseBorderSide(kBorderTopIDs, true);
@@ -11256,16 +11257,24 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_marker:
     return ParseMarker();
   case eCSSProperty_paint_order:
     return ParsePaintOrder();
   case eCSSProperty_clip_path:
     return ParseClipPath();
   case eCSSProperty_scroll_snap_type:
     return ParseScrollSnapType();
+  case eCSSProperty_mask:
+    return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable);
+  case eCSSProperty_mask_repeat:
+    return ParseImageLayerRepeat(eCSSProperty_mask_repeat);
+  case eCSSProperty_mask_position:
+    return ParseImageLayerPosition(eCSSProperty_mask_position);
+  case eCSSProperty_mask_size:
+    return ParseImageLayerSize(eCSSProperty_mask_size);
   case eCSSProperty_all:
     return ParseAll();
   default:
     MOZ_ASSERT(false, "should not be called");
     return false;
   }
 }
 
@@ -11509,88 +11518,115 @@ BoxPositionMaskToCSSValue(int32_t aMask,
       val = NS_STYLE_IMAGELAYER_POSITION_BOTTOM;
     }
   }
 
   return nsCSSValue(val, eCSSUnit_Enumerated);
 }
 
 bool
-CSSParserImpl::ParseBackground()
+CSSParserImpl::ParseImageLayers(const nsCSSProperty aTable[])
 {
   nsAutoParseCompoundProperty compound(this);
 
   // background-color can only be set once, so it's not a list.
   nsCSSValue color;
 
   // Check first for inherit/initial/unset.
   if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) {
     // must be alone
     for (const nsCSSProperty* subprops =
-           nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
+           nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]);
          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
       AppendValue(*subprops, color);
     }
     return true;
   }
 
-  nsCSSValue image, repeat, attachment, clip, origin, position, size, composite, mode;
+  nsCSSValue image, repeat, attachment, clip, origin, position, size,
+             composite, maskMode;
   ImageLayersShorthandParseState state(color, image.SetListValue(),
                                        repeat.SetPairListValue(),
                                        attachment.SetListValue(), clip.SetListValue(),
                                        origin.SetListValue(), position.SetListValue(),
                                        size.SetPairListValue(), composite.SetListValue(),
-                                       mode.SetListValue());
+                                       maskMode.SetListValue());
 
   for (;;) {
-    if (!ParseBackgroundItem(state)) {
-      return false;
-    }
+    if (!ParseImageLayersItem(state, aTable)) {
+      return false;
+    }
+
     // If we saw a color, this must be the last item.
     if (color.GetUnit() != eCSSUnit_Null) {
-      break;
-    }
+      MOZ_ASSERT(aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN);
+      break;
+    }
+
     // If there's a comma, expect another item.
     if (!ExpectSymbol(',', true)) {
       break;
     }
+
+#define APPENDNEXT(propID_, propMember_, propType_) \
+  if (aTable[propID_] != eCSSProperty_UNKNOWN) { \
+    propMember_->mNext = new propType_; \
+    propMember_ = propMember_->mNext; \
+  }
     // Chain another entry on all the lists.
-    state.mImage->mNext = new nsCSSValueList;
-    state.mImage = state.mImage->mNext;
-    state.mRepeat->mNext = new nsCSSValuePairList;
-    state.mRepeat = state.mRepeat->mNext;
-    state.mAttachment->mNext = new nsCSSValueList;
-    state.mAttachment = state.mAttachment->mNext;
-    state.mClip->mNext = new nsCSSValueList;
-    state.mClip = state.mClip->mNext;
-    state.mOrigin->mNext = new nsCSSValueList;
-    state.mOrigin = state.mOrigin->mNext;
-    state.mPosition->mNext = new nsCSSValueList;
-    state.mPosition = state.mPosition->mNext;
-    state.mSize->mNext = new nsCSSValuePairList;
-    state.mSize = state.mSize->mNext;
+    APPENDNEXT(nsStyleImageLayers::image, state.mImage,
+               nsCSSValueList);
+    APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat,
+               nsCSSValuePairList);
+    APPENDNEXT(nsStyleImageLayers::clip, state.mClip,
+               nsCSSValueList);
+    APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin,
+               nsCSSValueList);
+    APPENDNEXT(nsStyleImageLayers::position, state.mPosition,
+               nsCSSValueList);
+    APPENDNEXT(nsStyleImageLayers::size, state.mSize,
+               nsCSSValuePairList);
+    APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment,
+               nsCSSValueList);
+    APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode,
+               nsCSSValueList);
+    APPENDNEXT(nsStyleImageLayers::composite, state.mComposite,
+               nsCSSValueList);
+#undef APPENDNEXT
   }
 
   // If we get to this point without seeing a color, provide a default.
-  if (color.GetUnit() == eCSSUnit_Null) {
-    color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
-  }
-
-  AppendValue(eCSSProperty_background_image,      image);
-  AppendValue(eCSSProperty_background_repeat,     repeat);
-  AppendValue(eCSSProperty_background_attachment, attachment);
-  AppendValue(eCSSProperty_background_clip,       clip);
-  AppendValue(eCSSProperty_background_origin,     origin);
-  AppendValue(eCSSProperty_background_position,   position);
-  AppendValue(eCSSProperty_background_size,       size);
-  AppendValue(eCSSProperty_background_color,      color);
-  return true;
-}
-
-// Helper for ParseBackgroundItem. Returns true if the passed-in nsCSSToken is
+ if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
+    if (color.GetUnit() == eCSSUnit_Null) {
+      color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor);
+    }
+  }
+
+#define APPENDVALUE(propID_, propValue_) \
+  if (aTable[propID_] != eCSSProperty_UNKNOWN) { \
+    AppendValue(aTable[propID_],  propValue_); \
+  }
+
+  APPENDVALUE(nsStyleImageLayers::image,      image);
+  APPENDVALUE(nsStyleImageLayers::repeat,     repeat);
+  APPENDVALUE(nsStyleImageLayers::clip,       clip);
+  APPENDVALUE(nsStyleImageLayers::origin,     origin);
+  APPENDVALUE(nsStyleImageLayers::position,   position);
+  APPENDVALUE(nsStyleImageLayers::size,       size);
+  APPENDVALUE(nsStyleImageLayers::color,      color);
+  APPENDVALUE(nsStyleImageLayers::attachment, attachment);
+  APPENDVALUE(nsStyleImageLayers::maskMode,   maskMode);
+  APPENDVALUE(nsStyleImageLayers::composite,  composite);
+
+#undef APPENDVALUE
+
+  return true;
+}
+
+// Helper for ParseImageLayersItem. Returns true if the passed-in nsCSSToken is
 // a function which is accepted for background-image.
 bool
 CSSParserImpl::IsFunctionTokenValidForImageLayerImage(
   const nsCSSToken& aToken) const
 {
   MOZ_ASSERT(aToken.mType == eCSSToken_Function,
              "Should only be called for function-typed tokens");
 
@@ -11611,18 +11647,19 @@ CSSParserImpl::IsFunctionTokenValidForIm
       funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") ||
       funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") ||
       funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") ||
       funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient")));
 }
 
 // Parse one item of the background shorthand property.
 bool
-CSSParserImpl::ParseBackgroundItem(CSSParserImpl::ImageLayersShorthandParseState& aState)
-
+CSSParserImpl::ParseImageLayersItem(
+  CSSParserImpl::ImageLayersShorthandParseState& aState,
+  const nsCSSProperty aTable[])
 {
   // Fill in the values that the shorthand will set if we don't find
   // other values.
   aState.mImage->mValue.SetNoneValue();
   aState.mRepeat->mXValue.SetIntValue(NS_STYLE_IMAGELAYER_REPEAT_REPEAT,
                                       eCSSUnit_Enumerated);
   aState.mRepeat->mYValue.Reset();
   aState.mAttachment->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL,
@@ -11632,23 +11669,28 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
   aState.mOrigin->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ORIGIN_PADDING,
                                      eCSSUnit_Enumerated);
   RefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
   aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
   positionArr->Item(1).SetPercentValue(0.0f);
   positionArr->Item(3).SetPercentValue(0.0f);
   aState.mSize->mXValue.SetAutoValue();
   aState.mSize->mYValue.SetAutoValue();
-
+  aState.mComposite->mValue.SetIntValue(NS_STYLE_COMPOSITE_MODE_ADD,
+                                        eCSSUnit_Enumerated);
+  aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_AUTO,
+                                   eCSSUnit_Enumerated);
   bool haveColor = false,
        haveImage = false,
        haveRepeat = false,
        haveAttach = false,
        havePositionAndSize = false,
        haveOrigin = false,
+       haveComposite = false,
+       haveMode = false,
        haveSomething = false;
 
   while (GetToken(true)) {
     nsCSSTokenType tt = mToken.mType;
     UngetToken(); // ...but we'll still cheat and use mToken
     if (tt == eCSSToken_Symbol) {
       // ExpectEndProperty only looks for symbols, and nothing else will
       // show up as one.
@@ -11662,28 +11704,30 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
           keyword == eCSSKeyword_initial ||
           keyword == eCSSKeyword_unset) {
         return false;
       } else if (keyword == eCSSKeyword_none) {
         if (haveImage)
           return false;
         haveImage = true;
         if (ParseSingleValueProperty(aState.mImage->mValue,
-                                     eCSSProperty_background_image) !=
+                                     aTable[nsStyleImageLayers::image]) !=
             CSSParseResult::Ok) {
           NS_NOTREACHED("should be able to parse");
           return false;
         }
-      } else if (nsCSSProps::FindKeyword(keyword,
+      } else if (aTable[nsStyleImageLayers::attachment] !=
+                 eCSSProperty_UNKNOWN &&
+                 nsCSSProps::FindKeyword(keyword,
                    nsCSSProps::kImageLayerAttachmentKTable, dummy)) {
         if (haveAttach)
           return false;
         haveAttach = true;
         if (ParseSingleValueProperty(aState.mAttachment->mValue,
-                                     eCSSProperty_background_attachment) !=
+                                     aTable[nsStyleImageLayers::attachment]) !=
             CSSParseResult::Ok) {
           NS_NOTREACHED("should be able to parse");
           return false;
         }
       } else if (nsCSSProps::FindKeyword(keyword,
                    nsCSSProps::kImageLayerRepeatKTable, dummy)) {
         if (haveRepeat)
           return false;
@@ -11712,70 +11756,98 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
           aState.mSize->mYValue = scratch.mYValue;
         }
       } else if (nsCSSProps::FindKeyword(keyword,
                    nsCSSProps::kImageLayerOriginKTable, dummy)) {
         if (haveOrigin)
           return false;
         haveOrigin = true;
         if (ParseSingleValueProperty(aState.mOrigin->mValue,
-                                     eCSSProperty_background_origin) !=
+                                     aTable[nsStyleImageLayers::origin]) !=
             CSSParseResult::Ok) {
           NS_NOTREACHED("should be able to parse");
           return false;
         }
 
         // The spec allows a second box value (for background-clip),
         // immediately following the first one (for background-origin).
 
         // 'background-clip' and 'background-origin' use the same keyword table
         MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
-                     eCSSProperty_background_origin] ==
+                     aTable[nsStyleImageLayers::origin]] ==
                    nsCSSProps::kImageLayerOriginKTable);
         MOZ_ASSERT(nsCSSProps::kKeywordTableTable[
-                     eCSSProperty_background_clip] ==
+                     aTable[nsStyleImageLayers::clip]] ==
                    nsCSSProps::kImageLayerOriginKTable);
         static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER ==
                       NS_STYLE_IMAGELAYER_ORIGIN_BORDER &&
                       NS_STYLE_IMAGELAYER_CLIP_PADDING ==
                       NS_STYLE_IMAGELAYER_ORIGIN_PADDING &&
                       NS_STYLE_IMAGELAYER_CLIP_CONTENT ==
                       NS_STYLE_IMAGELAYER_ORIGIN_CONTENT,
                       "bg-clip and bg-origin style constants must agree");
 
         CSSParseResult result =
           ParseSingleValueProperty(aState.mClip->mValue,
-                                   eCSSProperty_background_clip);
+                                   aTable[nsStyleImageLayers::clip]);
         MOZ_ASSERT(result != CSSParseResult::Error,
                    "how can failing to parse a single background-clip value "
                    "consume tokens?");
         if (result == CSSParseResult::NotFound) {
           // When exactly one <box> value is set, it is used for both
           // 'background-origin' and 'background-clip'.
           // See assertions above showing these values are compatible.
           aState.mClip->mValue = aState.mOrigin->mValue;
         }
-      } else {
-        if (haveColor)
+      } else if (aTable[nsStyleImageLayers::composite] != eCSSProperty_UNKNOWN &&
+                 nsCSSProps::FindKeyword(keyword,
+                   nsCSSProps::kImageLayerCompositeKTable, dummy)) {
+        if (haveComposite)
+          return false;
+        haveComposite = true;
+        if (ParseSingleValueProperty(aState.mComposite->mValue,
+                                     aTable[nsStyleImageLayers::composite]) !=
+            CSSParseResult::Ok) {
+          NS_NOTREACHED("should be able to parse");
+          return false;
+        }
+      } else if (aTable[nsStyleImageLayers::maskMode] != eCSSProperty_UNKNOWN &&
+                 nsCSSProps::FindKeyword(keyword,
+                   nsCSSProps::kImageLayerModeKTable, dummy)) {
+        if (haveMode)
           return false;
-        haveColor = true;
-        if (ParseSingleValueProperty(aState.mColor,
-                                     eCSSProperty_background_color) !=
+        haveMode = true;
+        if (ParseSingleValueProperty(aState.mMode->mValue,
+                                     aTable[nsStyleImageLayers::maskMode]) !=
             CSSParseResult::Ok) {
+          NS_NOTREACHED("should be able to parse");
+          return false;
+        }
+      } else {
+        if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
+          if (haveColor)
+            return false;
+          haveColor = true;
+          if (ParseSingleValueProperty(aState.mColor,
+                                       aTable[nsStyleImageLayers::color]) !=
+              CSSParseResult::Ok) {
+            return false;
+          }
+        } else {
           return false;
         }
       }
     } else if (tt == eCSSToken_URL ||
                (tt == eCSSToken_Function &&
                 IsFunctionTokenValidForImageLayerImage(mToken))) {
       if (haveImage)
         return false;
       haveImage = true;
       if (ParseSingleValueProperty(aState.mImage->mValue,
-                                   eCSSProperty_background_image) !=
+                                   aTable[nsStyleImageLayers::image]) !=
           CSSParseResult::Ok) {
         return false;
       }
     } else if (tt == eCSSToken_Dimension ||
                tt == eCSSToken_Number ||
                tt == eCSSToken_Percentage ||
                (tt == eCSSToken_Function &&
                 (mToken.mIdent.LowerCaseEqualsLiteral("calc") ||
@@ -11790,35 +11862,42 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
         nsCSSValuePair scratch;
         if (!ParseImageLayerSizeValues(scratch)) {
           return false;
         }
         aState.mSize->mXValue = scratch.mXValue;
         aState.mSize->mYValue = scratch.mYValue;
       }
     } else {
-      if (haveColor)
-        return false;
-      haveColor = true;
-      // Note: This parses 'inherit', 'initial' and 'unset', but
-      // we've already checked for them, so it's ok.
-      if (ParseSingleValueProperty(aState.mColor,
-                                   eCSSProperty_background_color) !=
-          CSSParseResult::Ok) {
-        return false;
-      }
-    }
+      if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
+        if (haveColor)
+          return false;
+        haveColor = true;
+        // Note: This parses 'inherit', 'initial' and 'unset', but
+        // we've already checked for them, so it's ok.
+        if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) {
+          if (ParseSingleValueProperty(aState.mColor,
+                                       aTable[nsStyleImageLayers::color]) !=
+              CSSParseResult::Ok) {
+            return false;
+          }
+        }
+      } else {
+        return false;
+      }
+    }
+
     haveSomething = true;
   }
 
   return haveSomething;
 }
 
 // This function is very similar to ParseScrollSnapCoordinate,
-// ParseImageLayerPosition, and ParseBackgroundSize.
+// ParseImageLayerPosition, and ParseImageLayersSize.
 bool
 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
 {
   // aPropID is a single value prop-id
   nsCSSValue value;
   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
     nsCSSValueList* item = value.SetListValue();
@@ -11885,17 +11964,17 @@ CSSParserImpl::ParseImageLayerRepeatValu
     }
     return true;
   }
 
   return false;
 }
 
 // This function is very similar to ParseScrollSnapCoordinate,
-// ParseBackgroundList, and ParseBackgroundSize.
+// ParseImageLayers, ParseImageLayerSize.
 bool
 CSSParserImpl::ParseImageLayerPosition(nsCSSProperty aPropID)
 {
   nsCSSValue value;
   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
     nsCSSValue itemValue;
     if (!ParsePositionValue(itemValue)) {
@@ -12203,17 +12282,17 @@ CSSParserImpl::ParsePositionValue(nsCSSV
     yEdge = swapEdge;
     yOffset = swapOffset;
   }
 
   return true;
 }
 
 // This function is very similar to ParseScrollSnapCoordinate,
-// ParseBackgroundList, and ParseBackgroundPosition.
+// ParseImageLayers, and ParseImageLayerPosition.
 bool
 CSSParserImpl::ParseImageLayerSize(nsCSSProperty aPropID)
 {
   nsCSSValue value;
   // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
   if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) {
     nsCSSValuePair valuePair;
     if (!ParseImageLayerSizeValues(valuePair)) {
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2115,114 +2115,60 @@ nsComputedDOMStyle::SetValueToStyleImage
       break;
     default:
       NS_NOTREACHED("unexpected image type");
       break;
   }
 }
 
 CSSValue*
-nsComputedDOMStyle::DoGetBackgroundImage()
-{
-  const nsStyleBackground* bg = StyleBackground();
-
+nsComputedDOMStyle::DoGetImageLayerImage(const nsStyleImageLayers& aLayers)
+{
   nsDOMCSSValueList *valueList = GetROCSSValueList(true);
 
-  for (uint32_t i = 0, i_end = bg->mLayers.mImageCount; i < i_end; ++i) {
+  for (uint32_t i = 0, i_end = aLayers.mImageCount; i < i_end; ++i) {
     nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
     valueList->AppendCSSValue(val);
 
-    const nsStyleImage& image = bg->mLayers.mLayers[i].mImage;
+    const nsStyleImage& image = aLayers.mLayers[i].mImage;
     SetValueToStyleImage(image, val);
   }
 
   return valueList;
 }
 
 CSSValue*
-nsComputedDOMStyle::DoGetBackgroundBlendMode()
-{
-  return GetBackgroundList(&nsStyleImageLayers::Layer::mBlendMode,
-                           &nsStyleImageLayers::mBlendModeCount,
-                           StyleBackground()->mLayers,
-                           nsCSSProps::kBlendModeKTable);
-}
-
-CSSValue*
-nsComputedDOMStyle::DoGetBackgroundOrigin()
-{
-  return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin,
-                           &nsStyleImageLayers::mOriginCount,
-                           StyleBackground()->mLayers,
-                           nsCSSProps::kImageLayerOriginKTable);
-}
-
-void
-nsComputedDOMStyle::SetValueToPositionCoord(
-    const nsStyleImageLayers::Position::PositionCoord& aCoord,
-    nsROCSSPrimitiveValue* aValue)
-{
-  if (!aCoord.mHasPercent) {
-    MOZ_ASSERT(aCoord.mPercent == 0.0f,
-               "Shouldn't have mPercent!");
-    aValue->SetAppUnits(aCoord.mLength);
-  } else if (aCoord.mLength == 0) {
-    aValue->SetPercent(aCoord.mPercent);
-  } else {
-    SetValueToCalc(&aCoord, aValue);
-  }
-}
-
-void
-nsComputedDOMStyle::SetValueToPosition(
-    const nsStyleImageLayers::Position& aPosition,
-    nsDOMCSSValueList* aValueList)
-{
-  nsROCSSPrimitiveValue* valX = new nsROCSSPrimitiveValue;
-  aValueList->AppendCSSValue(valX);
-  SetValueToPositionCoord(aPosition.mXPosition, valX);
-
-  nsROCSSPrimitiveValue* valY = new nsROCSSPrimitiveValue;
-  aValueList->AppendCSSValue(valY);
-  SetValueToPositionCoord(aPosition.mYPosition, valY);
-}
-
-CSSValue*
-nsComputedDOMStyle::DoGetBackgroundPosition()
-{
-  const nsStyleBackground* bg = StyleBackground();
-
+nsComputedDOMStyle::DoGetImageLayerPosition(const nsStyleImageLayers& aLayers)
+{
   nsDOMCSSValueList *valueList = GetROCSSValueList(true);
 
-  for (uint32_t i = 0, i_end = bg->mLayers.mPositionCount; i < i_end; ++i) {
+  for (uint32_t i = 0, i_end = aLayers.mPositionCount; i < i_end; ++i) {
     nsDOMCSSValueList *itemList = GetROCSSValueList(false);
     valueList->AppendCSSValue(itemList);
 
-    SetValueToPosition(bg->mLayers.mLayers[i].mPosition, itemList);
+    SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
   }
 
   return valueList;
 }
 
 CSSValue*
-nsComputedDOMStyle::DoGetBackgroundRepeat()
-{
-  const nsStyleBackground* bg = StyleBackground();
-
+nsComputedDOMStyle::DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers)
+{
   nsDOMCSSValueList *valueList = GetROCSSValueList(true);
 
-  for (uint32_t i = 0, i_end = bg->mLayers.mRepeatCount; i < i_end; ++i) {
+  for (uint32_t i = 0, i_end = aLayers.mRepeatCount; i < i_end; ++i) {
     nsDOMCSSValueList *itemList = GetROCSSValueList(false);
     valueList->AppendCSSValue(itemList);
 
     nsROCSSPrimitiveValue *valX = new nsROCSSPrimitiveValue;
     itemList->AppendCSSValue(valX);
 
-    const uint8_t& xRepeat = bg->mLayers.mLayers[i].mRepeat.mXRepeat;
-    const uint8_t& yRepeat = bg->mLayers.mLayers[i].mRepeat.mYRepeat;
+    const uint8_t& xRepeat = aLayers.mLayers[i].mRepeat.mXRepeat;
+    const uint8_t& yRepeat = aLayers.mLayers[i].mRepeat.mYRepeat;
 
     bool hasContraction = true;
     unsigned contraction;
     if (xRepeat == yRepeat) {
       contraction = xRepeat;
     } else if (xRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT &&
                yRepeat == NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT) {
       contraction = NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X;
@@ -2234,37 +2180,34 @@ nsComputedDOMStyle::DoGetBackgroundRepea
     }
 
     if (hasContraction) {
       valX->SetIdent(nsCSSProps::ValueToKeywordEnum(contraction,
                                          nsCSSProps::kImageLayerRepeatKTable));
     } else {
       nsROCSSPrimitiveValue *valY = new nsROCSSPrimitiveValue;
       itemList->AppendCSSValue(valY);
-      
+
       valX->SetIdent(nsCSSProps::ValueToKeywordEnum(xRepeat,
                                           nsCSSProps::kImageLayerRepeatKTable));
       valY->SetIdent(nsCSSProps::ValueToKeywordEnum(yRepeat,
                                           nsCSSProps::kImageLayerRepeatKTable));
     }
   }
 
   return valueList;
 }
 
-
-CSSValue*
-nsComputedDOMStyle::DoGetBackgroundSize()
-{
-  const nsStyleBackground* bg = StyleBackground();
-
+CSSValue*
+nsComputedDOMStyle::DoGetImageLayerSize(const nsStyleImageLayers& aLayers)
+{
   nsDOMCSSValueList *valueList = GetROCSSValueList(true);
 
-  for (uint32_t i = 0, i_end = bg->mLayers.mSizeCount; i < i_end; ++i) {
-    const nsStyleImageLayers::Size &size = bg->mLayers.mLayers[i].mSize;
+  for (uint32_t i = 0, i_end = aLayers.mSizeCount; i < i_end; ++i) {
+    const nsStyleImageLayers::Size &size = aLayers.mLayers[i].mSize;
 
     switch (size.mWidthType) {
       case nsStyleImageLayers::Size::eContain:
       case nsStyleImageLayers::Size::eCover: {
         MOZ_ASSERT(size.mWidthType == size.mHeightType,
                    "unsynced types");
         nsCSSKeyword keyword = size.mWidthType == nsStyleImageLayers::Size::eContain
                              ? eCSSKeyword_contain
@@ -2328,16 +2271,92 @@ nsComputedDOMStyle::DoGetBackgroundSize(
       }
     }
   }
 
   return valueList;
 }
 
 CSSValue*
+nsComputedDOMStyle::DoGetBackgroundImage()
+{
+  const nsStyleImageLayers& layers = StyleBackground()->mLayers;
+  return DoGetImageLayerImage(layers);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetBackgroundBlendMode()
+{
+  return GetBackgroundList(&nsStyleImageLayers::Layer::mBlendMode,
+                           &nsStyleImageLayers::mBlendModeCount,
+                           StyleBackground()->mLayers,
+                           nsCSSProps::kBlendModeKTable);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetBackgroundOrigin()
+{
+  return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin,
+                           &nsStyleImageLayers::mOriginCount,
+                           StyleBackground()->mLayers,
+                           nsCSSProps::kImageLayerOriginKTable);
+}
+
+void
+nsComputedDOMStyle::SetValueToPositionCoord(
+    const nsStyleImageLayers::Position::PositionCoord& aCoord,
+    nsROCSSPrimitiveValue* aValue)
+{
+  if (!aCoord.mHasPercent) {
+    MOZ_ASSERT(aCoord.mPercent == 0.0f,
+               "Shouldn't have mPercent!");
+    aValue->SetAppUnits(aCoord.mLength);
+  } else if (aCoord.mLength == 0) {
+    aValue->SetPercent(aCoord.mPercent);
+  } else {
+    SetValueToCalc(&aCoord, aValue);
+  }
+}
+
+void
+nsComputedDOMStyle::SetValueToPosition(
+    const nsStyleImageLayers::Position& aPosition,
+    nsDOMCSSValueList* aValueList)
+{
+  nsROCSSPrimitiveValue* valX = new nsROCSSPrimitiveValue;
+  aValueList->AppendCSSValue(valX);
+  SetValueToPositionCoord(aPosition.mXPosition, valX);
+
+  nsROCSSPrimitiveValue* valY = new nsROCSSPrimitiveValue;
+  aValueList->AppendCSSValue(valY);
+  SetValueToPositionCoord(aPosition.mYPosition, valY);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetBackgroundPosition()
+{
+  const nsStyleImageLayers& layers = StyleBackground()->mLayers;
+  return DoGetImageLayerPosition(layers);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetBackgroundRepeat()
+{
+  const nsStyleImageLayers& layers = StyleBackground()->mLayers;
+  return DoGetImageLayerRepeat(layers);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetBackgroundSize()
+{
+  const nsStyleImageLayers& layers = StyleBackground()->mLayers;
+  return DoGetImageLayerSize(layers);
+}
+
+CSSValue*
 nsComputedDOMStyle::DoGetGridTemplateAreas()
 {
   const css::GridTemplateAreasValue* areas =
     StylePosition()->mGridTemplateAreas;
   if (!areas) {
     nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
     val->SetIdent(eCSSKeyword_none);
     return val;
@@ -5812,29 +5831,105 @@ nsComputedDOMStyle::DoGetFilter()
     valueList->AppendCSSValue(value);
   }
   return valueList;
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetMask()
 {
+  const nsStyleSVGReset* svg = StyleSVGReset();
+  const nsStyleImageLayers::Layer& firstLayer = svg->mLayers.mLayers[0];
+
+  if (svg->mLayers.mImageCount > 1 ||
+      firstLayer.mClip != NS_STYLE_IMAGELAYER_CLIP_BORDER ||
+      firstLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_PADDING ||
+      firstLayer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL ||
+      firstLayer.mBlendMode != NS_STYLE_BLEND_NORMAL ||
+      firstLayer.mComposite != NS_STYLE_COMPOSITE_MODE_ADD ||
+      firstLayer.mMaskMode != NS_STYLE_MASK_MODE_AUTO ||
+      !nsStyleImageLayers::Position::IsInitialValue(firstLayer.mPosition) ||
+      !nsStyleImageLayers::Repeat::IsInitialValue(firstLayer.mRepeat) ||
+      !nsStyleImageLayers::Size::IsInitialValue(firstLayer.mSize)){
+    return nullptr;
+  }
+
   nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
 
-  const nsStyleSVGReset* svg = StyleSVGReset();
-
   if (svg->mMask)
     val->SetURI(svg->mMask);
   else
     val->SetIdent(eCSSKeyword_none);
 
   return val;
 }
 
 CSSValue*
+nsComputedDOMStyle::DoGetMaskClip()
+{
+  return GetBackgroundList(&nsStyleImageLayers::Layer::mClip,
+                           &nsStyleImageLayers::mClipCount,
+                           StyleSVGReset()->mLayers,
+                           nsCSSProps::kImageLayerOriginKTable);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetMaskComposite()
+{
+  return GetBackgroundList(&nsStyleImageLayers::Layer::mComposite,
+                           &nsStyleImageLayers::mCompositeCount,
+                           StyleSVGReset()->mLayers,
+                           nsCSSProps::kImageLayerCompositeKTable);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetMaskImage()
+{
+  const nsStyleImageLayers& layers = StyleSVGReset()->mLayers;
+  return DoGetImageLayerImage(layers);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetMaskMode()
+{
+  return GetBackgroundList(&nsStyleImageLayers::Layer::mMaskMode,
+                           &nsStyleImageLayers::mMaskModeCount,
+                           StyleSVGReset()->mLayers,
+                           nsCSSProps::kImageLayerModeKTable);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetMaskOrigin()
+{
+  return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin,
+                           &nsStyleImageLayers::mOriginCount,
+                           StyleSVGReset()->mLayers,
+                           nsCSSProps::kImageLayerOriginKTable);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetMaskPosition()
+{
+  const nsStyleImageLayers& layers = StyleSVGReset()->mLayers;
+  return DoGetImageLayerPosition(layers);
+}
+CSSValue*
+nsComputedDOMStyle::DoGetMaskRepeat()
+{
+  const nsStyleImageLayers& layers = StyleSVGReset()->mLayers;
+  return DoGetImageLayerRepeat(layers);
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetMaskSize()
+{
+  const nsStyleImageLayers& layers = StyleSVGReset()->mLayers;
+  return DoGetImageLayerSize(layers);
+}
+CSSValue*
 nsComputedDOMStyle::DoGetMaskType()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(StyleSVGReset()->mMaskType,
                                    nsCSSProps::kMaskTypeKTable));
   return val;
 }
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -265,27 +265,44 @@ private:
   mozilla::dom::CSSValue* DoGetGridTemplateRows();
   mozilla::dom::CSSValue* DoGetGridColumnStart();
   mozilla::dom::CSSValue* DoGetGridColumnEnd();
   mozilla::dom::CSSValue* DoGetGridRowStart();
   mozilla::dom::CSSValue* DoGetGridRowEnd();
   mozilla::dom::CSSValue* DoGetGridColumnGap();
   mozilla::dom::CSSValue* DoGetGridRowGap();
 
+  /* StyleImageLayer properties */
+  mozilla::dom::CSSValue* DoGetImageLayerImage(const nsStyleImageLayers& aLayers);
+  mozilla::dom::CSSValue* DoGetImageLayerPosition(const nsStyleImageLayers& aLayers);
+  mozilla::dom::CSSValue* DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers);
+  mozilla::dom::CSSValue* DoGetImageLayerSize(const nsStyleImageLayers& aLayers);
+
   /* 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();
   mozilla::dom::CSSValue* DoGetBackgroundBlendMode();
   mozilla::dom::CSSValue* DoGetBackgroundOrigin();
   mozilla::dom::CSSValue* DoGetBackgroundSize();
 
+  /* Mask properties */
+  mozilla::dom::CSSValue* DoGetMask();
+  mozilla::dom::CSSValue* DoGetMaskImage();
+  mozilla::dom::CSSValue* DoGetMaskPosition();
+  mozilla::dom::CSSValue* DoGetMaskRepeat();
+  mozilla::dom::CSSValue* DoGetMaskClip();
+  mozilla::dom::CSSValue* DoGetMaskOrigin();
+  mozilla::dom::CSSValue* DoGetMaskSize();
+  mozilla::dom::CSSValue* DoGetMaskMode();
+  mozilla::dom::CSSValue* DoGetMaskComposite();
+
   /* Padding properties */
   mozilla::dom::CSSValue* DoGetPaddingTop();
   mozilla::dom::CSSValue* DoGetPaddingBottom();
   mozilla::dom::CSSValue* DoGetPaddingLeft();
   mozilla::dom::CSSValue* DoGetPaddingRight();
 
   /* Table Properties */
   mozilla::dom::CSSValue* DoGetBorderCollapse();
@@ -524,17 +541,16 @@ private:
   mozilla::dom::CSSValue* DoGetTextRendering();
 
   mozilla::dom::CSSValue* DoGetFloodColor();
   mozilla::dom::CSSValue* DoGetLightingColor();
   mozilla::dom::CSSValue* DoGetStopColor();
 
   mozilla::dom::CSSValue* DoGetClipPath();
   mozilla::dom::CSSValue* DoGetFilter();
-  mozilla::dom::CSSValue* DoGetMask();
   mozilla::dom::CSSValue* DoGetMaskType();
   mozilla::dom::CSSValue* DoGetPaintOrder();
 
   /* Custom properties */
   mozilla::dom::CSSValue* DoGetCustomProperty(const nsAString& aPropertyName);
 
   nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited);
 
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -313,16 +313,24 @@ COMPUTED_STYLE_PROP(filter,             
 COMPUTED_STYLE_PROP(flood_color,                   FloodColor)
 COMPUTED_STYLE_PROP(flood_opacity,                 FloodOpacity)
 COMPUTED_STYLE_PROP(image_rendering,               ImageRendering)
 COMPUTED_STYLE_PROP(lighting_color,                LightingColor)
 COMPUTED_STYLE_PROP(marker_end,                    MarkerEnd)
 COMPUTED_STYLE_PROP(marker_mid,                    MarkerMid)
 COMPUTED_STYLE_PROP(marker_start,                  MarkerStart)
 COMPUTED_STYLE_PROP(mask,                          Mask)
+COMPUTED_STYLE_PROP(mask_clip,                     MaskClip)
+COMPUTED_STYLE_PROP(mask_composite,                MaskComposite)
+COMPUTED_STYLE_PROP(mask_image,                    MaskImage)
+COMPUTED_STYLE_PROP(mask_mode,                     MaskMode)
+COMPUTED_STYLE_PROP(mask_origin,                   MaskOrigin)
+COMPUTED_STYLE_PROP(mask_position,                 MaskPosition)
+COMPUTED_STYLE_PROP(mask_repeat,                   MaskRepeat)
+COMPUTED_STYLE_PROP(mask_size,                     MaskSize)
 COMPUTED_STYLE_PROP(mask_type,                     MaskType)
 COMPUTED_STYLE_PROP(paint_order,                   PaintOrder)
 COMPUTED_STYLE_PROP(shape_rendering,               ShapeRendering)
 COMPUTED_STYLE_PROP(stop_color,                    StopColor)
 COMPUTED_STYLE_PROP(stop_opacity,                  StopOpacity)
 COMPUTED_STYLE_PROP(stroke,                        Stroke)
 COMPUTED_STYLE_PROP(stroke_dasharray,              StrokeDasharray)
 COMPUTED_STYLE_PROP(stroke_dashoffset,             StrokeDashoffset)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -6800,103 +6800,122 @@ nsRuleNode::ComputeBackgroundData(void* 
 
   uint32_t maxItemCount = 1;
   bool rebuild = false;
 
   // background-image: url (stored as image), none, inherit [list]
   nsStyleImage initialImage;
   SetImageLayerList(aContext, *aRuleData->ValueForBackgroundImage(),
                     bg->mLayers.mLayers,
-                    parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mImage,
-                    initialImage, parentBG->mLayers.mImageCount, bg->mLayers.mImageCount,
+                    parentBG->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mImage,
+                    initialImage, parentBG->mLayers.mImageCount,
+                    bg->mLayers.mImageCount,
                     maxItemCount, rebuild, conditions);
 
   // background-repeat: enum, inherit, initial [pair list]
   nsStyleImageLayers::Repeat initialRepeat;
   initialRepeat.SetInitialValues();
   SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundRepeat(),
                         bg->mLayers.mLayers,
-                        parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mRepeat,
+                        parentBG->mLayers.mLayers,
+                        &nsStyleImageLayers::Layer::mRepeat,
                         initialRepeat, parentBG->mLayers.mRepeatCount,
                         bg->mLayers.mRepeatCount, maxItemCount, rebuild,
                         conditions);
 
   // background-attachment: enum, inherit, initial [list]
   SetImageLayerList(aContext, *aRuleData->ValueForBackgroundAttachment(),
                     bg->mLayers.mLayers, parentBG->mLayers.mLayers,
                     &nsStyleImageLayers::Layer::mAttachment,
                     uint8_t(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL),
                     parentBG->mLayers.mAttachmentCount,
                     bg->mLayers.mAttachmentCount, maxItemCount, rebuild,
                     conditions);
 
   // background-clip: enum, inherit, initial [list]
   SetImageLayerList(aContext, *aRuleData->ValueForBackgroundClip(),
                     bg->mLayers.mLayers,
-                    parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mClip,
-                    uint8_t(NS_STYLE_IMAGELAYER_CLIP_BORDER), parentBG->mLayers.mClipCount,
+                    parentBG->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mClip,
+                    uint8_t(NS_STYLE_IMAGELAYER_CLIP_BORDER),
+                    parentBG->mLayers.mClipCount,
                     bg->mLayers.mClipCount, maxItemCount, rebuild, conditions);
 
   // background-blend-mode: enum, inherit, initial [list]
   SetImageLayerList(aContext, *aRuleData->ValueForBackgroundBlendMode(),
                     bg->mLayers.mLayers,
-                    parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mBlendMode,
-                    uint8_t(NS_STYLE_BLEND_NORMAL), parentBG->mLayers.mBlendModeCount,
+                    parentBG->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mBlendMode,
+                    uint8_t(NS_STYLE_BLEND_NORMAL),
+                    parentBG->mLayers.mBlendModeCount,
                     bg->mLayers.mBlendModeCount, maxItemCount, rebuild,
                     conditions);
 
   // background-origin: enum, inherit, initial [list]
   SetImageLayerList(aContext, *aRuleData->ValueForBackgroundOrigin(),
                     bg->mLayers.mLayers,
-                    parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mOrigin,
-                    uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), parentBG->mLayers.mOriginCount,
+                    parentBG->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mOrigin,
+                    uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
+                    parentBG->mLayers.mOriginCount,
                     bg->mLayers.mOriginCount, maxItemCount, rebuild,
                     conditions);
 
   // background-position: enum, length, percent (flags), inherit [pair list]
   nsStyleImageLayers::Position initialPosition;
   initialPosition.SetInitialPercentValues(0.0f);
   SetImageLayerList(aContext, *aRuleData->ValueForBackgroundPosition(),
                     bg->mLayers.mLayers,
-                    parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mPosition,
+                    parentBG->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mPosition,
                     initialPosition, parentBG->mLayers.mPositionCount,
                     bg->mLayers.mPositionCount, maxItemCount, rebuild,
                     conditions);
 
   // background-size: enum, length, auto, inherit, initial [pair list]
   nsStyleImageLayers::Size initialSize;
   initialSize.SetInitialValues();
   SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundSize(),
                         bg->mLayers.mLayers,
-                        parentBG->mLayers.mLayers, &nsStyleImageLayers::Layer::mSize,
+                        parentBG->mLayers.mLayers,
+                        &nsStyleImageLayers::Layer::mSize,
                         initialSize, parentBG->mLayers.mSizeCount,
                         bg->mLayers.mSizeCount, maxItemCount, rebuild,
                         conditions);
 
   if (rebuild) {
     // Delete any extra items.  We need to keep layers in which any
     // property was specified.
     bg->mLayers.mLayers.TruncateLength(maxItemCount);
 
     uint32_t fillCount = bg->mLayers.mImageCount;
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mImage,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mImage,
                        bg->mLayers.mImageCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mRepeat,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mRepeat,
                        bg->mLayers.mRepeatCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mAttachment,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mAttachment,
                        bg->mLayers.mAttachmentCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mClip,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mClip,
                        bg->mLayers.mClipCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mBlendMode,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mBlendMode,
                        bg->mLayers.mBlendModeCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mOrigin,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mOrigin,
                        bg->mLayers.mOriginCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mPosition,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mPosition,
                        bg->mLayers.mPositionCount, fillCount);
-    FillBackgroundList(bg->mLayers.mLayers, &nsStyleImageLayers::Layer::mSize,
+    FillBackgroundList(bg->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mSize,
                        bg->mLayers.mSizeCount, fillCount);
   }
 
   // Now that the dust has settled, register the images with the document
   bg->mLayers.TrackImages(aContext->PresContext());
 
   COMPUTE_END_RESET(Background, bg)
 }
@@ -9666,16 +9685,135 @@ nsRuleNode::ComputeSVGResetData(void* aS
   // mask-type: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForMaskType(),
               svgReset->mMaskType,
               conditions,
               SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
               parentSVGReset->mMaskType,
               NS_STYLE_MASK_TYPE_LUMINANCE, 0, 0, 0, 0);
 
+  uint32_t maxItemCount = 1;
+  bool rebuild = false;
+
+  // mask-image: none | <url> | <image-list> | <element-reference>  | <gradient>
+  nsStyleImage initialImage;
+  SetImageLayerList(aContext, *aRuleData->ValueForMaskImage(),
+                    svgReset->mLayers.mLayers,
+                    parentSVGReset->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mImage,
+                    initialImage, parentSVGReset->mLayers.mImageCount,
+                    svgReset->mLayers.mImageCount,
+                    maxItemCount, rebuild, conditions);
+
+  // mask-repeat: enum, inherit, initial [pair list]
+  nsStyleImageLayers::Repeat initialRepeat;
+  initialRepeat.SetInitialValues();
+  SetImageLayerPairList(aContext, *aRuleData->ValueForMaskRepeat(),
+                        svgReset->mLayers.mLayers,
+                        parentSVGReset->mLayers.mLayers,
+                        &nsStyleImageLayers::Layer::mRepeat,
+                        initialRepeat, parentSVGReset->mLayers.mRepeatCount,
+                        svgReset->mLayers.mRepeatCount, maxItemCount, rebuild,
+                        conditions);
+
+  // mask-clip: enum, inherit, initial [list]
+  SetImageLayerList(aContext, *aRuleData->ValueForMaskClip(),
+                    svgReset->mLayers.mLayers,
+                    parentSVGReset->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mClip,
+                    uint8_t(NS_STYLE_IMAGELAYER_CLIP_BORDER),
+                    parentSVGReset->mLayers.mClipCount,
+                    svgReset->mLayers.mClipCount, maxItemCount, rebuild,
+                    conditions);
+
+  // mask-origin: enum, inherit, initial [list]
+  SetImageLayerList(aContext, *aRuleData->ValueForMaskOrigin(),
+                    svgReset->mLayers.mLayers,
+                    parentSVGReset->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mOrigin,
+                    uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
+                    parentSVGReset->mLayers.mOriginCount,
+                    svgReset->mLayers.mOriginCount, maxItemCount, rebuild,
+                    conditions);
+
+  // mask-position: enum, length, percent (flags), inherit [pair list]
+  nsStyleImageLayers::Position initialPosition;
+  initialPosition.SetInitialPercentValues(0.0f);
+  SetImageLayerList(aContext, *aRuleData->ValueForMaskPosition(),
+                    svgReset->mLayers.mLayers,
+                    parentSVGReset->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mPosition,
+                    initialPosition, parentSVGReset->mLayers.mPositionCount,
+                    svgReset->mLayers.mPositionCount, maxItemCount, rebuild,
+                    conditions);
+
+  // mask-size: enum, length, auto, inherit, initial [pair list]
+  nsStyleImageLayers::Size initialSize;
+  initialSize.SetInitialValues();
+  SetImageLayerPairList(aContext, *aRuleData->ValueForMaskSize(),
+                        svgReset->mLayers.mLayers,
+                        parentSVGReset->mLayers.mLayers,
+                        &nsStyleImageLayers::Layer::mSize,
+                        initialSize, parentSVGReset->mLayers.mSizeCount,
+                        svgReset->mLayers.mSizeCount, maxItemCount, rebuild,
+                        conditions);
+
+  // mask-mode: enum, inherit, initial [list]
+  SetImageLayerList(aContext, *aRuleData->ValueForMaskMode(),
+                    svgReset->mLayers.mLayers,
+                    parentSVGReset->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mMaskMode,
+                    uint8_t(NS_STYLE_MASK_MODE_AUTO),
+                    parentSVGReset->mLayers.mMaskModeCount,
+                    svgReset->mLayers.mMaskModeCount, maxItemCount, rebuild, conditions);
+
+  // mask-composite: enum, inherit, initial [list]
+  SetImageLayerList(aContext, *aRuleData->ValueForMaskComposite(),
+                    svgReset->mLayers.mLayers,
+                    parentSVGReset->mLayers.mLayers,
+                    &nsStyleImageLayers::Layer::mComposite,
+                    uint8_t(NS_STYLE_COMPOSITE_MODE_ADD),
+                    parentSVGReset->mLayers.mCompositeCount,
+                    svgReset->mLayers.mCompositeCount, maxItemCount, rebuild, conditions);
+
+  if (rebuild) {
+    // Delete any extra items.  We need to keep layers in which any
+    // property was specified.
+    svgReset->mLayers.mLayers.TruncateLength(maxItemCount);
+
+    uint32_t fillCount = svgReset->mLayers.mImageCount;
+
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mImage,
+                       svgReset->mLayers.mImageCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mRepeat,
+                       svgReset->mLayers.mRepeatCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mClip,
+                       svgReset->mLayers.mClipCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mOrigin,
+                       svgReset->mLayers.mOriginCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mPosition,
+                       svgReset->mLayers.mPositionCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mSize,
+                       svgReset->mLayers.mSizeCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mMaskMode,
+                       svgReset->mLayers.mMaskModeCount, fillCount);
+    FillBackgroundList(svgReset->mLayers.mLayers,
+                       &nsStyleImageLayers::Layer::mComposite,
+                       svgReset->mLayers.mCompositeCount, fillCount);
+  }
+
+  svgReset->mLayers.TrackImages(aContext->PresContext());
+
   COMPUTE_END_RESET(SVGReset, svgReset)
 }
 
 const void*
 nsRuleNode::ComputeVariablesData(void* aStartStruct,
                                  const nsRuleData* aRuleData,
                                  nsStyleContext* aContext,
                                  nsRuleNode* aHighestNode,