Bug 1072894 - Implement polygon() parsing for clip-path. r=heycam
authorDirk Schulze <krit>
Sun, 28 Sep 2014 01:56:00 +0200
changeset 207640 99b68a13246b91d61c81dde4c7685e8bd19f6dab
parent 207639 22b5ded1d700e862aab8c66013658575356bd1f0
child 207641 47e245533187c543c7c47beff52ad9e5afab01ac
push id27564
push userryanvm@gmail.com
push dateMon, 29 Sep 2014 18:57:04 +0000
treeherdermozilla-central@ce9a0b34225e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1072894
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1072894 - Implement polygon() parsing for clip-path. r=heycam
dom/locales/en-US/chrome/layout/css.properties
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsRuleNode.h
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/svg/nsSVGEffects.cpp
layout/svg/nsSVGIntegrationUtils.cpp
modules/libpref/init/all.js
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -51,16 +51,17 @@ PECounterStyleBadName=Name of @counter-s
 PECounterStyleBadBlockStart=Expected '{' to begin @counter-style rule but found '%1$S'.
 PECounterStyleEOF=closing '}' of @counter-style block
 PECounterDescExpected=Expected counter descriptor but found '%1$S'.
 PEUnknownCounterDesc=Unknown descriptor '%1$S' in @counter-style rule.
 PECounterExtendsNotIdent=Expected identifier for extends system but found '%1$S'.
 PECounterASWeight=Each weight in the additive-symbols descriptor must be smaller than the previous weight.
 PEClassSelEOF=class name
 PEClassSelNotIdent=Expected identifier for class selector but found '%1$S'.
+PECoordinatePair=Expected coordinate pair but found '%1$S'.
 PETypeSelEOF=element type
 PETypeSelNotType=Expected element name or '*' but found '%1$S'.
 PEUnknownNamespacePrefix=Unknown namespace prefix '%1$S'.
 PEAttributeNameEOF=attribute name
 PEAttributeNameExpected=Expected identifier for attribute name but found '%1$S'.
 PEAttributeNameOrNamespaceExpected=Expected attribute name or namespace but found '%1$S'.
 PEAttSelNoBar=Expected '|' but found '%1$S'.
 PEAttSelInnerEOF=part of attribute selector
@@ -101,16 +102,17 @@ PEColorBadRGBContents=Expected number or
 PEColorComponentBadTerm=Expected '%2$S' but found '%1$S'.
 PEColorHueEOF=hue
 PEExpectedComma=Expected ',' but found '%1$S'.
 PEColorSaturationEOF=saturation
 PEColorLightnessEOF=lightness
 PEColorOpacityEOF=opacity in color value
 PEExpectedNumber=Expected a number but found '%1$S'.
 PEExpectedCloseParen=Expected ')' but found '%1$S'.
+PEClipPathEOF=<basic-shape> or reference box
 PEDeclEndEOF=';' or '}' to end declaration
 PEParseDeclarationNoColon=Expected ':' but found '%1$S'.
 PEParseDeclarationDeclExpected=Expected declaration but found '%1$S'.
 PEEndOfDeclEOF=end of declaration
 PEImportantEOF=important
 PEExpectedImportant=Expected 'important' but found '%1$S'.
 PEBadDeclEnd=Expected ';' to terminate declaration but found '%1$S'.
 PEBadDeclOrRuleEnd2=Expected ';' or '}' to terminate declaration but found '%1$S'.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -603,16 +603,32 @@ nsLayoutUtils::CSSFiltersEnabled()
                                  "layout.css.filters.enabled",
                                  false);
   }
 
   return sCSSFiltersEnabled;
 }
 
 bool
+nsLayoutUtils::CSSClipPathShapesEnabled()
+{
+  static bool sCSSClipPathShapesEnabled;
+  static bool sCSSClipPathShapesPrefCached = false;
+
+  if (!sCSSClipPathShapesPrefCached) {
+   sCSSClipPathShapesPrefCached = true;
+   Preferences::AddBoolVarCache(&sCSSClipPathShapesEnabled,
+                                "layout.css.clip-path-shapes.enabled",
+                                false);
+  }
+
+  return sCSSClipPathShapesEnabled;
+}
+
+bool
 nsLayoutUtils::UnsetValueEnabled()
 {
   static bool sUnsetValueEnabled;
   static bool sUnsetValuePrefCached = false;
 
   if (!sUnsetValuePrefCached) {
     sUnsetValuePrefCached = true;
     Preferences::AddBoolVarCache(&sUnsetValueEnabled,
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1925,16 +1925,21 @@ public:
   static bool AnimatedImageLayersEnabled();
 
   /**
    * Checks if we should enable parsing for CSS Filters.
    */
   static bool CSSFiltersEnabled();
 
   /**
+   * Checks if we should enable parsing for CSS clip-path basic shapes.
+   */
+  static bool CSSClipPathShapesEnabled();
+
+  /**
    * Checks whether support for the CSS-wide "unset" value is enabled.
    */
   static bool UnsetValueEnabled();
 
   /**
    * Checks whether support for the CSS text-align (and -moz-text-align-last)
    * 'true' value is enabled.
    */
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -252,16 +252,17 @@ 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)
 CSS_KEY(farthest-corner, farthest_corner)
 CSS_KEY(fill, fill)
+CSS_KEY(fill-box, fill_box)
 CSS_KEY(fixed, fixed)
 CSS_KEY(flat, flat)
 CSS_KEY(flex, flex)
 CSS_KEY(flex-end, flex_end)
 CSS_KEY(flex-start, flex_start)
 CSS_KEY(flip, flip)
 CSS_KEY(forwards, forwards)
 CSS_KEY(fraktur, fraktur)
@@ -399,16 +400,17 @@ CSS_KEY(painted, painted)
 CSS_KEY(pan-x, pan_x)
 CSS_KEY(pan-y, pan_y)
 CSS_KEY(paused, paused)
 CSS_KEY(pc, pc)
 CSS_KEY(perspective, perspective)
 CSS_KEY(petite-caps, petite_caps)
 CSS_KEY(physical, physical)
 CSS_KEY(pointer, pointer)
+CSS_KEY(polygon, polygon)
 CSS_KEY(portrait, portrait)
 CSS_KEY(pre, pre)
 CSS_KEY(pre-wrap, pre_wrap)
 CSS_KEY(pre-line, pre_line)
 CSS_KEY(preserve-3d, preserve_3d)
 CSS_KEY(progress, progress)
 CSS_KEY(progressive, progressive)
 CSS_KEY(proportional-nums, proportional_nums)
@@ -498,16 +500,17 @@ CSS_KEY(static, static)
 CSS_KEY(status-bar, status_bar)
 CSS_KEY(step-end, step_end)
 CSS_KEY(step-start, step_start)
 CSS_KEY(sticky, sticky)
 CSS_KEY(stretch, stretch)
 CSS_KEY(stretch-to-fit, stretch_to_fit)
 CSS_KEY(stretched, stretched)
 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(super, super)
 CSS_KEY(sw-resize, sw_resize)
 CSS_KEY(swash, swash)
@@ -553,16 +556,17 @@ CSS_KEY(underline, underline)
 CSS_KEY(unicase, unicase)
 CSS_KEY(unset, unset)
 CSS_KEY(uppercase, uppercase)
 CSS_KEY(upright, upright)
 CSS_KEY(vertical, vertical)
 CSS_KEY(vertical-lr, vertical_lr)
 CSS_KEY(vertical-rl, vertical_rl)
 CSS_KEY(vertical-text, vertical_text)
+CSS_KEY(view-box, view_box)
 CSS_KEY(visible, visible)
 CSS_KEY(visiblefill, visiblefill)
 CSS_KEY(visiblepainted, visiblepainted)
 CSS_KEY(visiblestroke, visiblestroke)
 CSS_KEY(w-resize, w_resize)
 CSS_KEY(wait, wait)
 CSS_KEY(wavy, wavy)
 CSS_KEY(weight, weight)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -783,16 +783,17 @@ protected:
   bool ParseFontFeatureSettings(nsCSSValue& aValue);
   bool ParseFontSrc(nsCSSValue& aValue);
   bool ParseFontSrcFormat(InfallibleTArray<nsCSSValue>& values);
   bool ParseFontRanges(nsCSSValue& aValue);
   bool ParseListStyle();
   bool ParseListStyleType(nsCSSValue& aValue);
   bool ParseMargin();
   bool ParseMarks(nsCSSValue& aValue);
+  bool ParseClipPath();
   bool ParseTransform(bool aIsPrefixed);
   bool ParseObjectPosition();
   bool ParseOutline();
   bool ParseOverflow();
   bool ParsePadding();
   bool ParseQuotes();
   bool ParseSize();
   bool ParseTextAlign(nsCSSValue& aValue,
@@ -956,16 +957,20 @@ protected:
 
   void SetParsingCompoundProperty(bool aBool) {
     mParsingCompoundProperty = aBool;
   }
   bool IsParsingCompoundProperty(void) const {
     return mParsingCompoundProperty;
   }
 
+  /* Functions for basic shapes */
+  bool ParseBasicShape(nsCSSValue& aValue);
+  bool ParsePolygonFunction(nsCSSValue& aValue);
+
   /* Functions for transform Parsing */
   bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue);
   bool ParseFunction(nsCSSKeyword aFunction, const int32_t aAllowedTypes[],
                      int32_t aVariantMaskAll, uint16_t aMinElems,
                      uint16_t aMaxElems, nsCSSValue &aValue);
   bool ParseFunctionInternals(const int32_t aVariantMask[],
                               int32_t aVariantMaskAll,
                               uint16_t aMinElems,
@@ -9912,16 +9917,18 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_stroke:
     return ParsePaint(aPropID);
   case eCSSProperty_stroke_dasharray:
     return ParseDasharray();
   case eCSSProperty_marker:
     return ParseMarker();
   case eCSSProperty_paint_order:
     return ParsePaintOrder();
+  case eCSSProperty_clip_path:
+    return ParseClipPath();
   case eCSSProperty_all:
     return ParseAll();
   default:
     NS_ABORT_IF_FALSE(false, "should not be called");
     return false;
   }
 }
 
@@ -13775,16 +13782,135 @@ bool CSSParserImpl::ParseTransform(bool 
       cur->mNext = new nsCSSValueList;
       cur = cur->mNext;
     }
   }
   AppendValue(eCSSProperty_transform, value);
   return true;
 }
 
