Bug 635511 - Improve performance getting parent SVG elements r=jwatt
authorRobert Longson <longsonr@gmail.com>
Sat, 26 Feb 2011 10:21:11 +0000
changeset 64140 23f36fd8b36eba90591688691e4973187f505326
parent 64139 fc1935dc39874c203c48e8ee3f80a8684360fbba
child 64141 d6832692e74913bd18b5ae7741b44e77881978dc
push idunknown
push userunknown
push dateunknown
reviewersjwatt
bugs635511
milestone2.2a1pre
Bug 635511 - Improve performance getting parent SVG elements r=jwatt
content/svg/content/src/nsSVGAnimationElement.cpp
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGGraphicElement.cpp
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGSVGElement.h
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
--- a/content/svg/content/src/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -265,26 +265,24 @@ nsSVGAnimationElement::BindToTree(nsIDoc
   NS_ABORT_IF_FALSE(!mHrefTarget.get(),
                     "Shouldn't have href-target yet "
                     "(or it should've been cleared)");
   nsresult rv = nsSVGAnimationElementBase::BindToTree(aDocument, aParent,
                                                       aBindingParent,
                                                       aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv,rv);
 
-  // XXXdholbert is ownerDOMSVG (as a check for SVG parent) still needed here?
-  nsCOMPtr<nsIDOMSVGSVGElement> ownerDOMSVG;
-  rv = GetOwnerSVGElement(getter_AddRefs(ownerDOMSVG));
-
-  if (NS_FAILED(rv) || !ownerDOMSVG)
+  // XXXdholbert is GetCtx (as a check for SVG parent) still needed here?
+  if (!GetCtx()) {
     // No use proceeding. We don't have an SVG parent (yet) so we won't be able
     // to register ourselves etc. Maybe next time we'll have more luck.
     // (This sort of situation will arise a lot when trees are being constructed
     // piece by piece via script)
     return NS_OK;
+  }
 
   // Add myself to the animation controller's master set of animation elements.
   if (aDocument) {
     nsSMILAnimationController *controller = aDocument->GetAnimationController();
     if (controller) {
       controller->RegisterAnimationElement(this);
     }
     const nsAttrValue* href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
@@ -415,28 +413,23 @@ nsSVGAnimationElement::IsNodeOfType(PRUi
 }
 
 //----------------------------------------------------------------------
 // Implementation helpers
 
 nsSMILTimeContainer*
 nsSVGAnimationElement::GetTimeContainer()
 {
-  nsSMILTimeContainer *result = nsnull;
-  nsCOMPtr<nsIDOMSVGSVGElement> ownerDOMSVG;
-
-  nsresult rv = GetOwnerSVGElement(getter_AddRefs(ownerDOMSVG));
+  nsSVGSVGElement *element = nsSVGUtils::GetOuterSVGElement(this);
 
-  if (NS_SUCCEEDED(rv) && ownerDOMSVG) {
-    nsSVGSVGElement *ownerSVG =
-      static_cast<nsSVGSVGElement*>(ownerDOMSVG.get());
-    result = ownerSVG->GetTimedDocumentRoot();
+  if (element) {
+    return element->GetTimedDocumentRoot();
   }
 
-  return result;
+  return nsnull;
 }
 
 // nsIDOMElementTimeControl
 /* void beginElement (); */
 NS_IMETHODIMP
 nsSVGAnimationElement::BeginElement(void)
 {
   return BeginElementAt(0.f);
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1019,44 +1019,23 @@ NS_IMETHODIMP nsSVGElement::SetId(const 
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::id, aId, PR_TRUE);
 }
 
 /* readonly attribute nsIDOMSVGSVGElement ownerSVGElement; */
 NS_IMETHODIMP
 nsSVGElement::GetOwnerSVGElement(nsIDOMSVGSVGElement * *aOwnerSVGElement)
 {
-  *aOwnerSVGElement = nsnull;
-
-  nsIContent* ancestor = nsSVGUtils::GetParentElement(this);
+  NS_IF_ADDREF(*aOwnerSVGElement = GetCtx());
 
-  while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG) {
-    nsIAtom* tag = ancestor->Tag();
-    if (tag == nsGkAtoms::foreignObject) {
-      // SVG in a foreignObject must have its own <svg> (nsSVGOuterSVGFrame).
-      // Leave *aOwnerSVGElement nulled out, but don't throw.
-      return NS_OK;
-    }
-    if (tag == nsGkAtoms::svg) {
-      *aOwnerSVGElement = static_cast<nsSVGSVGElement*>(ancestor);
-      NS_ADDREF(*aOwnerSVGElement);
-      return NS_OK;
-    }
-    ancestor = nsSVGUtils::GetParentElement(ancestor);
-  }
-
-  // we don't have a parent SVG element...
-
-  // are _we_ the outermost SVG element? If yes, return nsnull, but don't fail
-  if (Tag() == nsGkAtoms::svg) {
+  if (*aOwnerSVGElement || Tag() == nsGkAtoms::svg) {
+    // If we found something or we're the outermost SVG element, that's OK.
     return NS_OK;
   }
-  
-  // no owner found and we aren't the outermost SVG element either.
-  // this situation can e.g. occur during content tree teardown. 
+  // Otherwise, we've got an invalid structure
   return NS_ERROR_FAILURE;
 }
 
 /* readonly attribute nsIDOMSVGElement viewportElement; */
 NS_IMETHODIMP
 nsSVGElement::GetViewportElement(nsIDOMSVGElement * *aViewportElement)
 {
   *aViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
@@ -1427,19 +1406,31 @@ nsIAtom* nsSVGElement::GetEventNameForAt
 #endif // MOZ_SMIL
 
   return aAttr;
 }
 
 nsSVGSVGElement *
 nsSVGElement::GetCtx()
 {
-  nsCOMPtr<nsIDOMSVGSVGElement> svg;
-  GetOwnerSVGElement(getter_AddRefs(svg));
-  return static_cast<nsSVGSVGElement*>(svg.get());
+  dom::Element* ancestor = nsSVGUtils::GetParentElement(this);
+
+  while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG) {
+    nsIAtom* tag = ancestor->Tag();
+    if (tag == nsGkAtoms::foreignObject) {
+      return nsnull;
+    }
+    if (tag == nsGkAtoms::svg) {
+      return static_cast<nsSVGSVGElement*>(ancestor);
+    }
+    ancestor = nsSVGUtils::GetParentElement(ancestor);
+  }
+
+  // we don't have an ancestor <svg> element...
+  return nsnull;
 }
 
 /* virtual */ gfxMatrix
 nsSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix)
 {
   return aMatrix;
 }
 
--- a/content/svg/content/src/nsSVGGraphicElement.cpp
+++ b/content/svg/content/src/nsSVGGraphicElement.cpp
@@ -33,16 +33,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSVGGraphicElement.h"
+#include "nsSVGSVGElement.h"
 #include "nsSVGTransformList.h"
 #include "nsSVGAnimatedTransformList.h"
 #include "nsGkAtoms.h"
 #include "nsSVGMatrix.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIFrame.h"
 #include "nsISVGChildFrame.h"
 #include "nsIDOMSVGPoint.h"
@@ -77,17 +78,17 @@ NS_IMETHODIMP nsSVGGraphicElement::GetNe
 {
   *aNearestViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGElement farthestViewportElement; */
 NS_IMETHODIMP nsSVGGraphicElement::GetFarthestViewportElement(nsIDOMSVGElement * *aFarthestViewportElement)
 {
-  *aFarthestViewportElement = nsSVGUtils::GetFarthestViewportElement(this).get();
+  NS_IF_ADDREF(*aFarthestViewportElement = nsSVGUtils::GetOuterSVGElement(this));
   return NS_OK;
 }
 
 /* nsIDOMSVGRect getBBox (); */
 NS_IMETHODIMP nsSVGGraphicElement::GetBBox(nsIDOMSVGRect **_retval)
 {
   *_retval = nsnull;
 
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -634,17 +634,17 @@ nsSVGSVGElement::CreateSVGNumber(nsIDOMS
   NS_ADDREF(*_retval = new DOMSVGNumber());
   return NS_OK;
 }
 
 /* nsIDOMSVGLength createSVGLength (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGLength(nsIDOMSVGLength **_retval)
 {
-  NS_IF_ADDREF(*_retval = new DOMSVGLength());
+  NS_ADDREF(*_retval = new DOMSVGLength());
   return NS_OK;
 }
 
 /* nsIDOMSVGAngle createSVGAngle (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGAngle(nsIDOMSVGAngle **_retval)
 {
   return NS_NewDOMSVGAngle(_retval);
@@ -729,17 +729,17 @@ nsSVGSVGElement::GetNearestViewportEleme
   *aNearestViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGElement farthestViewportElement; */
 NS_IMETHODIMP
 nsSVGSVGElement::GetFarthestViewportElement(nsIDOMSVGElement * *aFarthestViewportElement)
 {
-  *aFarthestViewportElement = nsSVGUtils::GetFarthestViewportElement(this).get();
+  NS_IF_ADDREF(*aFarthestViewportElement = nsSVGUtils::GetOuterSVGElement(this));
   return NS_OK;
 }
 
 /* nsIDOMSVGRect getBBox (); */
 NS_IMETHODIMP
 nsSVGSVGElement::GetBBox(nsIDOMSVGRect **_retval)
 {
   *_retval = nsnull;
@@ -877,34 +877,29 @@ nsSVGSVGElement::SetCurrentTranslate(flo
 {
   return SetCurrentScaleTranslate(mCurrentScale, x, y);
 }
 
 #ifdef MOZ_SMIL
 nsSMILTimeContainer*
 nsSVGSVGElement::GetTimedDocumentRoot()
 {
-  nsSMILTimeContainer *result = nsnull;
-
   if (mTimedDocumentRoot) {
-    result = mTimedDocumentRoot;
-  } else {
-    // We must not be the outermost SVG element, try to find it
-    nsCOMPtr<nsIDOMSVGSVGElement> outerSVGDOM;
-
-    nsresult rv = GetOwnerSVGElement(getter_AddRefs(outerSVGDOM));
-
-    if (NS_SUCCEEDED(rv) && outerSVGDOM) {
-      nsSVGSVGElement *outerSVG =
-        static_cast<nsSVGSVGElement*>(outerSVGDOM.get());
-      result = outerSVG->GetTimedDocumentRoot();
-    }
+    return mTimedDocumentRoot;
   }
 
-  return result;
+  // We must not be the outermost <svg> element, try to find it
+  nsSVGSVGElement *outerSVGElement =
+    nsSVGUtils::GetOuterSVGElement(this);
+
+  if (outerSVGElement) {
+    return outerSVGElement->GetTimedDocumentRoot();
+  }
+  // invalid structure
+  return nsnull;
 }
 #endif // MOZ_SMIL
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(PRBool)
 nsSVGSVGElement::IsAttributeMapped(const nsIAtom* name) const
@@ -991,17 +986,17 @@ nsSVGSVGElement::GetViewBoxTransform()
   // Do we have an override preserveAspectRatio value?
   const SVGPreserveAspectRatio* overridePARPtr =
     GetImageOverridePreserveAspectRatio();
 
   // May assign this to overridePARPtr if we have no viewBox but are faking one:
   SVGPreserveAspectRatio tmpPAR;
 
   float viewportWidth, viewportHeight;
-  if (nsSVGUtils::IsInnerSVG(this)) {
+  if (IsInner()) {
     nsSVGSVGElement *ctx = GetCtx();
     viewportWidth = mLengthAttributes[WIDTH].GetAnimValue(ctx);
     viewportHeight = mLengthAttributes[HEIGHT].GetAnimValue(ctx);
   } else {
     viewportWidth = mViewportWidth;
     viewportHeight = mViewportHeight;
   }
 
@@ -1164,17 +1159,17 @@ float
 nsSVGSVGElement::GetLength(PRUint8 aCtxType)
 {
   float h, w;
 
   if (mViewBox.IsValid()) {
     const nsSVGViewBoxRect& viewbox = mViewBox.GetAnimValue();
     w = viewbox.width;
     h = viewbox.height;
-  } else if (nsSVGUtils::IsInnerSVG(this)) {
+  } else if (IsInner()) {
     nsSVGSVGElement *ctx = GetCtx();
     w = mLengthAttributes[WIDTH].GetAnimValue(ctx);
     h = mLengthAttributes[HEIGHT].GetAnimValue(ctx);
   } else if (ShouldSynthesizeViewBox()) {
     w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[WIDTH],
                                            mViewportWidth, this);
     h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[HEIGHT],
                                            mViewportHeight, this);
@@ -1198,17 +1193,17 @@ nsSVGSVGElement::GetLength(PRUint8 aCtxT
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
 nsSVGSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix)
 {
-  if (nsSVGUtils::IsInnerSVG(this)) {
+  if (IsInner()) {
     float x, y;
     GetAnimatedLengthValues(&x, &y, nsnull);
     return GetViewBoxTransform() * gfxMatrix().Translate(gfxPoint(x, y)) * aMatrix;
   }
 
   if (IsRoot()) {
     gfxMatrix zoomPanTM;
     zoomPanTM.Translate(gfxPoint(mCurrentTranslate.GetX(), mCurrentTranslate.GetY()));
--- a/content/svg/content/src/nsSVGSVGElement.h
+++ b/content/svg/content/src/nsSVGSVGElement.h
@@ -52,20 +52,16 @@
 #include "nsSVGViewBox.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 #include "mozilla/dom/FromParser.h"
 
 #ifdef MOZ_SMIL
 class nsSMILTimeContainer;
 #endif // MOZ_SMIL
 
-#define QI_AND_CAST_TO_NSSVGSVGELEMENT(base)                                  \
-  (nsCOMPtr<nsIDOMSVGSVGElement>(do_QueryInterface(base)) ?                   \
-   static_cast<nsSVGSVGElement*>(base.get()) : nsnull)
-
 typedef nsSVGStylableElement nsSVGSVGElementBase;
 
 class nsSVGSVGElement;
 
 class nsSVGTranslatePoint {
 public:
   nsSVGTranslatePoint(float aX, float aY) :
     mX(aX), mY(aY) {}
@@ -266,16 +262,26 @@ protected:
 
   PRBool IsRoot() {
     NS_ASSERTION((IsInDoc() && !GetParent()) ==
                  (GetOwnerDoc() && (GetOwnerDoc()->GetRootElement() == this)),
                  "Can't determine if we're root");
     return IsInDoc() && !GetParent();
   }
 
+  /**
+   * Returns true if this is an SVG <svg> element that is the child of
+   * another non-foreignObject SVG element.
+   */
+  PRBool IsInner() {
+    const mozilla::dom::Element *parent = nsSVGUtils::GetParentElement(this);
+    return parent && parent->GetNameSpaceID() == kNameSpaceID_SVG &&
+           parent->Tag() != nsGkAtoms::foreignObject;
+  }
+
 #ifdef MOZ_SMIL
   /* 
    * While binding to the tree we need to determine if we will be the outermost
    * <svg> element _before_ the children are bound (as they want to know what
    * timed document root to register with) and therefore _before_ our parent is
    * set (both actions are performed by nsGenericElement::BindToTree) so we
    * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is
    * basically a simplified version of GetOwnerSVGElement that uses the parent
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -218,16 +218,34 @@ nsSVGUtils::GetParentElement(nsIContent 
     }
   }
 
   // otherewise use the explicit one, whether it's null or not...
   nsIContent* parent = aContent->GetParent();
   return parent && parent->IsElement() ? parent->AsElement() : nsnull;
 }
 
+nsSVGSVGElement*
+nsSVGUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
+{
+  nsIContent *element = nsnull;
+  nsIContent *ancestor = GetParentElement(aSVGElement);
+
+  while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG &&
+                     ancestor->Tag() != nsGkAtoms::foreignObject) {
+    element = ancestor;
+    ancestor = GetParentElement(element);
+  }
+
+  if (element && element->Tag() == nsGkAtoms::svg) {
+    return static_cast<nsSVGSVGElement*>(element);
+  }
+  return nsnull;
+}
+
 float
 nsSVGUtils::GetFontSize(Element *aElement)
 {
   if (!aElement)
     return 1.0f;
 
   nsRefPtr<nsStyleContext> styleContext = 
     nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
@@ -456,34 +474,16 @@ nsSVGUtils::GetNearestViewportElement(ns
       }
       return nsCOMPtr<nsIDOMSVGElement>(do_QueryInterface(element)).forget();
     }
     element = GetParentElement(element);
   }
   return nsnull;
 }
 
-already_AddRefed<nsIDOMSVGElement>
-nsSVGUtils::GetFarthestViewportElement(nsIContent *aContent)
-{
-  nsIContent *element = nsnull;
-  nsIContent *ancestor = GetParentElement(aContent);
-
-  while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG &&
-                     ancestor->Tag() != nsGkAtoms::foreignObject) {
-    element = ancestor;
-    ancestor = GetParentElement(element);
-  }
-
-  if (element && element->Tag() == nsGkAtoms::svg) {
-    return nsCOMPtr<nsIDOMSVGElement>(do_QueryInterface(element)).forget();
-  }
-  return nsnull;
-}
-
 gfxMatrix
 nsSVGUtils::GetCTM(nsSVGElement *aElement, PRBool aScreenCTM)
 {
   nsIDocument* currentDoc = aElement->GetCurrentDoc();
   if (currentDoc) {
     // Flush all pending notifications so that our frames are up to date
     currentDoc->FlushPendingNotifications(Flush_Layout);
   }
@@ -1463,27 +1463,16 @@ nsSVGUtils::PathExtentsToMaxStrokeExtent
   double dx = style_expansion * (fabs(ctm.xx) + fabs(ctm.xy));
   double dy = style_expansion * (fabs(ctm.yy) + fabs(ctm.yx));
 
   gfxRect strokeExtents = aPathExtents;
   strokeExtents.Outset(dy, dx, dy, dx);
   return strokeExtents;
 }
 
-/* static */ PRBool
-nsSVGUtils::IsInnerSVG(nsIContent* aContent)
-{
-  if (!aContent->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
-    return PR_FALSE;
-  }
-  nsIContent *ancestor = GetParentElement(aContent);
-  return ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG &&
-                     ancestor->Tag() != nsGkAtoms::foreignObject;
-}
-
 // ----------------------------------------------------------------------
 
 nsSVGRenderState::nsSVGRenderState(nsIRenderingContext *aContext) :
   mRenderMode(NORMAL), mRenderingContext(aContext), mPaintingToWindow(PR_FALSE)
 {
   mGfxContext = aContext->ThebesContext();
 }
 
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -224,16 +224,21 @@ public:
   typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio;
 
   /*
    * Get the parent element of an nsIContent
    */
   static mozilla::dom::Element *GetParentElement(nsIContent *aContent);
 
   /*
+   * Get the outer SVG element of an nsIContent
+   */
+  static nsSVGSVGElement *GetOuterSVGElement(nsSVGElement *aSVGElement);
+
+  /*
    * Get the number of CSS px (user units) per em (i.e. the em-height in user
    * units) for an nsIContent
    *
    * XXX document the conditions under which these may fail, and what they
    * return in those cases.
    */
   static float GetFontSize(mozilla::dom::Element *aElement);
   static float GetFontSize(nsIFrame *aFrame);
@@ -297,19 +302,16 @@ public:
    * Check if this is one of the SVG elements that SVG 1.1 Full says
    * establishes a viewport: svg, symbol, image or foreignObject.
    */
   static PRBool EstablishesViewport(nsIContent *aContent);
 
   static already_AddRefed<nsIDOMSVGElement>
   GetNearestViewportElement(nsIContent *aContent);
 
-  static already_AddRefed<nsIDOMSVGElement>
-  GetFarthestViewportElement(nsIContent *aContent);
-
   /**
    * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame
    * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame,
    * returns nsnull.
    */
   static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame);
   
   /**
@@ -582,22 +584,16 @@ public:
    * fill/path extents.
    *
    * This should die once bug 478152 is fixed.
    */
   static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
                                                nsSVGGeometryFrame* aFrame);
 
   /**
-   * Returns true if aContent is an SVG <svg> element that is the child of
-   * another non-foreignObject SVG element.
-   */
-  static PRBool IsInnerSVG(nsIContent* aContent);
-
-  /**
    * Convert a floating-point value to a 32-bit integer value, clamping to
    * the range of valid integers.
    */
   static PRInt32 ClampToInt(double aVal)
   {
     return NS_lround(NS_MAX(double(PR_INT32_MIN),
                             NS_MIN(double(PR_INT32_MAX), aVal)));
   }