Bug 686281 - Implement CSS mask style; r=dbaron. draft
authorCJKu <cku@mozilla.com>
Mon, 21 Dec 2015 11:32:34 +0800
changeset 316592 b6f53848b47815741c753fcb836cd01d079487d2
parent 316591 929d307d10e459d402237b03bc2db6436592717d
child 316593 ce9d2ff7abb9a37660547cbdcc1145658151520a
push id8575
push usercku@mozilla.com
push dateMon, 21 Dec 2015 03:33:13 +0000
reviewersdbaron
bugs686281
milestone45.0a1
Bug 686281 - Implement CSS mask style; r=dbaron.
layout/generic/nsFrame.cpp
layout/inspector/inDOMUtils.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -726,16 +726,66 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
 nsresult
 nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const
 {
   aStart = 0;
   aEnd = 0;
   return NS_OK;
 }
 
+static
+void
+AssociateStyleImageWithFrame(nsFrame* aFrame,
+                             const nsStyleImageLayers* aOldLayers,
+                             const nsStyleImageLayers* aNewLayers)
+{
+   ImageLoader* imageLoader =
+     aFrame->PresContext()->Document()->StyleImageLoader();
+
+  // If the old context had a background image image, or mask image image,
+  // and new context does not have the same image, clear the image load
+  // notifier(which keeps the image loading, if it still is) for the frame.
+  // We want to do this conservatively because some frames paint their
+  // backgrounds from some other frame's style data, and we don't want
+  // to clear those notifiers unless we have to.  (They'll be reset
+  // when we paint, although we could miss a notification in that
+  // interval.)
+
+  if (aOldLayers) {
+    NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aOldLayers)) {
+      // If there is an image in oldBG that's not in newBG, drop it.
+      if (i >= aNewLayers->mImageCount ||
+          !aOldLayers->mLayers[i].mImage.ImageDataEquals(
+            aNewLayers->mLayers[i].mImage)) {
+        const nsStyleImage& oldImage = aOldLayers->mLayers[i].mImage;
+        if (oldImage.GetType() != eStyleImageType_Image) {
+          continue;
+        }
+
+        imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
+                                                   aFrame);
+      }
+    }
+  }
+
+  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aNewLayers)) {
+    // If there is an image in newBG that's not in oldBG, add it.
+    if (!aOldLayers || i >= aOldLayers->mImageCount ||
+        !aNewLayers->mLayers[i].mImage.ImageDataEquals(
+          aOldLayers->mLayers[i].mImage)) {
+      const nsStyleImage& newImage = aNewLayers->mLayers[i].mImage;
+      if (newImage.GetType() != eStyleImageType_Image) {
+        continue;
+      }
+
+      imageLoader->AssociateRequestToFrame(newImage.GetImageData(), aFrame);
+    }
+  }
+}
+
 // Subclass hook for style post processing
 /* virtual */ void
 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   if (IsSVGText()) {
     SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
         nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::svgTextFrame));
     nsIFrame* anonBlock = svgTextFrame->GetFirstPrincipalChild();
@@ -750,60 +800,28 @@ nsFrame::DidSetStyleContext(nsStyleConte
     // be happening under frame construction under a Reflow() call.)
     if (anonBlock && !(anonBlock->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
         (svgTextFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
         !(svgTextFrame->GetStateBits() & NS_STATE_SVG_TEXT_IN_REFLOW)) {
       svgTextFrame->ScheduleReflowSVGNonDisplayText();
     }
   }
 
-  ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
-
-  // If the old context had a background image image and new context
-  // does not have the same image, clear the image load notifier
-  // (which keeps the image loading, if it still is) for the frame.
-  // We want to do this conservatively because some frames paint their
-  // backgrounds from some other frame's style data, and we don't want
-  // to clear those notifiers unless we have to.  (They'll be reset
-  // when we paint, although we could miss a notification in that
-  // interval.)
-  const nsStyleBackground *oldBG = aOldStyleContext ?
-                                   aOldStyleContext->StyleBackground() :
-                                   nullptr;
-  const nsStyleBackground *newBG = StyleBackground();
-  const nsStyleImageLayers& oldBGLayers = oldBG->mLayers;
-  const nsStyleImageLayers& newBGLayers = newBG->mLayers;
-  if (oldBG) {
-    NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, oldBGLayers) {
-      // If there is an image in oldBG that's not in newBG, drop it.
-      if (i >= newBGLayers.mImageCount ||
-          !oldBGLayers.mLayers[i].mImage.ImageDataEquals(newBGLayers.mLayers[i].mImage)) {
-        const nsStyleImage& oldImage = oldBGLayers.mLayers[i].mImage;
-        if (oldImage.GetType() != eStyleImageType_Image) {
-          continue;
-        }
-
-        imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
-                                                  this);
-      }
-    }
-  }
-
-  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, newBGLayers) {
-    // If there is an image in newBG that's not in oldBG, add it.
-    if (!oldBG || i >= oldBGLayers.mImageCount ||
-        !newBGLayers.mLayers[i].mImage.ImageDataEquals(oldBGLayers.mLayers[i].mImage)) {
-      const nsStyleImage& newImage = newBGLayers.mLayers[i].mImage;
-      if (newImage.GetType() != eStyleImageType_Image) {
-        continue;
-      }
-
-      imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this);
-    }
-  }
+  // Setup relation between background style images and this frame.
+  const nsStyleImageLayers *oldLayers = aOldStyleContext ?
+                              &aOldStyleContext->StyleBackground()->mLayers :
+                              nullptr;
+  const nsStyleImageLayers *newLayers = &StyleBackground()->mLayers;
+  AssociateStyleImageWithFrame(this, oldLayers, newLayers);
+
+  // Setup relation between mask style images and this frame.
+  oldLayers = aOldStyleContext ? &aOldStyleContext->StyleSVGReset()->mLayers :
+                                  nullptr;
+  newLayers = &StyleSVGReset()->mLayers;
+  AssociateStyleImageWithFrame(this, oldLayers, newLayers);
 
   if (aOldStyleContext) {
     // If we detect a change on margin, padding or border, we store the old
     // values on the frame itself between now and reflow, so if someone
     // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
     // can give an accurate answer.
     // We don't want to set the property if one already exists.
     FrameProperties props = Properties();
@@ -831,16 +849,17 @@ nsFrame::DidSetStyleContext(nsStyleConte
       newValue = StyleBorder()->GetComputedBorder();
       if (oldValue != newValue &&
           !props.Get(UsedBorderProperty())) {
         props.Set(UsedBorderProperty(), new nsMargin(oldValue));
       }
     }
   }
 
+  ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
   imgIRequest *oldBorderImage = aOldStyleContext
     ? aOldStyleContext->StyleBorder()->GetBorderImageRequest()
     : nullptr;
   imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest();
   // FIXME (Bug 759996): The following is no longer true.
   // For border-images, we can't be as conservative (we need to set the
   // new loaders if there has been any change) since the CalcDifference
   // call depended on the result of GetComputedBorder() and that result
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -760,16 +760,18 @@ PropertySupportsVariant(nsCSSProperty aP
         break;
 
       case eCSSProperty_border_top_left_radius:
       case eCSSProperty_border_top_right_radius:
       case eCSSProperty_border_bottom_left_radius:
       case eCSSProperty_border_bottom_right_radius:
       case eCSSProperty_background_position:
       case eCSSProperty_background_size:
+      case eCSSProperty_mask_position:
+      case eCSSProperty_mask_size:
       case eCSSProperty_grid_auto_columns:
       case eCSSProperty_grid_auto_rows:
       case eCSSProperty_grid_template_columns:
       case eCSSProperty_grid_template_rows:
       case eCSSProperty_object_position:
       case eCSSProperty_scroll_snap_coordinate:
       case eCSSProperty_scroll_snap_destination:
       case eCSSProperty_transform_origin:
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -125,16 +125,17 @@ CSS_KEY(-moz-window, _moz_window)
 CSS_KEY(-moz-workspace, _moz_workspace)
 CSS_KEY(-moz-zoom-in, _moz_zoom_in)
 CSS_KEY(-moz-zoom-out, _moz_zoom_out)
 CSS_KEY(-webkit-box, _webkit_box)
 CSS_KEY(absolute, absolute)
 CSS_KEY(active, active)
 CSS_KEY(activeborder, activeborder)
 CSS_KEY(activecaption, activecaption)
+CSS_KEY(add, add)
 CSS_KEY(additive, additive)
 CSS_KEY(alias, alias)
 CSS_KEY(all, all)
 CSS_KEY(all-petite-caps, all_petite_caps)
 CSS_KEY(all-scroll, all_scroll)
 CSS_KEY(all-small-caps, all_small_caps)
 CSS_KEY(alpha, alpha)
 CSS_KEY(alternate, alternate)
@@ -248,16 +249,17 @@ CSS_KEY(elements, elements)
 CSS_KEY(ellipse, ellipse)
 CSS_KEY(ellipsis, ellipsis)
 CSS_KEY(em, em)
 CSS_KEY(embed, embed)
 CSS_KEY(enabled, enabled)
 CSS_KEY(end, end)
 CSS_KEY(ethiopic-numeric, ethiopic_numeric)
 CSS_KEY(ex, ex)
+CSS_KEY(exclude, exclude)
 CSS_KEY(exclusion, exclusion)
 CSS_KEY(expanded, expanded)
 CSS_KEY(extends, extends)
 CSS_KEY(extra-condensed, extra_condensed)
 CSS_KEY(extra-expanded, extra_expanded)
 CSS_KEY(ew-resize, ew_resize)
 CSS_KEY(fantasy, fantasy)
 CSS_KEY(farthest-side, farthest_side)
@@ -316,16 +318,17 @@ CSS_KEY(inline-end, inline_end)
 CSS_KEY(inline-flex, inline_flex)
 CSS_KEY(inline-grid, inline_grid)
 CSS_KEY(inline-start, inline_start)
 CSS_KEY(inline-table, inline_table)
 CSS_KEY(inset, inset)
 CSS_KEY(inside, inside)
 // CSS_KEY(inter-character, inter_character) // TODO see bug 1055672
 CSS_KEY(interpolatematrix, interpolatematrix)
+CSS_KEY(intersect, intersect)
 CSS_KEY(isolate, isolate)
 CSS_KEY(invert, invert)
 CSS_KEY(italic, italic)
 CSS_KEY(japanese-formal, japanese_formal)
 CSS_KEY(japanese-informal, japanese_informal)
 CSS_KEY(jis78, jis78)
 CSS_KEY(jis83, jis83)
 CSS_KEY(jis90, jis90)
@@ -536,16 +539,17 @@ CSS_KEY(stretched, stretched)
 CSS_KEY(strict, strict)
 CSS_KEY(stroke, stroke)
 CSS_KEY(stroke-box, stroke_box)
 CSS_KEY(style, style)
 CSS_KEY(styleset, styleset)
 CSS_KEY(stylistic, stylistic)
 CSS_KEY(sub, sub)
 CSS_KEY(subgrid, subgrid)
+CSS_KEY(substract, substract)
 CSS_KEY(super, super)
 CSS_KEY(sw-resize, sw_resize)
 CSS_KEY(swash, swash)
 CSS_KEY(table, table)
 CSS_KEY(table-caption, table_caption)
 CSS_KEY(table-cell, table_cell)
 CSS_KEY(table-column, table_column)
 CSS_KEY(table-column-group, table_column_group)
@@ -761,8 +765,9 @@ CSS_KEY(reset-size, reset_size)
 //CSS_KEY(start, start)
 CSS_KEY(srgb, srgb)
 CSS_KEY(symbolic, symbolic)
 CSS_KEY(symbols, symbols)
 CSS_KEY(text-after-edge, text_after_edge)
 CSS_KEY(text-before-edge, text_before_edge)
 CSS_KEY(use-script, use_script)
 CSS_KEY(-moz-crisp-edges, _moz_crisp_edges)
+
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -4044,28 +4044,116 @@ CSS_PROP_SVG(
     marker_start,
     MarkerStart,
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HUO,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
-CSS_PROP_SVGRESET(
+CSS_PROP_SHORTHAND(
     mask,
     mask,
     Mask,
-    CSS_PROPERTY_PARSE_VALUE |
-        CSS_PROPERTY_CREATES_STACKING_CONTEXT,
-    "",
-    VARIANT_HUO,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "")
+CSS_PROP_SVGRESET(
+    mask-clip,
+    mask_clip,
+    MaskClip,
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    "",
+    VARIANT_KEYWORD, // used by list parsing
+    kImageLayerOriginKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
+    mask-composite,
+    mask_composite,
+    MaskComposite,
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    "",
+    VARIANT_KEYWORD, // used by list parsing
+    kImageLayerCompositeKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
+    mask-image,
+    mask_image,
+    MaskImage,
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_CREATES_STACKING_CONTEXT |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+        CSS_PROPERTY_START_IMAGE_LOADS,
+    "",
+    VARIANT_IMAGE, // used by list parsing
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_SVGRESET(
+    mask-mode,
+    mask_mode,
+    MaskMode,
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    "",
+    VARIANT_KEYWORD, // used by list parsing
+    kImageLayerModeKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
+    mask-origin,
+    mask_origin,
+    MaskOrigin,
+    CSS_PROPERTY_PARSE_VALUE_LIST |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    "",
+    VARIANT_KEYWORD, // used by list parsing
+    kImageLayerOriginKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
+    mask-repeat,
+    mask_repeat,
+    MaskRepeat,
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    "",
+    VARIANT_KEYWORD, // used by list parsing
+    kImageLayerRepeatKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
+    mask-position,
+    mask_position,
+    MaskPosition,
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+        CSS_PROPERTY_STORES_CALC,
+    "",
+    0,
+    kImageLayerPositionKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
+    mask-size,
+    mask_size,
+    MaskSize,
+    CSS_PROPERTY_PARSE_FUNCTION |
+        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
+        CSS_PROPERTY_VALUE_NONNEGATIVE |
+        CSS_PROPERTY_STORES_CALC,
+    "",
+    0,
+    kImageLayerSizeKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+CSS_PROP_SVGRESET(
     mask-type,
     mask_type,
     MaskType,
     CSS_PROPERTY_PARSE_VALUE,
     "layout.css.masking.enabled",
     VARIANT_HK,
     kMaskTypeKTable,
     offsetof(nsStyleSVGReset, mMaskType),
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -908,16 +908,34 @@ const KTableEntry nsCSSProps::kImageLaye
 };
 
 const KTableEntry nsCSSProps::kImageLayerSizeKTable[] = {
   { eCSSKeyword_contain, NS_STYLE_IMAGELAYER_SIZE_CONTAIN },
   { eCSSKeyword_cover,   NS_STYLE_IMAGELAYER_SIZE_COVER },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
+const KTableEntry nsCSSProps::kImageLayerModeKTable[] = {
+  { eCSSKeyword_alpha, NS_STYLE_MASK_MODE_ALPHA },
+  { eCSSKeyword_luminance, NS_STYLE_MASK_MODE_LUMINANCE },
+  // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1224424
+  // It's ambigious at mask shorthand parsing while we have both mask-mode:auto
+  // and mask-size:auto.
+  { eCSSKeyword_auto, NS_STYLE_MASK_MODE_AUTO },
+  { eCSSKeyword_UNKNOWN, -1 }
+};
+
+const KTableEntry nsCSSProps::kImageLayerCompositeKTable[] = {
+  { eCSSKeyword_add, NS_STYLE_COMPOSITE_MODE_ADD },
+  { eCSSKeyword_substract, NS_STYLE_COMPOSITE_MODE_SUBSTRACT },
+  { eCSSKeyword_intersect, NS_STYLE_COMPOSITE_MODE_INTERSECT },
+  { eCSSKeyword_exclude, NS_STYLE_COMPOSITE_MODE_EXCLUDE },
+  { eCSSKeyword_UNKNOWN, -1 }
+};
+
 const KTableEntry nsCSSProps::kBlendModeKTable[] = {
     { eCSSKeyword_normal,      NS_STYLE_BLEND_NORMAL },
     { eCSSKeyword_multiply,    NS_STYLE_BLEND_MULTIPLY },
     { eCSSKeyword_screen,      NS_STYLE_BLEND_SCREEN },
     { eCSSKeyword_overlay,     NS_STYLE_BLEND_OVERLAY },
     { eCSSKeyword_darken,      NS_STYLE_BLEND_DARKEN },
     { eCSSKeyword_lighten,     NS_STYLE_BLEND_LIGHTEN },
     { eCSSKeyword_color_dodge, NS_STYLE_BLEND_COLOR_DODGE },
@@ -2818,16 +2836,31 @@ static const nsCSSProperty gMozTransform
 };
 
 static const nsCSSProperty gScrollSnapTypeSubpropTable[] = {
   eCSSProperty_scroll_snap_type_x,
   eCSSProperty_scroll_snap_type_y,
   eCSSProperty_UNKNOWN
 };
 
+static const nsCSSProperty gMaskSubpropTable[] = {
+  eCSSProperty_mask_image,
+  eCSSProperty_mask_repeat,
+  eCSSProperty_mask_position,
+  eCSSProperty_mask_clip,
+  eCSSProperty_mask_origin,
+  eCSSProperty_mask_size,
+  eCSSProperty_mask_composite,
+  eCSSProperty_mask_mode,
+  eCSSProperty_UNKNOWN
+};
+
+// FIXME: mask-border tables should be added when we implement
+// mask-border properties.
+
 const nsCSSProperty *const
 nsCSSProps::kSubpropertyTable[eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands] = {
 #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_
 // Need an extra level of macro nesting to force expansion of method_
 // params before they get pasted.
 #define NSCSSPROPS_INNER_MACRO(method_) g##method_##SubpropTable,
 #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \
   NSCSSPROPS_INNER_MACRO(method_)
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -640,16 +640,18 @@ public:
   static const KTableEntry kBackfaceVisibilityKTable[];
   static const KTableEntry kTransformStyleKTable[];
   static const KTableEntry kImageLayerAttachmentKTable[];
   static const KTableEntry kImageLayerOriginKTable[];
   static const KTableEntry kImageLayerPositionKTable[];
   static const KTableEntry kImageLayerRepeatKTable[];
   static const KTableEntry kImageLayerRepeatPartKTable[];
   static const KTableEntry kImageLayerSizeKTable[];
+  static const KTableEntry kImageLayerCompositeKTable[];
+  static const KTableEntry kImageLayerModeKTable[];
   static const KTableEntry kBlendModeKTable[];
   static const KTableEntry kBorderCollapseKTable[];
   static const KTableEntry kBorderColorKTable[];
   static const KTableEntry kBorderImageRepeatKTable[];
   static const KTableEntry kBorderImageSliceKTable[];
   static const KTableEntry kBorderStyleKTable[];
   static const KTableEntry kBorderWidthKTable[];
   static const KTableEntry kBoxAlignKTable[];
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -9641,19 +9641,24 @@ nsRuleNode::ComputeSVGResetData(void* aS
       }
       break;
     }
     default:
       NS_NOTREACHED("unexpected unit");
   }
 
   // mask: url, none, inherit
-  const nsCSSValue* maskValue = aRuleData->ValueForMask();
-  if (eCSSUnit_URL == maskValue->GetUnit()) {
-    svgReset->mMask = maskValue->GetURLValue();
+  const nsCSSValue* maskValue = aRuleData->ValueForMaskImage();
+  if (eCSSUnit_List == maskValue->GetUnit() ||
+      eCSSUnit_ListDep == maskValue->GetUnit()) {
+    const nsCSSValue& item = maskValue->GetListValue()->mValue;
+    if (eCSSUnit_URL == item.GetUnit() ||
+        eCSSUnit_Image == item.GetUnit()) {
+      svgReset->mMask = item.GetURLValue();
+    }
   } else if (eCSSUnit_None == maskValue->GetUnit() ||
              eCSSUnit_Initial == maskValue->GetUnit() ||
              eCSSUnit_Unset == maskValue->GetUnit()) {
     svgReset->mMask = nullptr;
   } else if (eCSSUnit_Inherit == maskValue->GetUnit()) {
     conditions.SetUncacheable();
     svgReset->mMask = parentSVGReset->mMask;
   }
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -295,16 +295,21 @@ enum class FillMode : uint32_t;
 #define NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X          0x01
 #define NS_STYLE_IMAGELAYER_REPEAT_REPEAT_Y          0x02
 #define NS_STYLE_IMAGELAYER_REPEAT_REPEAT            0x03
 
 // See nsStyleImageLayers
 #define NS_STYLE_IMAGELAYER_SIZE_CONTAIN             0
 #define NS_STYLE_IMAGELAYER_SIZE_COVER               1
 
+// Mask mode
+#define NS_STYLE_MASK_MODE_ALPHA                0
+#define NS_STYLE_MASK_MODE_LUMINANCE            1
+#define NS_STYLE_MASK_MODE_AUTO                 2
+
 // See nsStyleBackground
 #define NS_STYLE_BG_INLINE_POLICY_EACH_BOX      0
 #define NS_STYLE_BG_INLINE_POLICY_CONTINUOUS    1
 #define NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX  2
 
 // See nsStyleTable
 #define NS_STYLE_BORDER_COLLAPSE                0
 #define NS_STYLE_BORDER_SEPARATE                1
@@ -1138,16 +1143,22 @@ enum class FillMode : uint32_t;
 #define NS_STYLE_BLEND_SOFT_LIGHT                   9
 #define NS_STYLE_BLEND_DIFFERENCE                   10
 #define NS_STYLE_BLEND_EXCLUSION                    11
 #define NS_STYLE_BLEND_HUE                          12
 #define NS_STYLE_BLEND_SATURATION                   13
 #define NS_STYLE_BLEND_COLOR                        14
 #define NS_STYLE_BLEND_LUMINOSITY                   15
 
+// composite
+#define NS_STYLE_COMPOSITE_MODE_ADD                 0
+#define NS_STYLE_COMPOSITE_MODE_SUBSTRACT           1
+#define NS_STYLE_COMPOSITE_MODE_INTERSECT           2
+#define NS_STYLE_COMPOSITE_MODE_EXCLUDE             3
+
 // See nsStyleText::mControlCharacterVisibility
 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN  0
 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE 1
 
 // counter system
 #define NS_STYLE_COUNTER_SYSTEM_CYCLIC      0
 #define NS_STYLE_COUNTER_SYSTEM_NUMERIC     1
 #define NS_STYLE_COUNTER_SYSTEM_ALPHABETIC  2
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1271,49 +1271,58 @@ nsStyleFilter::SetDropShadow(nsCSSShadow
   mType = NS_STYLE_FILTER_DROP_SHADOW;
 }
 
 // --------------------
 // nsStyleSVGReset
 //
 nsStyleSVGReset::nsStyleSVGReset()
 {
-    MOZ_COUNT_CTOR(nsStyleSVGReset);
-    mStopColor               = NS_RGB(0,0,0);
-    mFloodColor              = NS_RGB(0,0,0);
-    mLightingColor           = NS_RGB(255,255,255);
-    mMask                    = nullptr;
-    mStopOpacity             = 1.0f;
-    mFloodOpacity            = 1.0f;
-    mDominantBaseline        = NS_STYLE_DOMINANT_BASELINE_AUTO;
-    mVectorEffect            = NS_STYLE_VECTOR_EFFECT_NONE;
-    mMaskType                = NS_STYLE_MASK_TYPE_LUMINANCE;
+  MOZ_COUNT_CTOR(nsStyleSVGReset);
+  mStopColor               = NS_RGB(0,0,0);
+  mFloodColor              = NS_RGB(0,0,0);
+  mLightingColor           = NS_RGB(255,255,255);
+  mMask                    = nullptr;
+  mStopOpacity             = 1.0f;
+  mFloodOpacity            = 1.0f;
+  mDominantBaseline        = NS_STYLE_DOMINANT_BASELINE_AUTO;
+  mVectorEffect            = NS_STYLE_VECTOR_EFFECT_NONE;
+  mMaskType                = NS_STYLE_MASK_TYPE_LUMINANCE;
 }
 
 nsStyleSVGReset::~nsStyleSVGReset()
 {
   MOZ_COUNT_DTOR(nsStyleSVGReset);
 }
 
 nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
+  : mLayers(aSource.mLayers)
 {
   MOZ_COUNT_CTOR(nsStyleSVGReset);
   mStopColor = aSource.mStopColor;
   mFloodColor = aSource.mFloodColor;
   mLightingColor = aSource.mLightingColor;
   mClipPath = aSource.mClipPath;
   mFilters = aSource.mFilters;
   mMask = aSource.mMask;
   mStopOpacity = aSource.mStopOpacity;
   mFloodOpacity = aSource.mFloodOpacity;
   mDominantBaseline = aSource.mDominantBaseline;
   mVectorEffect = aSource.mVectorEffect;
   mMaskType = aSource.mMaskType;
 }
 
+void nsStyleSVGReset::Destroy(nsPresContext* aContext) {
+  mLayers.UntrackImages(aContext);
+
+  this->~nsStyleSVGReset();
+  aContext->PresShell()->
+    FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this);
+}
+
 nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (mClipPath != aOther.mClipPath ||
       !EqualURIs(mMask, aOther.mMask) ||
       mFilters != aOther.mFilters) {
     NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
@@ -1341,16 +1350,18 @@ nsChangeHint nsStyleSVGReset::CalcDiffer
              mFloodColor    != aOther.mFloodColor    ||
              mLightingColor != aOther.mLightingColor ||
              mStopOpacity   != aOther.mStopOpacity   ||
              mFloodOpacity  != aOther.mFloodOpacity  ||
              mMaskType      != aOther.mMaskType) {
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   }
 
+  mLayers.CalcDifference(aOther.mLayers, hint);
+
   return hint;
 }
 
 // nsStyleSVGPaint implementation
 nsStyleSVGPaint::~nsStyleSVGPaint()
 {
   if (mType == eStyleSVGPaintType_Server) {
     NS_IF_RELEASE(mPaint.mPaintServer);
@@ -1362,17 +1373,17 @@ nsStyleSVGPaint::SetType(nsStyleSVGPaint
 {
   if (mType == eStyleSVGPaintType_Server) {
     this->~nsStyleSVGPaint();
     new (this) nsStyleSVGPaint();
   }
   mType = aType;
 }
 
-nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) 
+nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther)
 {
   if (this == &aOther)
     return *this;
 
   SetType(aOther.mType);
 
   mFallbackColor = aOther.mFallbackColor;
   if (mType == eStyleSVGPaintType_Server) {
@@ -2272,59 +2283,166 @@ nsStyleImage::operator==(const nsStyleIm
 
   return true;
 }
 
 // --------------------
 // nsStyleImageLayers
 //
 
+const nsCSSProperty nsStyleImageLayers::kBackgroundLayerTable[] = {
+  eCSSProperty_background,                // shorthand
+  eCSSProperty_background_color,          // color
+  eCSSProperty_background_image,          // image
+  eCSSProperty_background_repeat,         // repeat
+  eCSSProperty_background_position,       // position
+  eCSSProperty_background_clip,           // clip
+  eCSSProperty_background_origin,         // origin
+  eCSSProperty_background_size,           // size
+  eCSSProperty_background_attachment,     // attachment
+  eCSSProperty_UNKNOWN,                   // maskMode
+  eCSSProperty_UNKNOWN                    // composite
+};
+
+const nsCSSProperty nsStyleImageLayers::kMaskLayerTable[] = {
+  eCSSProperty_mask,                      // shorthand
+  eCSSProperty_UNKNOWN,                   // color
+  eCSSProperty_mask_image,                // image
+  eCSSProperty_mask_repeat,               // repeat
+  eCSSProperty_mask_position,             // position
+  eCSSProperty_mask_clip,                 // clip
+  eCSSProperty_mask_origin,               // origin
+  eCSSProperty_mask_size,                 // size
+  eCSSProperty_UNKNOWN,                   // attachment
+  eCSSProperty_mask_mode,                 // maskMode
+  eCSSProperty_mask_composite             // composite
+};
+
 nsStyleImageLayers::nsStyleImageLayers()
   : mAttachmentCount(1)
   , mClipCount(1)
   , mOriginCount(1)
   , mRepeatCount(1)
   , mPositionCount(1)
   , mImageCount(1)
   , mSizeCount(1)
+  , mMaskModeCount(1)
   , mBlendModeCount(1)
+  , mCompositeCount(1)
 {
   MOZ_COUNT_CTOR(nsStyleImageLayers);
   mLayers.AppendElement();
   NS_ASSERTION(mLayers.Length() == 1, "auto array must have room for 1 element");
 }
 
 nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource)
   : mAttachmentCount(aSource.mAttachmentCount)
   , mClipCount(aSource.mClipCount)
   , mOriginCount(aSource.mOriginCount)
   , mRepeatCount(aSource.mRepeatCount)
   , mPositionCount(aSource.mPositionCount)
   , mImageCount(aSource.mImageCount)
   , mSizeCount(aSource.mSizeCount)
+  , mMaskModeCount(aSource.mMaskModeCount)
   , mBlendModeCount(aSource.mBlendModeCount)
+  , mCompositeCount(aSource.mCompositeCount)
   , mLayers(aSource.mLayers) // deep copy
 {
   MOZ_COUNT_CTOR(nsStyleImageLayers);
   // If the deep copy of mLayers failed, truncate the counts.
   uint32_t count = mLayers.Length();
   if (count != aSource.mLayers.Length()) {
     NS_WARNING("truncating counts due to out-of-memory");
     mAttachmentCount = std::max(mAttachmentCount, count);
     mClipCount = std::max(mClipCount, count);
     mOriginCount = std::max(mOriginCount, count);
     mRepeatCount = std::max(mRepeatCount, count);
     mPositionCount = std::max(mPositionCount, count);
     mImageCount = std::max(mImageCount, count);
     mSizeCount = std::max(mSizeCount, count);
+    mMaskModeCount = std::max(mMaskModeCount, count);
     mBlendModeCount = std::max(mBlendModeCount, count);
+    mCompositeCount = std::max(mCompositeCount, count);
   }
 }
 
 void
+nsStyleImageLayers::CalcDifference(const nsStyleImageLayers& aOther,
+                                   nsChangeHint& aHint) const
+{
+  const nsStyleImageLayers& moreLayers =
+    mImageCount > aOther.mImageCount ?
+      *this : aOther;
+  const nsStyleImageLayers& lessLayers =
+    mImageCount > aOther.mImageCount ?
+      aOther : *this;
+
+  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, moreLayers) {
+    if (i < lessLayers.mImageCount) {
+      nsChangeHint layerDifference =
+        moreLayers.mLayers[i].CalcDifference(lessLayers.mLayers[i]);
+      aHint |= layerDifference;
+      if (layerDifference &&
+          ((moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
+           (lessLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element))) {
+        aHint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
+      }
+    } else {
+      aHint |= nsChangeHint_RepaintFrame;
+      if (moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) {
+        aHint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
+      }
+    }
+  }
+
+  if (aHint) {
+    return;
+  }
+
+  if (mBlendModeCount != aOther.mBlendModeCount ||
+      mClipCount != aOther.mClipCount ||
+      mCompositeCount != aOther.mCompositeCount ||
+      mMaskModeCount != aOther.mMaskModeCount ||
+      mOriginCount != aOther.mOriginCount ||
+      mRepeatCount != aOther.mRepeatCount ||
+      mPositionCount != aOther.mPositionCount ||
+      mSizeCount != aOther.mSizeCount) {
+    NS_UpdateHint(aHint, nsChangeHint_NeutralChange);
+  }
+}
+
+bool
+nsStyleImageLayers::HasValidLayers() const
+{
+  for (uint32_t i = 0; i < mImageCount; i++) {
+    if (!mLayers[i].mImage.IsEmpty()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/* static */
+bool
+nsStyleImageLayers::Position::IsInitialValue(const Position& aPosition)
+{
+  if (aPosition.mXPosition.mPercent == 0.0 &&
+      aPosition.mXPosition.mLength == 0&&
+      aPosition.mXPosition.mHasPercent &&
+      aPosition.mYPosition.mPercent == 0.0 &&
+      aPosition.mYPosition.mLength == 0 &&
+      aPosition.mYPosition.mHasPercent) {
+    return true;
+  }
+
+  return false;
+}
+
+void
 nsStyleImageLayers::Position::SetInitialPercentValues(float aPercentVal)
 {
   mXPosition.mPercent = aPercentVal;
   mXPosition.mLength = 0;
   mXPosition.mHasPercent = true;
   mYPosition.mPercent = aPercentVal;
   mYPosition.mLength = 0;
   mYPosition.mHasPercent = true;
@@ -2439,28 +2557,37 @@ nsStyleImageLayers::Size::operator==(con
              "bad mHeightType for aOther");
 
   return mWidthType == aOther.mWidthType &&
          mHeightType == aOther.mHeightType &&
          (mWidthType != eLengthPercentage || mWidth == aOther.mWidth) &&
          (mHeightType != eLengthPercentage || mHeight == aOther.mHeight);
 }
 
+bool
+nsStyleImageLayers::Repeat::IsInitialValue(const Repeat& aRepeat)
+{
+  return (aRepeat.mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT) &&
+         (aRepeat.mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT);
+}
+
 void
 nsStyleImageLayers::Repeat::SetInitialValues()
 {
   mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT;
   mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT;
 }
 
 nsStyleImageLayers::Layer::Layer()
 : mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER),
   mOrigin(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
   mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL),
-  mBlendMode(NS_STYLE_BLEND_NORMAL)
+  mBlendMode(NS_STYLE_BLEND_NORMAL),
+  mComposite(NS_STYLE_COMPOSITE_MODE_ADD),
+  mMaskMode(NS_STYLE_MASK_MODE_AUTO)
 {
   mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%"
   mImage.SetNull();
   mRepeat.SetInitialValues();
   mSize.SetInitialValues();
 }
 
 nsStyleImageLayers::Layer::~Layer()
@@ -2484,30 +2611,34 @@ nsStyleImageLayers::Layer::operator==(co
 {
   return mAttachment == aOther.mAttachment &&
          mClip == aOther.mClip &&
          mOrigin == aOther.mOrigin &&
          mRepeat == aOther.mRepeat &&
          mBlendMode == aOther.mBlendMode &&
          mPosition == aOther.mPosition &&
          mSize == aOther.mSize &&
-         mImage == aOther.mImage;
+         mImage == aOther.mImage &&
+         mMaskMode == aOther.mMaskMode &&
+         mComposite == aOther.mComposite;
 }
 
 nsChangeHint
 nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
   if (mAttachment != aOther.mAttachment ||
       mClip != aOther.mClip ||
       mOrigin != aOther.mOrigin ||
       mRepeat != aOther.mRepeat ||
       mBlendMode != aOther.mBlendMode ||
       mSize != aOther.mSize ||
-      mImage != aOther.mImage) {
+      mImage != aOther.mImage ||
+      mMaskMode != aOther.mMaskMode ||
+      mComposite != aOther.mComposite) {
     hint |= nsChangeHint_RepaintFrame;
   }
   if (mPosition != aOther.mPosition) {
     hint |= nsChangeHint_SchedulePaint;
   }
   return hint;
 }
 
@@ -2542,61 +2673,23 @@ nsStyleBackground::Destroy(nsPresContext
 
   this->~nsStyleBackground();
   aContext->PresShell()->
     FreeByObjectID(eArenaObjectID_nsStyleBackground, this);
 }
 
 nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const
 {
-  const nsStyleImageLayers& moreLayers =
-    mLayers.mImageCount > aOther.mLayers.mImageCount ?
-      this->mLayers : aOther.mLayers;
-  const nsStyleImageLayers& lessLayers =
-    mLayers.mImageCount > aOther.mLayers.mImageCount ?
-      aOther.mLayers : this->mLayers;
-
   nsChangeHint hint = nsChangeHint(0);
-
-  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, moreLayers) {
-    if (i < lessLayers.mImageCount) {
-      nsChangeHint layerDifference = moreLayers.mLayers[i].
-                                       CalcDifference(lessLayers.mLayers[i]);
-      hint |= layerDifference;
-      if (layerDifference &&
-          ((moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
-           (lessLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element))) {
-        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
-      }
-    } else {
-      hint |= nsChangeHint_RepaintFrame;
-      if (moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) {
-        hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
-      }
-    }
-  }
-
   if (mBackgroundColor != aOther.mBackgroundColor) {
     hint |= nsChangeHint_RepaintFrame;
   }
-
-  if (hint) {
-    return hint;
-  }
-
-  if (mLayers.mAttachmentCount != aOther.mLayers.mAttachmentCount ||
-      mLayers.mClipCount != aOther.mLayers.mClipCount ||
-      mLayers.mOriginCount != aOther.mLayers.mOriginCount ||
-      mLayers.mRepeatCount != aOther.mLayers.mRepeatCount ||
-      mLayers.mPositionCount != aOther.mLayers.mPositionCount ||
-      mLayers.mSizeCount != aOther.mLayers.mSizeCount) {
-    return nsChangeHint_NeutralChange;
-  }
-
-  return NS_STYLE_HINT_NONE;
+  mLayers.CalcDifference(aOther.mLayers, hint);
+
+  return hint;
 }
 
 bool nsStyleBackground::HasFixedBackground() const
 {
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mLayers) {
     const nsStyleImageLayers::Layer &layer = mLayers.mLayers[i];
     if (layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED &&
         !layer.mImage.IsEmpty()) {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -395,25 +395,41 @@ struct nsStyleColor {
 
 struct nsStyleImageLayers {
   nsStyleImageLayers();
   nsStyleImageLayers(const nsStyleImageLayers &aSource);
   ~nsStyleImageLayers() {
     MOZ_COUNT_DTOR(nsStyleImageLayers);
   }
 
+  enum {
+    shorthand = 0,
+    color,
+    image,
+    repeat,
+    position,
+    clip,
+    origin,
+    size,
+    attachment,
+    maskMode,
+    composite
+  };
+
   struct Position;
   friend struct Position;
   struct Position {
     typedef nsStyleCoord::CalcValue PositionCoord;
     PositionCoord mXPosition, mYPosition;
 
     // Initialize nothing
     Position() {}
 
+    static bool IsInitialValue(const Position& aPosition);
+
     // Sets both mXPosition and mYPosition to the given percent value for the
     // initial property-value (e.g. 0.0f for "0% 0%", or 0.5f for "50% 50%")
     void SetInitialPercentValues(float aPercentVal);
 
     // Sets both mXPosition and mYPosition to 0 (app units) for the
     // initial property-value as a length with no percentage component.
     void SetInitialZeroValues();
 
@@ -440,16 +456,20 @@ struct nsStyleImageLayers {
         double d = double(mPercent) * double(aAvailable) + double(mLength);
         if (d < 0.0)
           return 0;
         return NSToCoordRoundWithClamp(float(d));
       }
     };
     Dimension mWidth, mHeight;
 
+    static bool IsInitialValue(const Size& aSize) {
+      return (aSize.mWidthType == eAuto && aSize.mHeightType == eAuto);
+    }
+
     nscoord ResolveWidthLengthPercentage(const nsSize& aBgPositioningArea) const {
       MOZ_ASSERT(mWidthType == eLengthPercentage,
                  "resolving non-length/percent dimension!");
       return mWidth.ResolveLengthPercentage(aBgPositioningArea.width);
     }
 
     nscoord ResolveHeightLengthPercentage(const nsSize& aBgPositioningArea) const {
       MOZ_ASSERT(mHeightType == eLengthPercentage,
@@ -493,16 +513,18 @@ struct nsStyleImageLayers {
   struct Repeat;
   friend struct Repeat;
   struct Repeat {
     uint8_t mXRepeat, mYRepeat;
 
     // Initialize nothing
     Repeat() {}
 
+    static bool IsInitialValue(const Repeat& aRepeat);
+
     // Initialize to initial values
     void SetInitialValues();
 
     bool operator==(const Repeat& aOther) const {
       return mXRepeat == aOther.mXRepeat &&
              mYRepeat == aOther.mYRepeat;
     }
     bool operator!=(const Repeat& aOther) const {
@@ -518,23 +540,35 @@ struct nsStyleImageLayers {
     Size          mSize;          // [reset]
     uint8_t       mClip;          // [reset] See nsStyleConsts.h
     uint8_t       mOrigin;        // [reset] See nsStyleConsts.h
     uint8_t       mAttachment;    // [reset] See nsStyleConsts.h
                                   // background-only property
                                   // This property is used for background layer
                                   // only. For a mask layer, it should always
                                   // be the initial value, which is
-                                  // NS_STYLE_BG_ATTACHMENT_SCROLL.
+                                  // NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL.
     uint8_t       mBlendMode;     // [reset] See nsStyleConsts.h
                                   // background-only property
                                   // This property is used for background layer
                                   // only. For a mask layer, it should always
                                   // be the initial value, which is
                                   // NS_STYLE_BLEND_NORMAL.
+    uint8_t       mComposite;     // [reset] See nsStyleConsts.h
+                                  // mask-only property
+                                  // This property is used for mask layer only.
+                                  // For a background layer, it should always
+                                  // be the initial value, which is
+                                  // NS_STYLE_COMPOSITE_MODE_ADD.
+    uint8_t       mMaskMode;      // [reset] See nsStyleConsts.h
+                                  // mask-only property
+                                  // This property is used for mask layer only.
+                                  // For a background layer, it should always
+                                  // be the initial value, which is
+                                  // NS_STYLE_MASK_MODE_AUTO.
     Repeat        mRepeat;        // [reset] See nsStyleConsts.h
 
     // Initializes only mImage
     Layer();
     ~Layer();
 
     // Register/unregister images with the document. We do this only
     // after the dust has settled in ComputeBackgroundData.
@@ -569,17 +603,19 @@ struct nsStyleImageLayers {
   // the lengths of the lists are independent.
   uint32_t mAttachmentCount,
            mClipCount,
            mOriginCount,
            mRepeatCount,
            mPositionCount,
            mImageCount,
            mSizeCount,
-           mBlendModeCount;
+           mMaskModeCount,
+           mBlendModeCount,
+           mCompositeCount;
 
   // Layers are stored in an array, matching the top-to-bottom order in
   // which they are specified in CSS.  The number of layers to be used
   // should come from the background-image property.  We create
   // additional |Layer| objects for *any* property, not just
   // background-image.  This means that the bottommost layer that
   // callers in layout care about (which is also the one whose
   // background-clip applies to the background-color) may not be last
@@ -594,24 +630,34 @@ struct nsStyleImageLayers {
     for (uint32_t i = 0; i < mImageCount; ++i) {
         mLayers[i].TrackImages(aContext);
     }
   }
   void UntrackImages(nsPresContext* aContext) {
     for (uint32_t i = 0; i < mImageCount; ++i)
       mLayers[i].UntrackImages(aContext);
   }
+
+  void CalcDifference(const nsStyleImageLayers& aOther, nsChangeHint& aHint) const;
+
+  bool HasValidLayers() const;
+
+  static const nsCSSProperty kBackgroundLayerTable[];
+  static const nsCSSProperty kMaskLayerTable[];
+
   #define NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(var_, layers_) \
     for (uint32_t var_ = (layers_).mImageCount; var_-- != 0; )
   #define NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(var_, layers_, start_, count_) \
     NS_ASSERTION((int32_t)(start_) >= 0 && (uint32_t)(start_) < (layers_).mImageCount, "Invalid layer start!"); \
     NS_ASSERTION((count_) > 0 && (count_) <= (start_) + 1, "Invalid layer range!"); \
     for (uint32_t var_ = (start_) + 1; var_-- != (uint32_t)((start_) + 1 - (count_)); )
 };
 
+
+
 struct nsStyleBackground {
   nsStyleBackground();
   nsStyleBackground(const nsStyleBackground& aOther);
   ~nsStyleBackground();
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBackground, sz);
@@ -3328,26 +3374,24 @@ struct nsStyleSVGReset {
   nsStyleSVGReset();
   nsStyleSVGReset(const nsStyleSVGReset& aSource);
   ~nsStyleSVGReset();
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, sz);
   }
-  void Destroy(nsPresContext* aContext) {
-    this->~nsStyleSVGReset();
-    aContext->PresShell()->
-      FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this);
-  }
+  void Destroy(nsPresContext* aContext);
 
   nsChangeHint CalcDifference(const nsStyleSVGReset& aOther) const;
   static nsChangeHint MaxDifference() {
-    return NS_CombineHint(nsChangeHint_UpdateEffects,
-            NS_CombineHint(nsChangeHint_UpdateOverflow, NS_STYLE_HINT_REFLOW));
+    return nsChangeHint_UpdateEffects |
+           nsChangeHint_UpdateOverflow |
+           nsChangeHint_NeutralChange |
+           NS_STYLE_HINT_REFLOW;
   }
   static nsChangeHint DifferenceAlwaysHandledForDescendants() {
     // CalcDifference never returns the reflow hints that are sometimes
     // handled for descendants as hints not handled for descendants.
     return nsChangeHint_NeedReflow |
            nsChangeHint_ReflowChangesSizeOrPosition |
            nsChangeHint_ClearAncestorIntrinsics;
   }
@@ -3355,16 +3399,17 @@ struct nsStyleSVGReset {
   bool HasFilters() const {
     return !mFilters.IsEmpty();
   }
 
   bool HasNonScalingStroke() const {
     return mVectorEffect == NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE;
   }
 
+  nsStyleImageLayers    mLayers;
   nsStyleClipPath mClipPath;          // [reset]
   nsTArray<nsStyleFilter> mFilters;   // [reset]
   nsCOMPtr<nsIURI> mMask;             // [reset]
   nscolor          mStopColor;        // [reset]
   nscolor          mFloodColor;       // [reset]
   nscolor          mLightingColor;    // [reset]
 
   float            mStopOpacity;      // [reset]