+/* Reads a polygon function's argument list.
+ */
+bool
+CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue)
+{
+  uint16_t numArgs = 1;
+
+  nsCSSValue fillRuleValue;
+  if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) {
+    numArgs++;
+
+    // The fill-rule must be comma separated from the polygon points.
+    if (!ExpectSymbol(',', true)) {
+      REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
+      SkipUntil(')');
+      return false;
+    }
+  }
+
+  nsCSSValue coordinates;
+  nsCSSValuePairList* item = coordinates.SetPairListValue();
+  for (;;) {
+    nsCSSValue xValue, yValue;
+    if (!ParseVariant(xValue, VARIANT_LPCALC, nullptr) ||
+        !ParseVariant(yValue, VARIANT_LPCALC, nullptr)) {
+      REPORT_UNEXPECTED_TOKEN(PECoordinatePair);
+      SkipUntil(')');
+      return false;
+    }
+    item->mXValue = xValue;
+    item->mYValue = yValue;
+
+    // See whether to continue or whether to look for end of function.
+    if (!ExpectSymbol(',', true)) {
+      // We need to read the closing parenthesis.
+      if (!ExpectSymbol(')', true)) {
+        REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen);
+        SkipUntil(')');
+        return false;
+      }
+      break;
+    }
+    item->mNext = new nsCSSValuePairList;
+    item = item->mNext;
+  }
+
+  nsRefPtr<nsCSSValue::Array> functionArray =
+    aValue.InitFunction(eCSSKeyword_polygon, numArgs);
+  functionArray->Item(numArgs) = coordinates;
+  if (numArgs > 1) {
+    functionArray->Item(1) = fillRuleValue;
+  }
+
+  return true;
+}
+
+bool
+CSSParserImpl::ParseBasicShape(nsCSSValue& aValue)
+{
+  if (!GetToken(true)) {
+    return false;
+  }
+
+  if (mToken.mType != eCSSToken_Function) {
+    UngetToken();
+    return false;
+  }
+
+  nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
+  switch (keyword) {
+  case eCSSKeyword_polygon:
+    return ParsePolygonFunction(aValue);
+  default:
+    return false;
+  }
+}
+
+/* Parse a clip-path url to a <clipPath> element or a basic shape. */
+bool CSSParserImpl::ParseClipPath()
+{
+  nsCSSValue value;
+  if (!ParseVariant(value, VARIANT_HUO, nullptr)) {
+    if (!nsLayoutUtils::CSSClipPathShapesEnabled()) {
+      // With CSS Clip Path Shapes disabled, we should only accept
+      // SVG clipPath reference and none.
+      REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL);
+      return false;
+    }
+
+    bool shape = false, box = false;
+    nsCSSValueList* cur = value.SetListValue();
+    bool eof = false;
+    for (int i = 0; i < 2; ++i) {
+      if (ParseBasicShape(cur->mValue) && !shape) {
+        shape = true;
+      } else if (ParseEnum(cur->mValue, nsCSSProps::kClipShapeSizingKTable) &&
+                 !box) {
+        box = true;
+      } else {
+        break;
+      }
+      if (!GetToken(true)) {
+        eof = true;
+        break;
+      }
+      UngetToken();
+      cur->mNext = new nsCSSValueList;
+      cur = cur->mNext;
+    }
+    if (!shape && !box && !eof) {
+      REPORT_UNEXPECTED_EOF(PEClipPathEOF);
+      return false;
+    }
+  }
+
+  AppendValue(eCSSProperty_clip_path, value);
+  return true;
+}
+
 bool CSSParserImpl::ParseTransformOrigin(bool aPerspective)
 {
   nsCSSValuePair position;
   if (!ParseBoxPositionValues(position, true))
     return false;
 
   nsCSSProperty prop = eCSSProperty_transform_origin;
   if (aPerspective) {
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -3607,20 +3607,20 @@ CSS_PROP_FONT(
     eStyleAnimType_None)
 #endif // !defined(CSS_PROP_LIST_EXCLUDE_INTERNAL)
 #endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND)
 
 CSS_PROP_SVGRESET(
     clip-path,
     clip_path,
     ClipPath,
-    CSS_PROPERTY_PARSE_VALUE |
+    CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_CREATES_STACKING_CONTEXT,
     "",
-    VARIANT_HUO,
+    0,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_SVG(
     clip-rule,
     clip_rule,
     ClipRule,
     CSS_PROPERTY_PARSE_VALUE,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1875,16 +1875,27 @@ const KTableValue nsCSSProps::kDominantB
 };
 
 const KTableValue nsCSSProps::kFillRuleKTable[] = {
   eCSSKeyword_nonzero, NS_STYLE_FILL_RULE_NONZERO,
   eCSSKeyword_evenodd, NS_STYLE_FILL_RULE_EVENODD,
   eCSSKeyword_UNKNOWN, -1
 };
 
+const KTableValue nsCSSProps::kClipShapeSizingKTable[] = {
+  eCSSKeyword_content_box,   NS_STYLE_CLIP_SHAPE_SIZING_CONTENT,
+  eCSSKeyword_padding_box,   NS_STYLE_CLIP_SHAPE_SIZING_PADDING,
+  eCSSKeyword_border_box,    NS_STYLE_CLIP_SHAPE_SIZING_BORDER,
+  eCSSKeyword_margin_box,    NS_STYLE_CLIP_SHAPE_SIZING_MARGIN,
+  eCSSKeyword_fill_box,      NS_STYLE_CLIP_SHAPE_SIZING_FILL,
+  eCSSKeyword_stroke_box,    NS_STYLE_CLIP_SHAPE_SIZING_STROKE,
+  eCSSKeyword_view_box,      NS_STYLE_CLIP_SHAPE_SIZING_VIEW,
+  eCSSKeyword_UNKNOWN,-1
+};
+
 const KTableValue nsCSSProps::kFilterFunctionKTable[] = {
   eCSSKeyword_blur, NS_STYLE_FILTER_BLUR,
   eCSSKeyword_brightness, NS_STYLE_FILTER_BRIGHTNESS,
   eCSSKeyword_contrast, NS_STYLE_FILTER_CONTRAST,
   eCSSKeyword_grayscale, NS_STYLE_FILTER_GRAYSCALE,
   eCSSKeyword_invert, NS_STYLE_FILTER_INVERT,
   eCSSKeyword_opacity, NS_STYLE_FILTER_OPACITY,
   eCSSKeyword_saturate, NS_STYLE_FILTER_SATURATE,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -530,16 +530,17 @@ public:
   static const KTableValue kBorderImageSliceKTable[];
   static const KTableValue kBorderStyleKTable[];
   static const KTableValue kBorderWidthKTable[];
   static const KTableValue kBoxAlignKTable[];
   static const KTableValue kBoxDecorationBreakKTable[];
   static const KTableValue kBoxDirectionKTable[];
   static const KTableValue kBoxOrientKTable[];
   static const KTableValue kBoxPackKTable[];
+  static const KTableValue kClipShapeSizingKTable[];
   static const KTableValue kDominantBaselineKTable[];
   static const KTableValue kFillRuleKTable[];
   static const KTableValue kFilterFunctionKTable[];
   static const KTableValue kImageRenderingKTable[];
   static const KTableValue kShapeRenderingKTable[];
   static const KTableValue kStrokeLinecapKTable[];
   static const KTableValue kStrokeLinejoinKTable[];
   static const KTableValue kStrokeContextValueKTable[];
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -822,16 +822,42 @@ private:
   nsCSSProperty mProperty;
   nsAString &mResult;
   nsCSSValue::Serialization mValueSerialization;
 };
 
 } // anonymous namespace
 
 void
+nsCSSValue::AppendPolygonToString(nsCSSProperty aProperty, nsAString& aResult,
+                                  Serialization aSerialization) const
+{
+  const nsCSSValue::Array* array = GetArrayValue();
+  NS_ABORT_IF_FALSE(array->Count() > 1 && array->Count() <= 3,
+                    "Polygons must have name and at least one more value.");
+  // When the array has 2 elements, the item on index 1 is the coordinate
+  // pair list.
+  // When the array has 3 elements, the item on index 1 is a fill-rule
+  // and item on index 2 is the coordinate pair list.
+  size_t index = 1;
+  if (array->Count() == 3) {
+    const nsCSSValue& fillRuleValue = array->Item(index);
+    NS_ABORT_IF_FALSE(fillRuleValue.GetUnit() == eCSSUnit_Enumerated,
+                      "Expected polygon fill rule.");
+    int32_t fillRule = fillRuleValue.GetIntValue();
+    AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(fillRule,
+                                                  nsCSSProps::kFillRuleKTable),
+                       aResult);
+    aResult.AppendLiteral(", ");
+    ++index;
+  }
+  array->Item(index).AppendToString(aProperty, aResult, aSerialization);
+}
+
+void
 nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult,
                            Serialization aSerialization) const
 {
   // eCSSProperty_UNKNOWN gets used for some recursive calls below.
   NS_ABORT_IF_FALSE((0 <= aProperty &&
                      aProperty <= eCSSProperty_COUNT_no_shorthands) ||
                     aProperty == eCSSProperty_UNKNOWN,
                     "property ID out of range");
@@ -917,63 +943,71 @@ nsCSSValue::AppendToString(nsCSSProperty
   /* Although Function is backed by an Array, we'll handle it separately
    * because it's a bit quirky.
    */
   else if (eCSSUnit_Function == unit) {
     const nsCSSValue::Array* array = GetArrayValue();
     NS_ABORT_IF_FALSE(array->Count() >= 1,
                       "Functions must have at least one element for the name.");
 
-    /* Append the function name. */
     const nsCSSValue& functionName = array->Item(0);
-    if (functionName.GetUnit() == eCSSUnit_Enumerated) {
-      // We assume that the first argument is always of nsCSSKeyword type.
-      const nsCSSKeyword functionId = functionName.GetKeywordValue();
-      NS_ConvertASCIItoUTF16 ident(nsCSSKeywords::GetStringValue(functionId));
-      // Bug 721136: Normalize the identifier to lowercase, except that things
-      // like scaleX should have the last character capitalized.  This matches
-      // what other browsers do.
-      switch (functionId) {
-        case eCSSKeyword_rotatex:
-        case eCSSKeyword_scalex:
-        case eCSSKeyword_skewx:
-        case eCSSKeyword_translatex:
-          ident.Replace(ident.Length() - 1, 1, char16_t('X'));
-          break;
+    NS_ABORT_IF_FALSE(functionName.GetUnit() == eCSSUnit_Enumerated,
+                      "Functions must have an enumerated name.");
 
-        case eCSSKeyword_rotatey:
-        case eCSSKeyword_scaley:
-        case eCSSKeyword_skewy:
-        case eCSSKeyword_translatey:
-          ident.Replace(ident.Length() - 1, 1, char16_t('Y'));
-          break;
+    /* Append the function name. */
+    // The first argument is always of nsCSSKeyword type.
+    const nsCSSKeyword functionId = functionName.GetKeywordValue();
+    NS_ConvertASCIItoUTF16 ident(nsCSSKeywords::GetStringValue(functionId));
+    // Bug 721136: Normalize the identifier to lowercase, except that things
+    // like scaleX should have the last character capitalized.  This matches
+    // what other browsers do.
+    switch (functionId) {
+      case eCSSKeyword_rotatex:
+      case eCSSKeyword_scalex:
+      case eCSSKeyword_skewx:
+      case eCSSKeyword_translatex:
+        ident.Replace(ident.Length() - 1, 1, char16_t('X'));
+        break;
 
-        case eCSSKeyword_rotatez:
-        case eCSSKeyword_scalez:
-        case eCSSKeyword_translatez:
-          ident.Replace(ident.Length() - 1, 1, char16_t('Z'));
-          break;
+      case eCSSKeyword_rotatey:
+      case eCSSKeyword_scaley:
+      case eCSSKeyword_skewy:
+      case eCSSKeyword_translatey:
+        ident.Replace(ident.Length() - 1, 1, char16_t('Y'));
+        break;
 
-        default:
-          break;
-      }
-      nsStyleUtil::AppendEscapedCSSIdent(ident, aResult);
-    } else {
-      MOZ_ASSERT(false, "should no longer have non-enumerated functions");
+      case eCSSKeyword_rotatez:
+      case eCSSKeyword_scalez:
+      case eCSSKeyword_translatez:
+        ident.Replace(ident.Length() - 1, 1, char16_t('Z'));
+        break;
+
+      default:
+        break;
     }
+    nsStyleUtil::AppendEscapedCSSIdent(ident, aResult);
     aResult.Append('(');
 
-    /* Now, step through the function contents, writing each of them as we go. */
-    for (size_t index = 1; index < array->Count(); ++index) {
-      array->Item(index).AppendToString(aProperty, aResult,
-                                        aSerialization);
+    switch (functionId) {
+      case eCSSKeyword_polygon:
+        AppendPolygonToString(aProperty, aResult, aSerialization);
+        break;
 
-      /* If we're not at the final element, append a comma. */
-      if (index + 1 != array->Count())
-        aResult.AppendLiteral(", ");
+      default: {
+        // Now, step through the function contents, writing each of
+        // them as we go.
+        for (size_t index = 1; index < array->Count(); ++index) {
+          array->Item(index).AppendToString(aProperty, aResult,
+                                            aSerialization);
+
+          /* If we're not at the final element, append a comma. */
+          if (index + 1 != array->Count())
+            aResult.AppendLiteral(", ");
+        }
+      }
     }
 
     /* Finally, append the closing parenthesis. */
     aResult.Append(')');
   }
   else if (IsCalcUnit()) {
     NS_ABORT_IF_FALSE(GetUnit() == eCSSUnit_Calc, "unexpected unit");
     CSSValueSerializeCalcOps ops(aProperty, aResult, aSerialization);
@@ -1081,16 +1115,22 @@ nsCSSValue::AppendToString(nsCSSProperty
 
     case eCSSProperty_touch_action:
       nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue,
                                          NS_STYLE_TOUCH_ACTION_NONE,
                                          NS_STYLE_TOUCH_ACTION_MANIPULATION,
                                          aResult);
       break;
 
+    case eCSSProperty_clip_path:
+      AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue,
+                            nsCSSProps::kClipShapeSizingKTable),
+                         aResult);
+      break;
+
     default:
       const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, intValue);
       AppendASCIItoUTF16(name, aResult);
       break;
     }
   }
   else if (eCSSUnit_EnumColor == unit) {
     // we can lookup the property in the ColorTable and then
@@ -2040,17 +2080,18 @@ nsCSSValuePairList::AppendToString(nsCSS
       aResult.Append(char16_t(' '));
       item->mYValue.AppendToString(aProperty, aResult, aSerialization);
     }
     item = item->mNext;
     if (!item)
       break;
 
     if (nsCSSProps::PropHasFlags(aProperty,
-                                 CSS_PROPERTY_VALUE_LIST_USES_COMMAS))
+                                 CSS_PROPERTY_VALUE_LIST_USES_COMMAS) ||
+        aProperty == eCSSProperty_clip_path)
       aResult.Append(char16_t(','));
     aResult.Append(char16_t(' '));
   }
 }
 
 bool
 nsCSSValuePairList::operator==(const nsCSSValuePairList& aOther) const
 {
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -714,16 +714,19 @@ public:
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   static const char16_t* GetBufferValue(nsStringBuffer* aBuffer) {
     return static_cast<char16_t*>(aBuffer->Data());
   }
 
+  void AppendPolygonToString(nsCSSProperty aProperty, nsAString& aResult,
+                             Serialization aValueSerialization) const;
+
 protected:
   nsCSSUnit mUnit;
   union {
     int32_t    mInt;
     float      mFloat;
     // Note: the capacity of the buffer may exceed the length of the string.
     // If we're of a string type, mString is not null.
     nsStringBuffer* mString;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -5171,28 +5171,94 @@ CSSValue*
 nsComputedDOMStyle::DoGetStopColor()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
   SetToRGBAColor(val, StyleSVGReset()->mStopColor);
   return val;
 }
 
 CSSValue*
+nsComputedDOMStyle::CreatePrimitiveValueForClipPath(
+  const nsStyleBasicShape* aStyleBasicShape, uint8_t aSizingBox)
+{
+  nsDOMCSSValueList* valueList = GetROCSSValueList(false);
+
+  if (aStyleBasicShape &&
+      aStyleBasicShape->GetShapeType() == nsStyleBasicShape::Type::ePolygon) {
+    nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+
+    // Shape function name and opening parenthesis.
+    nsAutoString shapeFunctionString;
+    AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(eCSSKeyword_polygon),
+                       shapeFunctionString);
+    shapeFunctionString.Append('(');
+    uint8_t fillRule = aStyleBasicShape->GetFillRule();
+    if (fillRule == NS_STYLE_FILL_RULE_EVENODD) {
+      shapeFunctionString.AppendLiteral("evenodd");
+    }
+    for (size_t i = 0; i < aStyleBasicShape->Coordinates().Length(); i += 2) {
+      nsAutoString coordString;
+      if (i > 0 || fillRule) {
+        shapeFunctionString.AppendLiteral(", ");
+      }
+      SetCssTextToCoord(coordString,
+                        aStyleBasicShape->Coordinates()[i]);
+      shapeFunctionString.Append(coordString);
+      shapeFunctionString.Append(' ');
+      SetCssTextToCoord(coordString,
+                        aStyleBasicShape->Coordinates()[i + 1]);
+      shapeFunctionString.Append(coordString);
+    }
+    shapeFunctionString.Append(')');
+    val->SetString(shapeFunctionString);
+    valueList->AppendCSSValue(val);
+  }
+
+  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+
+  if (aSizingBox == NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) {
+    return valueList;
+  }
+
+  nsAutoString boxString;
+  AppendASCIItoUTF16(
+    nsCSSProps::ValueToKeyword(aSizingBox,
+                               nsCSSProps::kClipShapeSizingKTable),
+                               boxString);
+  val->SetString(boxString);
+  valueList->AppendCSSValue(val);
+
+  return valueList;
+}
+
+CSSValue*
 nsComputedDOMStyle::DoGetClipPath()
 {
-  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
-
   const nsStyleSVGReset* svg = StyleSVGReset();
-
-  if (svg->mClipPath)
-    val->SetURI(svg->mClipPath);
-  else
-    val->SetIdent(eCSSKeyword_none);
-
-  return val;
+  switch (svg->mClipPath.GetType()) {
+    case NS_STYLE_CLIP_PATH_SHAPE:
+      return CreatePrimitiveValueForClipPath(svg->mClipPath.GetBasicShape(),
+                                             svg->mClipPath.GetSizingBox());
+    case NS_STYLE_CLIP_PATH_BOX:
+      return CreatePrimitiveValueForClipPath(nullptr,
+                                             svg->mClipPath.GetSizingBox());
+    case NS_STYLE_CLIP_PATH_URL: {
+      nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+      val->SetURI(svg->mClipPath.GetURL());
+      return val;
+    }
+    case NS_STYLE_CLIP_PATH_NONE: {
+      nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+      val->SetIdent(eCSSKeyword_none);
+      return val;
+    }
+    default:
+      NS_NOTREACHED("unexpected type");
+  }
+  return nullptr;
 }
 
 void
 nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
                                       const nsStyleCoord& aCoord)
 {
   nsRefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
   bool clampNegativeCalc = true;
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -581,16 +581,20 @@ private:
   bool GetFrameBorderRectWidth(nscoord& aWidth);
   bool GetFrameBorderRectHeight(nscoord& aHeight);
 
   /* Helper functions for computing the filter property style. */
   void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord);
   mozilla::dom::CSSValue* CreatePrimitiveValueForStyleFilter(
     const nsStyleFilter& aStyleFilter);
 
