Bug 1383650 - Resolve SVG geometry metrics from CSS r=longsonr,emilio
authorviolet <violet.bugreport@gmail.com>
Thu, 16 May 2019 13:21:20 +0000
changeset 474714 c6d19171ee1226562c32bc68826fcb8cf0a010be
parent 474713 b3152421e832cf876396bb7e7c1ad874cb174c90
child 474715 bda0f207ac29d986f2419f74c5b26b77888cc15f
push id113168
push userrmaries@mozilla.com
push dateTue, 21 May 2019 16:39:23 +0000
treeherdermozilla-inbound@3c0f78074b72 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr, emilio
bugs1383650
milestone69.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 1383650 - Resolve SVG geometry metrics from CSS r=longsonr,emilio This patch makes SVG retrieve metrics from CSS style. It doesn't handle <svg> element because geometry properties for outer <svg> element has been partially implemented long ago, it needs special change. It doesn't deal with the impact on SMIL. Differential Revision: https://phabricator.services.mozilla.com/D29992
dom/svg/SVGCircleElement.cpp
dom/svg/SVGEllipseElement.cpp
dom/svg/SVGForeignObjectElement.cpp
dom/svg/SVGGeometryProperty.h
dom/svg/SVGRectElement.cpp
testing/web-platform/meta/svg/embedded/image-embedding-svg-with-viewport-units-inline-style.svg.ini
testing/web-platform/meta/svg/embedded/image-embedding-svg-with-viewport-units.svg.ini
testing/web-platform/meta/svg/geometry/reftests/percentage.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-01.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-02.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-03.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-05.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-06.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-07.svg.ini
testing/web-platform/meta/svg/shapes/ellipse-08.svg.ini
testing/web-platform/tests/svg/shapes/reftests/disabled-shapes-01.svg
testing/web-platform/tests/svg/shapes/scripted/disabled-shapes-not-hit.svg
--- a/dom/svg/SVGCircleElement.cpp
+++ b/dom/svg/SVGCircleElement.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGCircleElement.h"
 #include "mozilla/gfx/2D.h"
 #include "nsGkAtoms.h"
 #include "mozilla/dom/SVGCircleElementBinding.h"
 #include "mozilla/dom/SVGLengthBinding.h"
+#include "SVGGeometryProperty.h"
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Circle)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
@@ -37,16 +38,18 @@ SVGCircleElement::SVGCircleElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGCircleElementBase(std::move(aNodeInfo)) {}
 
 bool SVGCircleElement::IsAttributeMapped(const nsAtom* aAttribute) const {
   return IsInLengthInfo(aAttribute, sLengthInfo) ||
          SVGCircleElementBase::IsAttributeMapped(aAttribute);
 }
 
+namespace SVGT = SVGGeometryProperty::Tags;
+
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGCircleElement)
 
 //----------------------------------------------------------------------
 
 already_AddRefed<DOMSVGAnimatedLength> SVGCircleElement::Cx() {
@@ -61,33 +64,39 @@ already_AddRefed<DOMSVGAnimatedLength> S
   return mLengthAttributes[ATTR_R].ToDOMAnimatedLength(this);
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 /* virtual */
 bool SVGCircleElement::HasValidDimensions() const {
-  return mLengthAttributes[ATTR_R].IsExplicitlySet() &&
-         mLengthAttributes[ATTR_R].GetAnimValInSpecifiedUnits() > 0;
+  float r;
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::R>(this, &r);
+  return r > 0;
 }
 
 SVGElement::LengthAttributesInfo SVGCircleElement::GetLengthInfo() {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // SVGGeometryElement methods
 
 bool SVGCircleElement::GetGeometryBounds(
     Rect* aBounds, const StrokeOptions& aStrokeOptions,
     const Matrix& aToBoundsSpace, const Matrix* aToNonScalingStrokeSpace) {
   float x, y, r;
-  GetAnimatedLengthValues(&x, &y, &r, nullptr);
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Cx, SVGT::Cy, SVGT::R>(this, &x, &y,
+                                                               &r);
 
   if (r <= 0.f) {
     // Rendering of the element is disabled
     *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x, y)), Size());
     return true;
   }
 
   if (aToBoundsSpace.IsRectilinear()) {
@@ -112,17 +121,19 @@ bool SVGCircleElement::GetGeometryBounds
     return true;
   }
 
   return false;
 }
 
 already_AddRefed<Path> SVGCircleElement::BuildPath(PathBuilder* aBuilder) {
   float x, y, r;
-  GetAnimatedLengthValues(&x, &y, &r, nullptr);
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Cx, SVGT::Cy, SVGT::R>(this, &x, &y,
+                                                               &r);
 
   if (r <= 0.0f) {
     return nullptr;
   }
 
   aBuilder->Arc(Point(x, y), r, 0, Float(2 * M_PI));
 
   return aBuilder->Finish();
--- a/dom/svg/SVGEllipseElement.cpp
+++ b/dom/svg/SVGEllipseElement.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGEllipseElement.h"
 #include "mozilla/dom/SVGEllipseElementBinding.h"
 #include "mozilla/dom/SVGLengthBinding.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/RefPtr.h"
+#include "SVGGeometryProperty.h"
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Ellipse)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
@@ -41,16 +42,18 @@ SVGEllipseElement::SVGEllipseElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGEllipseElementBase(std::move(aNodeInfo)) {}
 
 bool SVGEllipseElement::IsAttributeMapped(const nsAtom* aAttribute) const {
   return IsInLengthInfo(aAttribute, sLengthInfo) ||
          SVGEllipseElementBase::IsAttributeMapped(aAttribute);
 }
 
+namespace SVGT = SVGGeometryProperty::Tags;
+
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGEllipseElement)
 
 //----------------------------------------------------------------------
 // nsIDOMSVGEllipseElement methods
 
@@ -70,35 +73,40 @@ already_AddRefed<DOMSVGAnimatedLength> S
   return mLengthAttributes[RY].ToDOMAnimatedLength(this);
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 /* virtual */
 bool SVGEllipseElement::HasValidDimensions() const {
-  return mLengthAttributes[RX].IsExplicitlySet() &&
-         mLengthAttributes[RX].GetAnimValInSpecifiedUnits() > 0 &&
-         mLengthAttributes[RY].IsExplicitlySet() &&
-         mLengthAttributes[RY].GetAnimValInSpecifiedUnits() > 0;
+  float rx, ry;
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Rx, SVGT::Ry>(this, &rx, &ry);
+
+  return rx > 0 && ry > 0;
 }
 
 SVGElement::LengthAttributesInfo SVGEllipseElement::GetLengthInfo() {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // SVGGeometryElement methods
 
 bool SVGEllipseElement::GetGeometryBounds(
     Rect* aBounds, const StrokeOptions& aStrokeOptions,
     const Matrix& aToBoundsSpace, const Matrix* aToNonScalingStrokeSpace) {
   float x, y, rx, ry;
-  GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Cx, SVGT::Cy, SVGT::Rx, SVGT::Ry>(
+      this, &x, &y, &rx, &ry);
 
   if (rx <= 0.f || ry <= 0.f) {
     // Rendering of the element is disabled
     *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x, y)), Size());
     return true;
   }
 
   if (aToBoundsSpace.IsRectilinear()) {
@@ -124,17 +132,20 @@ bool SVGEllipseElement::GetGeometryBound
     return true;
   }
 
   return false;
 }
 
 already_AddRefed<Path> SVGEllipseElement::BuildPath(PathBuilder* aBuilder) {
   float x, y, rx, ry;
-  GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Cx, SVGT::Cy, SVGT::Rx, SVGT::Ry>(
+      this, &x, &y, &rx, &ry);
 
   if (rx <= 0.0f || ry <= 0.0f) {
     return nullptr;
   }
 
   EllipseToBezier(aBuilder, Point(x, y), Size(rx, ry));
 
   return aBuilder->Finish();
--- a/dom/svg/SVGForeignObjectElement.cpp
+++ b/dom/svg/SVGForeignObjectElement.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/dom/SVGForeignObjectElement.h"
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/SVGDocument.h"
 #include "mozilla/dom/SVGForeignObjectElementBinding.h"
 #include "mozilla/dom/SVGLengthBinding.h"
+#include "SVGGeometryProperty.h"
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(ForeignObject)
 
 namespace mozilla {
 namespace dom {
 
 JSObject* SVGForeignObjectElement::WrapNode(JSContext* aCx,
                                             JS::Handle<JSObject*> aGivenProto) {
@@ -35,16 +36,18 @@ SVGElement::LengthInfo SVGForeignObjectE
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGForeignObjectElement::SVGForeignObjectElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGGraphicsElement(std::move(aNodeInfo)) {}
 
+namespace SVGT = SVGGeometryProperty::Tags;
+
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGForeignObjectElement)
 
 //----------------------------------------------------------------------
 
 already_AddRefed<DOMSVGAnimatedLength> SVGForeignObjectElement::X() {
@@ -72,32 +75,42 @@ gfxMatrix SVGForeignObjectElement::Prepe
   // 'transform' attribute:
   gfxMatrix fromUserSpace =
       SVGGraphicsElement::PrependLocalTransformsTo(aMatrix, aWhich);
   if (aWhich == eUserSpaceToParent) {
     return fromUserSpace;
   }
   // our 'x' and 'y' attributes:
   float x, y;
-  const_cast<SVGForeignObjectElement*>(this)->GetAnimatedLengthValues(&x, &y,
-                                                                      nullptr);
+
+  if (GetPrimaryFrame()) {
+    SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y>(this, &x, &y);
+  } else {
+    // This function might be called for element in display:none subtree
+    // (e.g. getScreenCTM), we fall back to use SVG attributes.
+    const_cast<SVGForeignObjectElement*>(this)->GetAnimatedLengthValues(
+        &x, &y, nullptr);
+  }
+
   gfxMatrix toUserSpace = gfxMatrix::Translation(x, y);
   if (aWhich == eChildToUserSpace) {
     return toUserSpace * aMatrix;
   }
   MOZ_ASSERT(aWhich == eAllTransforms, "Unknown TransformTypes");
   return toUserSpace * fromUserSpace;
 }
 
 /* virtual */
 bool SVGForeignObjectElement::HasValidDimensions() const {
-  return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() &&
-         mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
-         mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
-         mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
+  float width, height;
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Width, SVGT::Height>(
+      const_cast<SVGForeignObjectElement*>(this), &width, &height);
+  return width > 0 && height > 0;
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(bool)
 SVGForeignObjectElement::IsAttributeMapped(const nsAtom* name) const {
   static const MappedAttributeEntry* const map[] = {sFEFloodMap,
new file mode 100644
--- /dev/null
+++ b/dom/svg/SVGGeometryProperty.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_SVGGeometryProperty_SVGGeometryProperty_h
+#define mozilla_dom_SVGGeometryProperty_SVGGeometryProperty_h
+
+#include "mozilla/dom/SVGElement.h"
+#include "SVGAnimatedLength.h"
+#include "ComputedStyle.h"
+#include "nsIFrame.h"
+#include <type_traits>
+
+namespace mozilla {
+namespace dom {
+
+namespace SVGGeometryProperty {
+namespace ResolverTypes {
+struct LengthPercentNoAuto {};
+struct LengthPercentRXY {};
+struct LengthPercentWidthHeight {};
+}  // namespace ResolverTypes
+
+namespace Tags {
+
+#define SVGGEOMETRYPROPERTY_GENERATETAG(tagName, resolver, direction, \
+                                        styleStruct)                  \
+  struct tagName {                                                    \
+    using ResolverType = ResolverTypes::resolver;                     \
+    constexpr static auto CtxDirection = SVGContentUtils::direction;  \
+    constexpr static auto Getter = &styleStruct::m##tagName;          \
+  }
+
+SVGGEOMETRYPROPERTY_GENERATETAG(X, LengthPercentNoAuto, X, nsStyleSVGReset);
+SVGGEOMETRYPROPERTY_GENERATETAG(Y, LengthPercentNoAuto, Y, nsStyleSVGReset);
+SVGGEOMETRYPROPERTY_GENERATETAG(Cx, LengthPercentNoAuto, X, nsStyleSVGReset);
+SVGGEOMETRYPROPERTY_GENERATETAG(Cy, LengthPercentNoAuto, Y, nsStyleSVGReset);
+SVGGEOMETRYPROPERTY_GENERATETAG(R, LengthPercentNoAuto, XY, nsStyleSVGReset);
+SVGGEOMETRYPROPERTY_GENERATETAG(Width, LengthPercentWidthHeight, X,
+                                nsStylePosition);
+SVGGEOMETRYPROPERTY_GENERATETAG(Height, LengthPercentWidthHeight, Y,
+                                nsStylePosition);
+
+#undef SVGGEOMETRYPROPERTY_GENERATETAG
+
+struct Ry;
+struct Rx {
+  using ResolverType = ResolverTypes::LengthPercentRXY;
+  constexpr static auto CtxDirection = SVGContentUtils::X;
+  constexpr static auto Getter = &nsStyleSVGReset::mRx;
+  using CounterPart = Ry;
+};
+struct Ry {
+  using ResolverType = ResolverTypes::LengthPercentRXY;
+  constexpr static auto CtxDirection = SVGContentUtils::Y;
+  constexpr static auto Getter = &nsStyleSVGReset::mRy;
+  using CounterPart = Rx;
+};
+
+}  // namespace Tags
+
+namespace details {
+template <class T>
+using AlwaysFloat = float;
+
+using CtxDirectionType = decltype(SVGContentUtils::X);
+
+template <CtxDirectionType CTD>
+float ResolvePureLengthPercentage(SVGElement* aElement,
+                                  const LengthPercentage& aLP) {
+  return aLP.ResolveToCSSPixelsWith(
+      [&] { return CSSCoord{SVGElementMetrics(aElement).GetAxisLength(CTD)}; });
+}
+
+template <class Tag>
+float ResolveImpl(ComputedStyle const& aStyle, SVGElement* aElement,
+                  ResolverTypes::LengthPercentNoAuto) {
+  auto const& value = aStyle.StyleSVGReset()->*Tag::Getter;
+  return ResolvePureLengthPercentage<Tag::CtxDirection>(aElement, value);
+}
+
+template <class Tag>
+float ResolveImpl(ComputedStyle const& aStyle, SVGElement* aElement,
+                  ResolverTypes::LengthPercentWidthHeight) {
+  static_assert(
+      std::is_same<Tag, Tags::Width>{} || std::is_same<Tag, Tags::Height>{},
+      "Wrong tag");
+
+  auto const& value = aStyle.StylePosition()->*Tag::Getter;
+  if (value.IsLengthPercentage()) {
+    return ResolvePureLengthPercentage<Tag::CtxDirection>(
+        aElement, value.AsLengthPercentage());
+  }
+
+  // |auto| and |max-content| etc. are treated as 0.
+  return 0.f;
+}
+
+template <class Tag>
+float ResolveImpl(ComputedStyle const& aStyle, SVGElement* aElement,
+                  ResolverTypes::LengthPercentRXY) {
+  static_assert(std::is_same<Tag, Tags::Rx>{} || std::is_same<Tag, Tags::Ry>{},
+                "Wrong tag");
+
+  auto const& value = aStyle.StyleSVGReset()->*Tag::Getter;
+  if (value.IsLengthPercentage()) {
+    return ResolvePureLengthPercentage<Tag::CtxDirection>(
+        aElement, value.AsLengthPercentage());
+  }
+
+  MOZ_ASSERT(value.IsAuto());
+  using Rother = typename Tag::CounterPart;
+  auto const& valueOther = aStyle.StyleSVGReset()->*Rother::Getter;
+
+  if (valueOther.IsAuto()) {
+    // Per SVG2, |Rx|, |Ry| resolve to 0 if both are |auto|
+    return 0.f;
+  }
+
+  // If |Rx| is auto while |Ry| not, |Rx| gets the value of |Ry|.
+  return ResolvePureLengthPercentage<Rother::CtxDirection>(
+      aElement, valueOther.AsLengthPercentage());
+}
+
+}  // namespace details
+
+template <class Tag>
+float ResolveWith(const ComputedStyle& aStyle, const SVGElement* aElement) {
+  // TODO: There are a lot of utilities lacking const-ness in dom/svg.
+  // We should fix that problem and remove this `const_cast`.
+  return details::ResolveImpl<Tag>(aStyle, const_cast<SVGElement*>(aElement),
+                                   typename Tag::ResolverType{});
+}
+
+// To add support for new properties, or to handle special cases for
+// existing properties, you can add a new tag in |Tags| and |ResolverTypes|
+// namespace, then implement the behavior in |details::ResolveImpl|.
+template <class... Tags>
+bool ResolveAll(const SVGElement* aElement,
+                details::AlwaysFloat<Tags>*... aRes) {
+  if (nsIFrame const* f = aElement->GetPrimaryFrame()) {
+    using dummy = int[];
+    (void)dummy{0, (*aRes = ResolveWith<Tags>(*f->Style(), aElement), 0)...};
+    return true;
+  }
+  return false;
+}
+
+}  // namespace SVGGeometryProperty
+}  // namespace dom
+}  // namespace mozilla
+
+#endif
--- a/dom/svg/SVGRectElement.cpp
+++ b/dom/svg/SVGRectElement.cpp
@@ -1,22 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGRectElement.h"
-#include "nsGkAtoms.h"
 #include "mozilla/dom/SVGLengthBinding.h"
 #include "mozilla/dom/SVGRectElementBinding.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/PathHelpers.h"
+#include "nsGkAtoms.h"
+#include "SVGGeometryProperty.h"
 #include <algorithm>
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Rect)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
@@ -49,16 +50,18 @@ SVGRectElement::SVGRectElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGRectElementBase(std::move(aNodeInfo)) {}
 
 bool SVGRectElement::IsAttributeMapped(const nsAtom* aAttribute) const {
   return IsInLengthInfo(aAttribute, sLengthInfo) ||
          SVGRectElementBase::IsAttributeMapped(aAttribute);
 }
 
+namespace SVGT = SVGGeometryProperty::Tags;
+
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRectElement)
 
 //----------------------------------------------------------------------
 
 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::X() {
@@ -85,38 +88,44 @@ already_AddRefed<DOMSVGAnimatedLength> S
   return mLengthAttributes[ATTR_RY].ToDOMAnimatedLength(this);
 }
 
 //----------------------------------------------------------------------
 // SVGElement methods
 
 /* virtual */
 bool SVGRectElement::HasValidDimensions() const {
-  return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() &&
-         mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
-         mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
-         mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
+  float width, height;
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::Width, SVGT::Height>(this, &width,
+                                                             &height);
+
+  return width > 0 && height > 0;
 }
 
 SVGElement::LengthAttributesInfo SVGRectElement::GetLengthInfo() {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // SVGGeometryElement methods
 
 bool SVGRectElement::GetGeometryBounds(Rect* aBounds,
                                        const StrokeOptions& aStrokeOptions,
                                        const Matrix& aToBoundsSpace,
                                        const Matrix* aToNonScalingStrokeSpace) {
   Rect rect;
   Float rx, ry;
-  GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width, &rect.height, &rx, &ry,
-                          nullptr);
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height,
+                                  SVGT::Rx, SVGT::Ry>(
+      this, &rect.x, &rect.y, &rect.width, &rect.height, &rx, &ry);
 
   if (rect.IsEmpty()) {
     // Rendering of the element disabled
     rect.SetEmpty();  // Make sure width/height are zero and not negative
     // We still want the x/y position from 'rect'
     *aBounds = aToBoundsSpace.TransformBounds(rect);
     return true;
   }
@@ -155,17 +164,21 @@ bool SVGRectElement::GetGeometryBounds(R
   }
 
   *aBounds = aToBoundsSpace.TransformBounds(rect);
   return true;
 }
 
 void SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath) {
   float x, y, width, height, rx, ry;
-  GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height,
+                                  SVGT::Rx, SVGT::Ry>(this, &x, &y, &width,
+                                                      &height, &rx, &ry);
 
   if (width <= 0 || height <= 0) {
     aSimplePath->Reset();
     return;
   }
 
   rx = std::max(rx, 0.0f);
   ry = std::max(ry, 0.0f);
@@ -175,17 +188,21 @@ void SVGRectElement::GetAsSimplePath(Sim
     return;
   }
 
   aSimplePath->SetRect(x, y, width, height);
 }
 
 already_AddRefed<Path> SVGRectElement::BuildPath(PathBuilder* aBuilder) {
   float x, y, width, height, rx, ry;
-  GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
+
+  MOZ_ASSERT(GetPrimaryFrame());
+  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height,
+                                  SVGT::Rx, SVGT::Ry>(this, &x, &y, &width,
+                                                      &height, &rx, &ry);
 
   if (width <= 0 || height <= 0) {
     return nullptr;
   }
 
   rx = std::max(rx, 0.0f);
   ry = std::max(ry, 0.0f);
 
@@ -193,28 +210,16 @@ already_AddRefed<Path> SVGRectElement::B
     // Optimization for the no rounded corners case.
     Rect r(x, y, width, height);
     aBuilder->MoveTo(r.TopLeft());
     aBuilder->LineTo(r.TopRight());
     aBuilder->LineTo(r.BottomRight());
     aBuilder->LineTo(r.BottomLeft());
     aBuilder->Close();
   } else {
-    // If either the 'rx' or the 'ry' attribute isn't set, then we have to
-    // set it to the value of the other:
-    bool hasRx = mLengthAttributes[ATTR_RX].IsExplicitlySet();
-    bool hasRy = mLengthAttributes[ATTR_RY].IsExplicitlySet();
-    MOZ_ASSERT(hasRx || hasRy);
-
-    if (hasRx && !hasRy) {
-      ry = rx;
-    } else if (hasRy && !hasRx) {
-      rx = ry;
-    }
-
     // Clamp rx and ry to half the rect's width and height respectively:
     rx = std::min(rx, width / 2);
     ry = std::min(ry, height / 2);
 
     RectCornerRadii radii(rx, ry);
     AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii);
   }
 
deleted file mode 100644
--- a/testing/web-platform/meta/svg/embedded/image-embedding-svg-with-viewport-units-inline-style.svg.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[image-embedding-svg-with-viewport-units-inline-style.svg]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/embedded/image-embedding-svg-with-viewport-units.svg.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[image-embedding-svg-with-viewport-units.svg]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/geometry/reftests/percentage.svg.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[percentage.svg]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-01.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-01.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-02.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-02.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-03.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-03.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-05.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-05.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-06.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-06.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-07.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-07.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/svg/shapes/ellipse-08.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ellipse-08.svg]
-  expected:
-    if (not (os == "win")): FAIL
-    if (os == "win"): FAIL
--- a/testing/web-platform/tests/svg/shapes/reftests/disabled-shapes-01.svg
+++ b/testing/web-platform/tests/svg/shapes/reftests/disabled-shapes-01.svg
@@ -16,35 +16,36 @@
       <rect style="width: 0"/>
       <rect style="width: 0" height="10"/>
       <rect style="width: -10px"/>
       <rect style="width: -10px" height="10"/>
       <rect style="height: 0"/>
       <rect style="height: 0" width="10"/>
       <rect style="height: -10px"/>
       <rect style="height: -10px" width="10"/>
