2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 #include "SVGResourcesCache.h"
23 #include "HTMLNames.h"
24 #include "RenderSVGResourceContainer.h"
25 #include "SVGResources.h"
26 #include "SVGResourcesCycleSolver.h"
30 SVGResourcesCache::SVGResourcesCache()
34 SVGResourcesCache::~SVGResourcesCache()
38 void SVGResourcesCache::addResourcesFromRenderer(RenderElement& renderer, const RenderStyle& style)
40 ASSERT(!m_cache.contains(&renderer));
42 const SVGRenderStyle& svgStyle = style.svgStyle();
44 // Build a list of all resources associated with the passed RenderObject
45 auto newResources = std::make_unique<SVGResources>();
46 if (!newResources->buildCachedResources(renderer, svgStyle))
49 // Put object in cache.
50 SVGResources& resources = *m_cache.add(&renderer, WTF::move(newResources)).iterator->value;
52 // Run cycle-detection _afterwards_, so self-references can be caught as well.
53 SVGResourcesCycleSolver solver(renderer, resources);
54 solver.resolveCycles();
56 // Walk resources and register the render object at each resources.
57 HashSet<RenderSVGResourceContainer*> resourceSet;
58 resources.buildSetOfResources(resourceSet);
60 for (auto* resourceContainer : resourceSet)
61 resourceContainer->addClient(renderer);
64 void SVGResourcesCache::removeResourcesFromRenderer(RenderElement& renderer)
66 std::unique_ptr<SVGResources> resources = m_cache.take(&renderer);
70 // Walk resources and register the render object at each resources.
71 HashSet<RenderSVGResourceContainer*> resourceSet;
72 resources->buildSetOfResources(resourceSet);
74 for (auto* resourceContainer : resourceSet)
75 resourceContainer->removeClient(renderer);
78 static inline SVGResourcesCache& resourcesCacheFromRenderer(const RenderElement& renderer)
80 return renderer.document().accessSVGExtensions().resourcesCache();
83 SVGResources* SVGResourcesCache::cachedResourcesForRenderer(const RenderElement& renderer)
85 return resourcesCacheFromRenderer(renderer).m_cache.get(&renderer);
88 void SVGResourcesCache::clientLayoutChanged(RenderElement& renderer)
90 auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer);
94 // Invalidate the resources if either the RenderElement itself changed,
95 // or we have filter resources, which could depend on the layout of children.
96 if (renderer.selfNeedsLayout())
97 resources->removeClientFromCache(renderer);
100 static inline bool rendererCanHaveResources(RenderObject& renderer)
102 return renderer.node() && renderer.node()->isSVGElement() && !renderer.isSVGInlineText();
105 void SVGResourcesCache::clientStyleChanged(RenderElement& renderer, StyleDifference diff, const RenderStyle& newStyle)
107 if (diff == StyleDifferenceEqual || !renderer.parent())
110 // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint.
111 if (renderer.isSVGResourceFilterPrimitive() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfTextOrBorderOrOutline))
114 // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer.
115 // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed
116 // to be able to selectively rebuild individual resources, instead of all of them.
117 if (rendererCanHaveResources(renderer)) {
118 auto& cache = resourcesCacheFromRenderer(renderer);
119 cache.removeResourcesFromRenderer(renderer);
120 cache.addResourcesFromRenderer(renderer, newStyle);
123 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
125 if (renderer.element() && !renderer.element()->isSVGElement())
126 renderer.element()->setNeedsStyleRecalc(SyntheticStyleChange);
129 void SVGResourcesCache::clientWasAddedToTree(RenderObject& renderer)
131 if (renderer.isAnonymous())
134 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
136 if (!rendererCanHaveResources(renderer))
138 RenderElement& elementRenderer = toRenderElement(renderer);
139 resourcesCacheFromRenderer(elementRenderer).addResourcesFromRenderer(elementRenderer, elementRenderer.style());
142 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject& renderer)
144 if (renderer.isAnonymous())
147 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
149 if (!rendererCanHaveResources(renderer))
151 RenderElement& elementRenderer = toRenderElement(renderer);
152 resourcesCacheFromRenderer(elementRenderer).removeResourcesFromRenderer(elementRenderer);
155 void SVGResourcesCache::clientDestroyed(RenderElement& renderer)
157 if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer))
158 resources->removeClientFromCache(renderer);
160 resourcesCacheFromRenderer(renderer).removeResourcesFromRenderer(renderer);
163 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer& resource)
165 auto& cache = resourcesCacheFromRenderer(resource);
167 // The resource itself may have clients, that need to be notified.
168 cache.removeResourcesFromRenderer(resource);
170 for (auto& it : cache.m_cache) {
171 it.value->resourceDestroyed(resource);
173 // Mark users of destroyed resources as pending resolution based on the id of the old resource.
174 Element& resourceElement = resource.element();
175 Element* clientElement = it.key->element();
176 clientElement->document().accessSVGExtensions().addPendingResource(resourceElement.getIdAttribute(), clientElement);