+  // Helper function for computing basic shape styles.
+  mozilla::dom::CSSValue* CreatePrimitiveValueForClipPath(
+    const nsStyleBasicShape* aStyleBasicShape, uint8_t aSizingBox);
+
   static nsComputedStyleMap* GetComputedStyleMap();
 
   // We don't really have a good immutable representation of "presentation".
   // Given the way GetComputedStyle is currently used, we should just grab the
   // 0th presshell, if any, from the document.
   nsWeakPtr mDocumentWeak;
   nsCOMPtr<nsIContent> mContent;
 
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -8753,16 +8753,96 @@ nsRuleNode::ComputeSVGData(void* aStartS
               svg->mTextRendering, canStoreInRuleTree,
               SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               parentSVG->mTextRendering,
               NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0);
 
   COMPUTE_END_INHERITED(SVG, svg)
 }
 
+void
+nsRuleNode::SetStyleClipPathToCSSValue(nsStyleClipPath* aStyleClipPath,
+                                       const nsCSSValue* aValue,
+                                       nsStyleContext* aStyleContext,
+                                       nsPresContext* aPresContext,
+                                       bool& aCanStoreInRuleTree)
+{
+  NS_ABORT_IF_FALSE(aValue->GetUnit() != eCSSUnit_ListDep ||
+                    aValue->GetUnit() != eCSSUnit_List,
+                    "expected a basic shape or reference box");
+
+  const nsCSSValueList* cur = aValue->GetListValue();
+
+  uint8_t sizingBox = NS_STYLE_CLIP_SHAPE_SIZING_NOBOX;
+  nsStyleBasicShape* basicShape = nullptr;
+  for (unsigned i = 0; i < 2; ++i) {
+    if (!cur) {
+      break;
+    }
+    if (cur->mValue.GetUnit() == eCSSUnit_Function) {
+      nsCSSValue::Array* shapeFunction = cur->mValue.GetArrayValue();
+      nsCSSKeyword functionName =
+        (nsCSSKeyword)shapeFunction->Item(0).GetIntValue();
+      if (functionName == eCSSKeyword_polygon) {
+        basicShape = new nsStyleBasicShape(nsStyleBasicShape::ePolygon);
+        NS_ABORT_IF_FALSE(shapeFunction->Count() > 1,
+                          "polygon has wrong number of arguments");
+        size_t j = 1;
+        if (shapeFunction->Item(j).GetUnit() == eCSSUnit_Enumerated) {
+          basicShape->SetFillRule(shapeFunction->Item(j).GetIntValue());
+          ++j;
+        }
+        int32_t mask = SETCOORD_PERCENT | SETCOORD_LENGTH |
+                       SETCOORD_STORE_CALC;
+        const nsCSSValuePairList* curPair =
+          shapeFunction->Item(j).GetPairListValue();
+        nsTArray<nsStyleCoord>& coordinates = basicShape->Coordinates();
+        while (curPair) {
+          nsStyleCoord xCoord, yCoord;
+          DebugOnly<bool> didSetCoordX = SetCoord(curPair->mXValue, xCoord,
+                                                  nsStyleCoord(), mask,
+                                                  aStyleContext, aPresContext,
+                                                  aCanStoreInRuleTree);
+          coordinates.AppendElement(xCoord);
+          NS_ABORT_IF_FALSE(didSetCoordX, "unexpected x coordinate unit");
+          DebugOnly<bool> didSetCoordY = SetCoord(curPair->mYValue, yCoord,
+                                                  nsStyleCoord(), mask,
+                                                  aStyleContext, aPresContext,
+                                                  aCanStoreInRuleTree);
+          coordinates.AppendElement(yCoord);
+          NS_ABORT_IF_FALSE(didSetCoordY, "unexpected y coordinate unit");
+          curPair = curPair->mNext;
+        }
+      } else {
+        // XXX Handle more basic shape functions later.
+        NS_NOTREACHED("unexpected basic shape function");
+        return;
+      }
+    } else if (cur->mValue.GetUnit() == eCSSUnit_Enumerated) {
+      int32_t type = cur->mValue.GetIntValue();
+      if (type > NS_STYLE_CLIP_SHAPE_SIZING_VIEW ||
+          type < NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) {
+        NS_NOTREACHED("unexpected reference box");
+        return;
+      }
+      sizingBox = (uint8_t)type;
+    } else {
+      NS_NOTREACHED("unexpected value");
+      return;
+    }
+    cur = cur->mNext;
+  }
+
+  if (basicShape) {
+    aStyleClipPath->SetBasicShape(basicShape, sizingBox);
+  } else {
+    aStyleClipPath->SetSizingBox(sizingBox);
+  }
+}
+
 // Returns true if the nsStyleFilter was successfully set using the nsCSSValue.
 bool
 nsRuleNode::SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
                                      const nsCSSValue& aValue,
                                      nsStyleContext* aStyleContext,
                                      nsPresContext* aPresContext,
                                      bool& aCanStoreInRuleTree)
 {
@@ -8857,27 +8937,47 @@ nsRuleNode::ComputeSVGResetData(void* aS
       eCSSUnit_Unset == lightingColorValue->GetUnit()) {
     svgReset->mLightingColor = NS_RGB(255, 255, 255);
   } else {
     SetColor(*lightingColorValue, parentSVGReset->mLightingColor,
              mPresContext, aContext, svgReset->mLightingColor,
              canStoreInRuleTree);
   }
 
-  // clip-path: url, none, inherit
+  // clip-path: url, <basic-shape> || <geometry-box>, none, inherit
   const nsCSSValue* clipPathValue = aRuleData->ValueForClipPath();
-  if (eCSSUnit_URL == clipPathValue->GetUnit()) {
-    svgReset->mClipPath = clipPathValue->GetURLValue();
-  } else if (eCSSUnit_None == clipPathValue->GetUnit() ||
-             eCSSUnit_Initial == clipPathValue->GetUnit() ||
-             eCSSUnit_Unset == clipPathValue->GetUnit()) {
-    svgReset->mClipPath = nullptr;
-  } else if (eCSSUnit_Inherit == clipPathValue->GetUnit()) {
-    canStoreInRuleTree = false;
-    svgReset->mClipPath = parentSVGReset->mClipPath;
+  switch (clipPathValue->GetUnit()) {
+    case eCSSUnit_Null:
+      break;
+    case eCSSUnit_None:
+    case eCSSUnit_Initial:
+    case eCSSUnit_Unset:
+      svgReset->mClipPath = nsStyleClipPath();
+      break;
+    case eCSSUnit_Inherit:
+      canStoreInRuleTree = false;
+      svgReset->mClipPath = parentSVGReset->mClipPath;
+      break;
+    case eCSSUnit_URL: {
+      svgReset->mClipPath = nsStyleClipPath();
+      nsIURI* url = clipPathValue->GetURLValue();
+      if (url) {
+        svgReset->mClipPath.SetURL(url);
+      }
+      break;
+    }
+    case eCSSUnit_List:
+    case eCSSUnit_ListDep: {
+      svgReset->mClipPath = nsStyleClipPath();
+      SetStyleClipPathToCSSValue(&svgReset->mClipPath, clipPathValue, aContext,
+                                 mPresContext, canStoreInRuleTree);
+      break;
+    }
+    default:
+      NS_NOTREACHED("unexpected unit");
   }
 
   // stop-opacity:
   SetFactor(*aRuleData->ValueForStopOpacity(),
             svgReset->mStopOpacity, canStoreInRuleTree,
             parentSVGReset->mStopOpacity, 1.0f,
             SETFCT_OPACITY | SETFCT_UNSET_INITIAL);
 
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -660,16 +660,21 @@ protected:
                             nsStyleContext* aContext,
                             bool aIsBoxShadow,
                             bool& aCanStoreInRuleTree);
   bool SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter,
                                 const nsCSSValue& aValue,
                                 nsStyleContext* aStyleContext,
                                 nsPresContext* aPresContext,
                                 bool& aCanStoreInRuleTree);
