Bug 1383650 - Resolve SVG geometry metrics from CSS r=longsonr,emilio
☠☠ backed out by 1624c5a31917 ☠ ☠
authorviolet <violet.bugreport@gmail.com>
Thu, 16 May 2019 00:47:37 +0000
changeset 532846 f1f7b4ad95474592279f9d5f9e337ce94240bdbd
parent 532845 18b030b3166027a1a1e3d35ed779bdc4d7c6ab85
child 532847 7bc3bff991c45a6e9caefe5fb4791aeba7d29256
push id11272
push userapavel@mozilla.com
push dateThu, 16 May 2019 15:28:22 +0000
treeherdermozilla-beta@2265bfc5920d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr, emilio
bugs1383650
milestone68.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>");