From 9922d273d92c66d35f0e4505de50c4f671fc16b6 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 27 Apr 2015 11:15:36 +0100 Subject: [PATCH] Bug 1159053 - Cache SVG getBBox and objectBoundingBox calculations for better performance. r=heycam --- dom/svg/test/mochitest.ini | 1 + dom/svg/test/test_bbox-changes.xhtml | 78 ++++++++++++++++++++++++++++ layout/svg/nsSVGEffects.cpp | 9 ++++ layout/svg/nsSVGUtils.cpp | 18 +++++++ layout/svg/nsSVGUtils.h | 11 ++++ 5 files changed, 117 insertions(+) create mode 100644 dom/svg/test/test_bbox-changes.xhtml diff --git a/dom/svg/test/mochitest.ini b/dom/svg/test/mochitest.ini index 3ecfed0e666..8cd2244ef83 100644 --- a/dom/svg/test/mochitest.ini +++ b/dom/svg/test/mochitest.ini @@ -34,6 +34,7 @@ support-files = [test_animLengthUnits.xhtml] [test_bbox-with-invalid-viewBox.xhtml] [test_bbox.xhtml] +[test_bbox-changes.xhtml] [test_bounds.html] [test_bug872812.html] [test_getBBox-method.html] diff --git a/dom/svg/test/test_bbox-changes.xhtml b/dom/svg/test/test_bbox-changes.xhtml new file mode 100644 index 00000000000..502f6a2c967 --- /dev/null +++ b/dom/svg/test/test_bbox-changes.xhtml @@ -0,0 +1,78 @@ + + + + + Test that the results of getBBox update for changes + + + + + +

+ + + + + + + + +

+ + + +
+
+
+ + diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index 4344208a386..2dc0f7ba206 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -778,6 +778,9 @@ nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame) if (!aFrame->GetContent()->IsElement()) return; + // If the rendering has changed, the bounds may well have changed too: + aFrame->Properties().Delete(nsSVGUtils::ObjectBoundingBoxProperty()); + nsSVGRenderingObserverList *observerList = GetObserverList(aFrame->GetContent()->AsElement()); if (observerList) { @@ -802,6 +805,12 @@ nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame) void nsSVGEffects::InvalidateDirectRenderingObservers(Element *aElement, uint32_t aFlags /* = 0 */) { + nsIFrame* frame = aElement->GetPrimaryFrame(); + if (frame) { + // If the rendering has changed, the bounds may well have changed too: + frame->Properties().Delete(nsSVGUtils::ObjectBoundingBoxProperty()); + } + if (aElement->HasRenderingObservers()) { nsSVGRenderingObserverList *observerList = GetObserverList(aElement); if (observerList) { diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 5bd9574a8ff..9fe3f1de180 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -903,6 +903,17 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags) !static_cast(content)->HasValidDimensions()) { return bbox; } + + FrameProperties props = aFrame->Properties(); + + if (aFlags == eBBoxIncludeFillGeometry) { + gfxRect* prop = + static_cast(props.Get(ObjectBoundingBoxProperty())); + if (prop) { + return *prop; + } + } + gfxMatrix matrix; if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame || aFrame->GetType() == nsGkAtoms::svgUseFrame) { @@ -971,6 +982,13 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags) bbox = gfxRect(0, 0, 0, 0); } } + + if (aFlags == eBBoxIncludeFillGeometry) { + // Obtaining the bbox for objectBoundingBox calculations is common so we + // cache the result for future calls, since calculation can be expensive: + props.Set(ObjectBoundingBoxProperty(), new gfxRect(bbox)); + } + return bbox; } return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame); diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index ab3009de61b..70ba3fa450b 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -21,6 +21,7 @@ #include "nsColor.h" #include "nsCOMPtr.h" #include "nsID.h" +#include "nsIFrame.h" #include "nsISupportsBase.h" #include "nsMathUtils.h" #include "nsStyleStruct.h" @@ -179,12 +180,20 @@ class nsSVGUtils { public: typedef mozilla::dom::Element Element; + typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor; typedef mozilla::gfx::AntialiasMode AntialiasMode; typedef mozilla::gfx::FillRule FillRule; typedef mozilla::gfx::GeneralPattern GeneralPattern; static void Init(); + static void DestroyObjectBoundingBoxProperty(void* aPropertyValue) { + delete static_cast(aPropertyValue); + } + + NS_DECLARE_FRAME_PROPERTY(ObjectBoundingBoxProperty, + DestroyObjectBoundingBoxProperty); + /** * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame, @@ -394,6 +403,8 @@ public: * aFrame's userspace. */ static gfxRect GetBBox(nsIFrame *aFrame, + // If the default arg changes, update the handling for + // ObjectBoundingBoxProperty() in the implementation. uint32_t aFlags = eBBoxIncludeFillGeometry); /*