+  void SetStyleClipPathToCSSValue(nsStyleClipPath* aStyleClipPath,
+                                  const nsCSSValue* aValue,
+                                  nsStyleContext* aStyleContext,
+                                  nsPresContext* aPresContext,
+                                  bool& aCanStoreInRuleTree);
 
 private:
   nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent,
              nsIStyleRule* aRule, uint8_t aLevel, bool aIsImportant);
   ~nsRuleNode();
 
 public:
   static nsRuleNode* CreateRootNode(nsPresContext* aPresContext);
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -60,16 +60,32 @@ static inline mozilla::css::Side operato
 #define NS_BOXPROP_SOURCE_PHYSICAL 0
 #define NS_BOXPROP_SOURCE_LOGICAL  1
 
 // box-sizing
 #define NS_STYLE_BOX_SIZING_CONTENT       0
 #define NS_STYLE_BOX_SIZING_PADDING       1
 #define NS_STYLE_BOX_SIZING_BORDER        2
 
+// clip-path sizing
+#define NS_STYLE_CLIP_SHAPE_SIZING_NOBOX   0
+#define NS_STYLE_CLIP_SHAPE_SIZING_CONTENT 1
+#define NS_STYLE_CLIP_SHAPE_SIZING_PADDING 2
+#define NS_STYLE_CLIP_SHAPE_SIZING_BORDER  3
+#define NS_STYLE_CLIP_SHAPE_SIZING_MARGIN  4
+#define NS_STYLE_CLIP_SHAPE_SIZING_FILL    5
+#define NS_STYLE_CLIP_SHAPE_SIZING_STROKE  6
+#define NS_STYLE_CLIP_SHAPE_SIZING_VIEW    7
+
+// Basic Shapes
+#define NS_STYLE_BASIC_SHAPE_POLYGON       0
+//#define NS_STYLE_BASIC_SHAPE_CIRCLE      1
+//#define NS_STYLE_BASIC_SHAPE_ELLIPSE     2
+//#define NS_STYLE_BASIC_SHAPE_INSET       3
+
 // box-shadow
 #define NS_STYLE_BOX_SHADOW_INSET         0
 
 // float-edge
 #define NS_STYLE_FLOAT_EDGE_CONTENT       0
 #define NS_STYLE_FLOAT_EDGE_MARGIN        1
 
 // user-focus
@@ -473,16 +489,22 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN  3
 #define NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND   4
 
 // See nsStyleDisplay
 #define NS_STYLE_FLOAT_NONE                     0
 #define NS_STYLE_FLOAT_LEFT                     1
 #define NS_STYLE_FLOAT_RIGHT                    2
 
+// See nsStyleClipPath
+#define NS_STYLE_CLIP_PATH_NONE                 0
+#define NS_STYLE_CLIP_PATH_URL                  1
+#define NS_STYLE_CLIP_PATH_SHAPE                2
+#define NS_STYLE_CLIP_PATH_BOX                  3
+
 // See nsStyleFilter
 #define NS_STYLE_FILTER_NONE                    0
 #define NS_STYLE_FILTER_URL                     1
 #define NS_STYLE_FILTER_BLUR                    2
 #define NS_STYLE_FILTER_BRIGHTNESS              3
 #define NS_STYLE_FILTER_CONTRAST                4
 #define NS_STYLE_FILTER_GRAYSCALE               5
 #define NS_STYLE_FILTER_INVERT                  6
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1026,16 +1026,129 @@ nsChangeHint nsStyleSVG::CalcDifference(
       NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
       return hint;
     }
 
   return hint;
 }
 
 // --------------------
+// nsStyleClipPath
+//
+nsStyleClipPath::nsStyleClipPath()
+  : mType(NS_STYLE_CLIP_PATH_NONE)
+  , mURL(nullptr)
+  , mSizingBox(NS_STYLE_CLIP_SHAPE_SIZING_NOBOX)
+{
+}
+
+nsStyleClipPath::nsStyleClipPath(const nsStyleClipPath& aSource)
+  : mType(NS_STYLE_CLIP_PATH_NONE)
+  , mURL(nullptr)
+  , mSizingBox(NS_STYLE_CLIP_SHAPE_SIZING_NOBOX)
+{
+  if (aSource.mType == NS_STYLE_CLIP_PATH_URL) {
+    SetURL(aSource.mURL);
+  } else if (aSource.mType == NS_STYLE_CLIP_PATH_SHAPE) {
+    SetBasicShape(aSource.mBasicShape, aSource.mSizingBox);
+  } else if (aSource.mType == NS_STYLE_CLIP_PATH_SHAPE) {
+    SetSizingBox(aSource.mSizingBox);
+  }
+}
+
+nsStyleClipPath::~nsStyleClipPath()
+{
+  ReleaseRef();
+}
+
+nsStyleClipPath&
+nsStyleClipPath::operator=(const nsStyleClipPath& aOther)
+{
+  if (this == &aOther) {
+    return *this;
+  }
+
+  ReleaseRef();
+
+  if (aOther.mType == NS_STYLE_CLIP_PATH_URL) {
+    SetURL(aOther.mURL);
+  } else if (aOther.mType == NS_STYLE_CLIP_PATH_SHAPE) {
+    SetBasicShape(aOther.mBasicShape, aOther.mSizingBox);
+  } else if (aOther.mType == NS_STYLE_CLIP_PATH_BOX) {
+    SetSizingBox(aOther.mSizingBox);
+  } else {
+    mSizingBox = NS_STYLE_CLIP_SHAPE_SIZING_NOBOX;
+    mType = NS_STYLE_CLIP_PATH_NONE;
+  }
+  return *this;
+}
+
+
+bool
+nsStyleClipPath::operator==(const nsStyleClipPath& aOther) const
+{
+  if (mType != aOther.mType) {
+    return false;
+  }
+
+  if (mType == NS_STYLE_CLIP_PATH_URL) {
+    return EqualURIs(mURL, aOther.mURL);
+  } else if (mType == NS_STYLE_CLIP_PATH_SHAPE) {
+    return *mBasicShape == *aOther.mBasicShape &&
+           mSizingBox == aOther.mSizingBox;
+  } else if (mType == NS_STYLE_CLIP_PATH_BOX) {
+    return mSizingBox == aOther.mSizingBox;
+  }
+
+  return true;
+}
+
+void
+nsStyleClipPath::ReleaseRef()
+{
+  if (mType == NS_STYLE_CLIP_PATH_SHAPE) {
+    NS_ASSERTION(mBasicShape, "expected pointer");
+    mBasicShape->Release();
+  } else if (mType == NS_STYLE_CLIP_PATH_URL) {
+    NS_ASSERTION(mURL, "expected pointer");
+    mURL->Release();
+  }
+  mURL = nullptr;
+}
+
+void
+nsStyleClipPath::SetURL(nsIURI* aURL)
+{
+  NS_ASSERTION(aURL, "expected pointer");
+  ReleaseRef();
+  mURL = aURL;
+  mURL->AddRef();
+  mType = NS_STYLE_CLIP_PATH_URL;
+}
+
+void
+nsStyleClipPath::SetBasicShape(nsStyleBasicShape* aBasicShape, uint8_t aSizingBox)
+{
+  NS_ASSERTION(aBasicShape, "expected pointer");
+  ReleaseRef();
+  mBasicShape = aBasicShape;
+  mBasicShape->AddRef();
+  mSizingBox = aSizingBox;
+  mType = NS_STYLE_CLIP_PATH_SHAPE;
+}
+
+void
+nsStyleClipPath::SetSizingBox(uint8_t aSizingBox)
+{
+  ReleaseRef();
+  mSizingBox = aSizingBox;
+  mType = NS_STYLE_CLIP_PATH_BOX;
+}
+
+// --------------------
 // nsStyleFilter
 //
 nsStyleFilter::nsStyleFilter()
   : mType(NS_STYLE_FILTER_NONE)
   , mDropShadow(nullptr)
 {
   MOZ_COUNT_CTOR(nsStyleFilter);
 }
