Bug 1383650 - Invalidate path cache when geometry changed via CSS r=longsonr
authorviolet <violet.bugreport@gmail.com>
Thu, 16 May 2019 13:21:20 +0000
changeset 474715 bda0f207ac29d986f2419f74c5b26b77888cc15f
parent 474714 c6d19171ee1226562c32bc68826fcb8cf0a010be
child 474716 db3f16ac63220693dc4e0b845b49d82ade8dd589
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
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 - Invalidate path cache when geometry changed via CSS r=longsonr We cached the path of an element. Previously we only need to invalidate the cached path if an geometry attribute is changed. Now we also need to invalidate if the corresponding CSS is changed. Differential Revision: https://phabricator.services.mozilla.com/D30472
dom/svg/SVGCircleElement.cpp
dom/svg/SVGCircleElement.h
dom/svg/SVGEllipseElement.cpp
dom/svg/SVGEllipseElement.h
dom/svg/SVGGeometryElement.cpp
dom/svg/SVGGeometryElement.h
dom/svg/SVGRectElement.cpp
dom/svg/SVGRectElement.h
layout/svg/SVGGeometryFrame.cpp
--- a/dom/svg/SVGCircleElement.cpp
+++ b/dom/svg/SVGCircleElement.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "ComputedStyle.h"
 #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)
@@ -134,10 +135,20 @@ already_AddRefed<Path> SVGCircleElement:
     return nullptr;
   }
 
   aBuilder->Arc(Point(x, y), r, 0, Float(2 * M_PI));
 
   return aBuilder->Finish();
 }
 