+      <rect style="width: calc(-10px); height: calc(-10px)"/>
     </g>
 
     <g transform="translate(150, 50)">
       <circle/>
       <circle r="0"/>
       <circle r="-10"/>
       <circle style="r: 0"/>
       <circle style="r: -10px"/>
+      <circle style="r: calc(-10px)"/>
     </g>
 
     <g transform="translate(250, 50)">
       <ellipse/>
       <ellipse rx="0"/>
       <ellipse rx="0" ry="10"/>
-      <ellipse rx="-10" ry="10"/>
       <ellipse ry="0"/>
       <ellipse ry="0" rx="10"/>
-      <ellipse ry="-10" rx="10"/>
       <ellipse style="rx: 0"/>
       <ellipse style="rx: -10px"/>
       <ellipse style="rx: 0" ry="10"/>
       <ellipse style="ry: 0"/>
       <ellipse style="ry: -10px"/>
       <ellipse style="ry: 0" rx="10"/>
+      <ellipse style="rx: calc(-10px); ry: calc(-10px)"/>
     </g>
   </g>
 </svg>
--- a/testing/web-platform/tests/svg/shapes/scripted/disabled-shapes-not-hit.svg
+++ b/testing/web-platform/tests/svg/shapes/scripted/disabled-shapes-not-hit.svg
@@ -17,40 +17,41 @@
       <rect style="width: 0"/>
       <rect style="width: 0" height="10"/>
       <rect style="width: -10px"/>
       <rect style="width: -10px" height="10"/>
       <rect style="height: 0"/>
       <rect style="height: 0" width="10"/>
       <rect style="height: -10px"/>
       <rect style="height: -10px" width="10"/>
+      <rect style="width: calc(-10px); height: calc(-10px)"/>
     </g>
 
     <g transform="translate(150, 50)">
       <circle/>
       <circle r="0"/>
       <circle r="-10"/>
       <circle style="r: 0"/>
       <circle style="r: -10px"/>
+      <circle style="r: calc(-10px)"/>
     </g>
 
     <g transform="translate(250, 50)">
       <ellipse/>
       <ellipse rx="0"/>
       <ellipse rx="0" ry="10"/>
-      <ellipse rx="-10" ry="10"/>
       <ellipse ry="0"/>
       <ellipse ry="0" rx="10"/>
-      <ellipse ry="-10" rx="10"/>
       <ellipse style="rx: 0"/>
       <ellipse style="rx: -10px"/>
       <ellipse style="rx: 0" ry="10"/>
       <ellipse style="ry: 0"/>
       <ellipse style="ry: -10px"/>
       <ellipse style="ry: 0" rx="10"/>
+      <ellipse style="rx: calc(-10px); ry: calc(-10px)"/>
     </g>
   </g>
   <script><![CDATA[
     test(function() {
       let element = document.elementFromPoint(50, 50);
       assert_equals(element, document.documentElement, "does not hit one of the shapes");
     }, document.title + ": <rect>");