@@ -1140,17 +1253,16 @@ nsStyleFilter::SetDropShadow(nsCSSShadow
 // 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);
-    mClipPath                = nullptr;
     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;
 }
 
@@ -1174,17 +1286,17 @@ nsStyleSVGReset::nsStyleSVGReset(const n
   mVectorEffect = aSource.mVectorEffect;
   mMaskType = aSource.mMaskType;
 }
 
 nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
-  if (!EqualURIs(mClipPath, aOther.mClipPath) ||
+  if (mClipPath != aOther.mClipPath ||
       !EqualURIs(mMask, aOther.mMask) ||
       mFilters != aOther.mFilters) {
     NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
     // We only actually need to update the overflow area for filter
     // changes.  However, mask and clip-path changes require that we
     // update the PreEffectsBBoxProperty, which is done during overflow
     // computation.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2822,16 +2822,117 @@ struct nsStyleSVG {
    * Returns true if the fill is not "none" and the fill-opacity is greater
    * than zero.
    */
   bool HasFill() const {
     return mFill.mType != eStyleSVGPaintType_None && mFillOpacity > 0;
   }
 };
 
+class nsStyleBasicShape MOZ_FINAL {
+public:
+  enum Type {
+    // eInset,
+    // eCircle,
+    // eEllipse,
+    ePolygon
+  };
+
+  nsStyleBasicShape(Type type)
+    : mType(type)
+  {
+  }
+
+  Type GetShapeType() const { return mType; }
+
+  int32_t GetFillRule() const { return mFillRule; }
+  void SetFillRule(int32_t aFillRule)
+  {
+    NS_ASSERTION(mType == ePolygon, "expected polygon");
+    mFillRule = aFillRule;
+  }
+
+  nsTArray<nsStyleCoord>& Coordinates()
+  {
+    NS_ASSERTION(mType == ePolygon, "expected polygon");
+    return mCoordinates;
+  }
+
+  const nsTArray<nsStyleCoord>& Coordinates() const
+  {
+    NS_ASSERTION(mType == ePolygon, "expected polygon");
+    return mCoordinates;
+  }
+
+  bool operator==(const nsStyleBasicShape& aOther) const
+  {
+    return mType == aOther.mType &&
+           mFillRule == aOther.mFillRule &&
+           mCoordinates == aOther.mCoordinates;
+  }
+  bool operator!=(const nsStyleBasicShape& aOther) const {
+    return !(*this == aOther);
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(nsStyleBasicShape);
+
+private:
+  ~nsStyleBasicShape() {}
+
+  Type mType;
+  int32_t mFillRule;
+  nsTArray<nsStyleCoord> mCoordinates;
+};
+
+struct nsStyleClipPath
+{
+  nsStyleClipPath();
+  nsStyleClipPath(const nsStyleClipPath& aSource);
+  ~nsStyleClipPath();
+
+  nsStyleClipPath& operator=(const nsStyleClipPath& aOther);
+
+  bool operator==(const nsStyleClipPath& aOther) const;
+  bool operator!=(const nsStyleClipPath& aOther) const {
+    return !(*this == aOther);
+  }
+
+  int32_t GetType() const {
+    return mType;
+  }
+
+  nsIURI* GetURL() const {
+    NS_ASSERTION(mType == NS_STYLE_CLIP_PATH_URL, "wrong clip-path type");
+    return mURL;
+  }
+  void SetURL(nsIURI* aURL);
+
+  nsStyleBasicShape* GetBasicShape() const {
+    NS_ASSERTION(mType == NS_STYLE_CLIP_PATH_SHAPE, "wrong clip-path type");
+    return mBasicShape;
+  }
+
+  void SetBasicShape(nsStyleBasicShape* mBasicShape,
+                     uint8_t aSizingBox = NS_STYLE_CLIP_SHAPE_SIZING_NOBOX);
+
+  uint8_t GetSizingBox() const { return mSizingBox; }
+  void SetSizingBox(uint8_t aSizingBox);
+
+private:
+  void ReleaseRef();
+  void* operator new(size_t) MOZ_DELETE;
+
+  int32_t mType; // see NS_STYLE_CLIP_PATH_* constants in nsStyleConsts.h
+  union {
+    nsStyleBasicShape* mBasicShape;
+    nsIURI* mURL;
+  };
+  uint8_t mSizingBox; // see NS_STYLE_CLIP_SHAPE_SIZING_* constants in nsStyleConsts.h
+};
+
 struct nsStyleFilter {
   nsStyleFilter();
   nsStyleFilter(const nsStyleFilter& aSource);
   ~nsStyleFilter();
 
   nsStyleFilter& operator=(const nsStyleFilter& aOther);
 
   bool operator==(const nsStyleFilter& aOther) const;
@@ -2906,17 +3007,17 @@ struct nsStyleSVGReset {
     return NS_CombineHint(nsChangeHint_NeedReflow,
                           nsChangeHint_ClearAncestorIntrinsics);
   }
 
   bool HasFilters() const {
     return mFilters.Length() > 0;
   }
 
-  nsCOMPtr<nsIURI> mClipPath;         // [reset]
+  nsStyleClipPath mClipPath;          // [reset]
   nsTArray<nsStyleFilter> mFilters;   // [reset]
   nsCOMPtr<nsIURI> mMask;             // [reset]
   nscolor          mStopColor;        // [reset]
   nscolor          mFloodColor;       // [reset]
   nscolor          mLightingColor;    // [reset]
 
   float            mStopOpacity;      // [reset]
   float            mFloodOpacity;     // [reset]
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4579,16 +4579,64 @@ if (SpecialPowers.getBoolPref("svg.paint
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "normal" ],
     other_values: [ "fill", "fill stroke", "fill stroke markers", "stroke markers fill" ],
     invalid_values: [ "fill stroke markers fill", "fill normal" ]
   };
 }
 
+if (SpecialPowers.getBoolPref("layout.css.clip-path-shapes.enabled")) {
+  gCSSProperties["clip-path"] = {
+    domProp: "clip-path",
+    inherited: false,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "none" ],
+    other_values: [
+      // SVG reference clip-path
+      "url(#my-clip-path)",
+
+      "polygon(20px 20px)",
+      "polygon(20px 20%)",
+      "polygon(20% 20%)",
+      "polygon(20rem 20em)",
+      "polygon(20cm 20mm)",
+      "polygon(20px 20px, 30px 30px)",
+      "polygon(20px 20px, 30% 30%, 30px 30px)",
+      "polygon(nonzero, 20px 20px, 30% 30%, 30px 30px)",
+      "polygon(evenodd, 20px 20px, 30% 30%, 30px 30px)",
+
+      "content-box",
+      "padding-box",
+      "border-box",
+      "margin-box",
+      "fill-box",
+      "stroke-box",
+      "view-box",
+
+      "polygon(0 0) conten-box",
+      "border-box polygon(0 0)",
+      "padding-box    polygon(   0  20px ,  30px    20% )  ",
+    ],
+    invalid_values: [
+      "url(#test) url(#tes2)",
+      "polygon (0 0)",
+      "polygon(20px, 40px)",
+      "border-box content-box",
+      "polygon(0 0) polygon(0 0)",
+      "polygon(nonzero 0 0)",
+      "polygon(evenodd 20px 20px)",
+      "polygon(20px 20px, evenodd)",
+      "polygon(20px 20px, nonzero)",
+      "polygon(30% 30%",
+    ]
+  };
+}
+
+
 if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
   gCSSProperties["filter"] = {
     domProp: "filter",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [
       // SVG reference filters
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -546,18 +546,22 @@ nsSVGEffects::GetPaintingPropertyForURI(
 nsSVGEffects::EffectProperties
 nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   EffectProperties result;
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   result.mFilter = GetOrCreateFilterProperty(aFrame);
-  result.mClipPath =
-    GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty());
+  if (style->mClipPath.GetType() == NS_STYLE_CLIP_PATH_URL) {
+    result.mClipPath =
+      GetPaintingProperty(style->mClipPath.GetURL(), aFrame, ClipPathProperty());
+  } else {
+    result.mClipPath = nullptr;
+  }
   result.mMask =
     GetPaintingProperty(style->mMask, aFrame, MaskProperty());
   return result;
 }
 
 nsSVGPaintServerFrame *
 nsSVGEffects::GetPaintServer(nsIFrame *aTargetFrame, const nsStyleSVGPaint *aPaint,
                              const FramePropertyDescriptor *aType)
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -147,17 +147,18 @@ GetPreEffectsVisualOverflowUnion(nsIFram
 bool
 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
 {
   // Even when SVG display lists are disabled, returning true for SVG frames
   // does not adversely affect any of our callers. Therefore we don't bother
   // checking the SDL prefs here, since we don't know if we're being called for
   // painting or hit-testing anyway.
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
-  return (style->HasFilters() || style->mClipPath || style->mMask);
+  return (style->HasFilters() ||
+          style->mClipPath.GetType() != NS_STYLE_CLIP_PATH_NONE || style->mMask);
 }
 
 // For non-SVG frames, this gives the offset to the frame's "user space".
 // For SVG frames, this returns a zero offset.
 static nsPoint
 GetOffsetToBoundingBox(nsIFrame* aFrame)
 {
   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2001,16 +2001,19 @@ pref("layout.css.devPixelsPerPx", "-1.0"
 pref("layout.css.masking.enabled", true);
 
 // Is support for mix-blend-mode enabled?
 pref("layout.css.mix-blend-mode.enabled", true);
 
 // Is support for CSS Filters enabled?
 pref("layout.css.filters.enabled", false);
 
+// Is support for basic shapes in clip-path enabled?
+pref("layout.css.clip-path-shapes.enabled", false);
+
 // Is support for CSS sticky positioning enabled?
 pref("layout.css.sticky.enabled", true);
 
 // Is support for CSS "will-change" enabled?
 pref("layout.css.will-change.enabled", false);
 
 // Is support for DOMPoint enabled?
 pref("layout.css.DOMPoint.enabled", true);