+bool SVGCircleElement::IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
+                                             const ComputedStyle& aOldStyle) {
+  auto *newSVGReset = aNewStyle.StyleSVGReset(),
+       *oldSVGReset = aOldStyle.StyleSVGReset();
+
+  return newSVGReset->mCx != oldSVGReset->mCx ||
+         newSVGReset->mCy != oldSVGReset->mCy ||
+         newSVGReset->mR != oldSVGReset->mR;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/SVGCircleElement.h
+++ b/dom/svg/SVGCircleElement.h
@@ -9,16 +9,18 @@
 
 #include "SVGGeometryElement.h"
 #include "SVGAnimatedLength.h"
 
 nsresult NS_NewSVGCircleElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
+class ComputedStyle;
+
 namespace dom {
 
 typedef SVGGeometryElement SVGCircleElementBase;
 
 class SVGCircleElement final : public SVGCircleElementBase {
  protected:
   explicit SVGCircleElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
@@ -38,16 +40,19 @@ class SVGCircleElement final : public SV
   virtual bool GetGeometryBounds(
       Rect* aBounds, const StrokeOptions& aStrokeOptions,
       const Matrix& aToBoundsSpace,
       const Matrix* aToNonScalingStrokeSpace = nullptr) override;
   virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
+  static bool IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
+                                    const ComputedStyle& aOldStyle);
+
   // WebIDL
   already_AddRefed<DOMSVGAnimatedLength> Cx();
   already_AddRefed<DOMSVGAnimatedLength> Cy();
   already_AddRefed<DOMSVGAnimatedLength> R();
 
  protected:
   virtual LengthAttributesInfo GetLengthInfo() override;
 
--- a/dom/svg/SVGEllipseElement.cpp
+++ b/dom/svg/SVGEllipseElement.cpp
@@ -1,14 +1,15 @@
 /* -*- 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 "ComputedStyle.h"
 #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"
 
@@ -146,10 +147,21 @@ already_AddRefed<Path> SVGEllipseElement
     return nullptr;
   }
 
   EllipseToBezier(aBuilder, Point(x, y), Size(rx, ry));
 
   return aBuilder->Finish();
 }
 
+bool SVGEllipseElement::IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
+                                              const ComputedStyle& aOldStyle) {
+  auto *newSVGReset = aNewStyle.StyleSVGReset(),
+       *oldSVGReset = aOldStyle.StyleSVGReset();
+
+  return newSVGReset->mCx != oldSVGReset->mCx ||
+         newSVGReset->mCy != oldSVGReset->mCy ||
+         newSVGReset->mRx != oldSVGReset->mRx ||
+         newSVGReset->mRy != oldSVGReset->mRy;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/SVGEllipseElement.h
+++ b/dom/svg/SVGEllipseElement.h
@@ -9,16 +9,18 @@
 
 #include "SVGAnimatedLength.h"
 #include "SVGGeometryElement.h"
 
 nsresult NS_NewSVGEllipseElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
+class ComputedStyle;
+
 namespace dom {
 
 typedef SVGGeometryElement SVGEllipseElementBase;
 
 class SVGEllipseElement final : public SVGEllipseElementBase {
  protected:
   explicit SVGEllipseElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
@@ -38,16 +40,19 @@ class SVGEllipseElement final : public S
   virtual bool GetGeometryBounds(
       Rect* aBounds, const StrokeOptions& aStrokeOptions,
       const Matrix& aToBoundsSpace,
       const Matrix* aToNonScalingStrokeSpace = nullptr) override;
   virtual already_AddRefed<Path> BuildPath(PathBuilder* aBuilder) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
+  static bool IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
+                                    const ComputedStyle& aOldStyle);
+
   // WebIDL
   already_AddRefed<DOMSVGAnimatedLength> Cx();
   already_AddRefed<DOMSVGAnimatedLength> Cy();
   already_AddRefed<DOMSVGAnimatedLength> Rx();
   already_AddRefed<DOMSVGAnimatedLength> Ry();
 
  protected:
   virtual LengthAttributesInfo GetLengthInfo() override;
--- a/dom/svg/SVGGeometryElement.cpp
+++ b/dom/svg/SVGGeometryElement.cpp
@@ -5,18 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SVGGeometryElement.h"
 
 #include "DOMSVGPoint.h"
 #include "gfxPlatform.h"
 #include "nsCOMPtr.h"
 #include "nsComputedDOMStyle.h"
+#include "nsSVGUtils.h"
 #include "SVGAnimatedLength.h"
-#include "nsSVGUtils.h"
+#include "SVGCircleElement.h"
+#include "SVGEllipseElement.h"
+#include "SVGRectElement.h"
 #include "mozilla/dom/SVGLengthBinding.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SVGContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::dom;
@@ -106,16 +109,32 @@ already_AddRefed<Path> SVGGeometryElemen
 
 already_AddRefed<Path> SVGGeometryElement::GetOrBuildPathForMeasuring() {
   RefPtr<DrawTarget> drawTarget =
       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
   FillRule fillRule = mCachedPath ? mCachedPath->GetFillRule() : GetFillRule();
   return GetOrBuildPath(drawTarget, fillRule);
 }
 
+bool SVGGeometryElement::IsGeometryChangedViaCSS(
+    ComputedStyle const& aNewStyle, ComputedStyle const& aOldStyle) const {
+  if (IsSVGElement(nsGkAtoms::rect)) {
+    return SVGRectElement::IsLengthChangedViaCSS(aNewStyle, aOldStyle);
+  }
+
+  if (IsSVGElement(nsGkAtoms::circle)) {
+    return SVGCircleElement::IsLengthChangedViaCSS(aNewStyle, aOldStyle);
+  }
+
+  if (IsSVGElement(nsGkAtoms::ellipse)) {
+    return SVGEllipseElement::IsLengthChangedViaCSS(aNewStyle, aOldStyle);
+  }
+  return false;
+}
+
 FillRule SVGGeometryElement::GetFillRule() {
   FillRule fillRule =
       FillRule::FILL_WINDING;  // Equivalent to StyleFillRule::Nonzero
 
   RefPtr<ComputedStyle> computedStyle =
       nsComputedDOMStyle::GetComputedStyleNoFlush(this, nullptr);
 
   if (computedStyle) {
--- a/dom/svg/SVGGeometryElement.h
+++ b/dom/svg/SVGGeometryElement.h
@@ -187,16 +187,23 @@ class SVGGeometryElement : public SVGGeo
    * In principle these inserted lines could interfere with path measurement,
    * so we keep callers that are looking to do measurement separate in case we
    * run into problems with the inserted lines negatively affecting measuring
    * for content.
    */
   virtual already_AddRefed<Path> GetOrBuildPathForMeasuring();
 
   /**
+   * Return |true| if some geometry properties (|x|, |y|, etc) are changed
+   * because of CSS change.
+   */
+  bool IsGeometryChangedViaCSS(ComputedStyle const& aNewStyle,
+                               ComputedStyle const& aOldStyle) const;
+
+  /**
    * Returns the current computed value of the CSS property 'fill-rule' for
    * this element.
    */
   FillRule GetFillRule();
 
   enum PathLengthScaleForType { eForTextPath, eForStroking };
 
   /**
--- a/dom/svg/SVGRectElement.cpp
+++ b/dom/svg/SVGRectElement.cpp
@@ -221,10 +221,25 @@ already_AddRefed<Path> SVGRectElement::B
 
     RectCornerRadii radii(rx, ry);
     AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii);
   }
 
   return aBuilder->Finish();
 }
 
+bool SVGRectElement::IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
+                                           const ComputedStyle& aOldStyle) {
+  auto *newSVGReset = aNewStyle.StyleSVGReset(),
+       *oldSVGReset = aOldStyle.StyleSVGReset();
+  auto *newPosition = aNewStyle.StylePosition(),
+       *oldPosition = aOldStyle.StylePosition();
+
+  return newSVGReset->mX != oldSVGReset->mX ||
+         newSVGReset->mY != oldSVGReset->mY ||
+         newPosition->mWidth != oldPosition->mWidth ||
+         newPosition->mHeight != oldPosition->mHeight ||
+         newSVGReset->mRx != oldSVGReset->mRx ||
+         newSVGReset->mRy != oldSVGReset->mRy;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/SVGRectElement.h
+++ b/dom/svg/SVGRectElement.h
@@ -9,16 +9,18 @@
 
 #include "SVGAnimatedLength.h"
 #include "SVGGeometryElement.h"
 
 nsresult NS_NewSVGRectElement(
     nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
+class ComputedStyle;
+
 namespace dom {
 
 typedef SVGGeometryElement SVGRectElementBase;
 
 class SVGRectElement final : public SVGRectElementBase {
  protected:
   explicit SVGRectElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
   virtual JSObject* WrapNode(JSContext* cx,
@@ -39,16 +41,19 @@ class SVGRectElement final : public SVGR
       const Matrix& aToBoundsSpace,
       const Matrix* aToNonScalingStrokeSpace = nullptr) override;
   virtual void GetAsSimplePath(SimplePath* aSimplePath) override;
   virtual already_AddRefed<Path> BuildPath(
       PathBuilder* aBuilder = nullptr) override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
+  static bool IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
+                                    const ComputedStyle& aOldStyle);
+
   // WebIDL
   already_AddRefed<DOMSVGAnimatedLength> X();
   already_AddRefed<DOMSVGAnimatedLength> Y();
   already_AddRefed<DOMSVGAnimatedLength> Height();
   already_AddRefed<DOMSVGAnimatedLength> Width();
   already_AddRefed<DOMSVGAnimatedLength> Rx();
   already_AddRefed<DOMSVGAnimatedLength> Ry();
 
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -192,16 +192,20 @@ void SVGGeometryFrame::DidSetComputedSty
         }
       } else {
         if (StyleSVG()->mFillRule != oldStyleSVG->mFillRule) {
           // Moz2D Path objects are fill-rule specific.
           element->ClearAnyCachedPath();
         }
       }
     }
+
+    if (element->IsGeometryChangedViaCSS(*Style(), *aOldComputedStyle)) {
+      element->ClearAnyCachedPath();
+    }
   }
 }
 
 bool SVGGeometryFrame::IsSVGTransformed(
     gfx::Matrix* aOwnTransform, gfx::Matrix* aFromParentTransform) const {
   bool foundTransform = false;
 
   // Check if our parent has children-only transforms: