[SVG] Update of element referenced by multiple 'use' nodes is absurdly slow
authorfmalita@chromium.org <fmalita@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Feb 2013 21:43:29 +0000 (21:43 +0000)
committerfmalita@chromium.org <fmalita@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Feb 2013 21:43:29 +0000 (21:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=97905

Reviewed by Dirk Schulze.

Rebuilding the shadow and instance trees of dependent use nodes recursively can be
extremely inefficient with non-trivial dependency graphs (the trees are not constructed
in topological order).

To avoid redundant buildShadowAndInstanceTree() invokations, separate the invalidation
phase from the actual tree (re)building phase, and only descend into the dependency DAG
during the invalidation phase (recursion also stops at nodes that have been previously
invalidated).

No new tests: functional coverage provided by existing tests, perfomance tracked by
PerformanceTests/SVG/SvgNestedUse.html.

* svg/SVGUseElement.cpp:
(WebCore::SVGUseElement::buildPendingResource):
(WebCore::SVGUseElement::buildShadowAndInstanceTree):
(WebCore::SVGUseElement::invalidateShadowTree):
(WebCore):
(WebCore::SVGUseElement::invalidateDependentShadowTrees):
* svg/SVGUseElement.h:
(SVGUseElement):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@143498 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/svg/SVGUseElement.cpp
Source/WebCore/svg/SVGUseElement.h

index 6bf5e43..10bffb3 100644 (file)
@@ -1,3 +1,31 @@
+2013-02-20  Florin Malita  <fmalita@chromium.org>
+
+        [SVG] Update of element referenced by multiple 'use' nodes is absurdly slow
+        https://bugs.webkit.org/show_bug.cgi?id=97905
+
+        Reviewed by Dirk Schulze.
+
+        Rebuilding the shadow and instance trees of dependent use nodes recursively can be
+        extremely inefficient with non-trivial dependency graphs (the trees are not constructed
+        in topological order).
+
+        To avoid redundant buildShadowAndInstanceTree() invokations, separate the invalidation
+        phase from the actual tree (re)building phase, and only descend into the dependency DAG
+        during the invalidation phase (recursion also stops at nodes that have been previously
+        invalidated).
+
+        No new tests: functional coverage provided by existing tests, perfomance tracked by
+        PerformanceTests/SVG/SvgNestedUse.html.
+
+        * svg/SVGUseElement.cpp:
+        (WebCore::SVGUseElement::buildPendingResource):
+        (WebCore::SVGUseElement::buildShadowAndInstanceTree):
+        (WebCore::SVGUseElement::invalidateShadowTree):
+        (WebCore):
+        (WebCore::SVGUseElement::invalidateDependentShadowTrees):
+        * svg/SVGUseElement.h:
+        (SVGUseElement):
+
 2013-02-20  Tim Horton  <timothy_horton@apple.com>
 
         RenderLayerBacking should initialize TiledBacking's isInWindow state with Page's isInWindow, not isOnscreen
index 0b72cf6..0f296b9 100644 (file)
@@ -409,10 +409,10 @@ void SVGUseElement::clearResourceReferences()
 
 void SVGUseElement::buildPendingResource()
 {
-    if (!referencedDocument())
+    if (!referencedDocument() || isInShadowTree())
         return;
     clearResourceReferences();
-    if (!inDocument() || isInShadowTree())
+    if (!inDocument())
         return;
 
     String id;
@@ -430,8 +430,11 @@ void SVGUseElement::buildPendingResource()
         return;
     }
 
-    if (target->isSVGElement())
+    if (target->isSVGElement()) {
         buildShadowAndInstanceTree(static_cast<SVGElement*>(target));
+        invalidateDependentShadowTrees();
+    }
+
     ASSERT(!m_needsShadowTreeRecreation);
 }
 
@@ -517,10 +520,6 @@ void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target)
     // Update relative length information.
     updateRelativeLengthsInformation();
 
-    // Rebuild all dependent use elements.
-    ASSERT(document());
-    document()->accessSVGExtensions()->rebuildAllElementReferencesForTarget(this);
-
     // Eventually dump instance tree
 #ifdef DUMP_INSTANCE_TREE
     String text;
@@ -906,6 +905,20 @@ void SVGUseElement::invalidateShadowTree()
         return;
     m_needsShadowTreeRecreation = true;
     setNeedsStyleRecalc();
+    invalidateDependentShadowTrees();
+}
+
+void SVGUseElement::invalidateDependentShadowTrees()
+{
+    // Recursively invalidate dependent <use> shadow trees
+    const HashSet<SVGElementInstance*>& instances = instancesForElement();
+    const HashSet<SVGElementInstance*>::const_iterator end = instances.end();
+    for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) {
+        if (SVGUseElement* element = (*it)->correspondingUseElement()) {
+            ASSERT(element->inDocument());
+            element->invalidateShadowTree();
+        }
+    }
 }
 
 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
index b7d0966..a7a1eaf 100644 (file)
@@ -50,6 +50,7 @@ public:
     SVGElementInstance* animatedInstanceRoot() const;
     SVGElementInstance* instanceForShadowTreeElement(Node*) const;
     void invalidateShadowTree();
+    void invalidateDependentShadowTrees();
 
     RenderObject* rendererClipChild() const;