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 RenderObject& renderer)
80 SVGDocumentExtensions* extensions = renderer.document().accessSVGExtensions();
82 return extensions->resourcesCache();
85 SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject& renderer)
87 return resourcesCacheFromRenderer(renderer).m_cache.get(&renderer);
90 void SVGResourcesCache::clientLayoutChanged(RenderElement& renderer)
92 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
96 // Invalidate the resources if either the RenderElement itself changed,
97 // or we have filter resources, which could depend on the layout of children.
98 if (renderer.selfNeedsLayout())
99 resources->removeClientFromCache(renderer);
102 static inline bool rendererCanHaveResources(RenderObject& renderer)
104 return renderer.node() && renderer.node()->isSVGElement() && !renderer.isSVGInlineText();
107 void SVGResourcesCache::clientStyleChanged(RenderElement& renderer, StyleDifference diff, const RenderStyle& newStyle)
109 if (diff == StyleDifferenceEqual || !renderer.parent())
112 // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint.
113 if (renderer.isSVGResourceFilterPrimitive() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfTextOrBorderOrOutline))
116 // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer.
117 // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed
118 // to be able to selectively rebuild individual resources, instead of all of them.
119 if (rendererCanHaveResources(renderer)) {
120 auto& cache = resourcesCacheFromRenderer(renderer);
121 cache.removeResourcesFromRenderer(renderer);
122 cache.addResourcesFromRenderer(renderer, newStyle);
125 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
127 if (renderer.element() && !renderer.element()->isSVGElement())
128 renderer.element()->setNeedsStyleRecalc(SyntheticStyleChange);
131 void SVGResourcesCache::clientWasAddedToTree(RenderObject& renderer)
133 if (renderer.isAnonymous())
136 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
138 if (!rendererCanHaveResources(renderer))
140 RenderElement& elementRenderer = toRenderElement(renderer);
141 resourcesCacheFromRenderer(elementRenderer).addResourcesFromRenderer(elementRenderer, elementRenderer.style());
144 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject& renderer)
146 if (renderer.isAnonymous())
149 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
151 if (!rendererCanHaveResources(renderer))
153 RenderElement& elementRenderer = toRenderElement(renderer);
154 resourcesCacheFromRenderer(elementRenderer).removeResourcesFromRenderer(elementRenderer);
157 void SVGResourcesCache::clientDestroyed(RenderElement& renderer)
159 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
161 resources->removeClientFromCache(renderer);
163 resourcesCacheFromRenderer(renderer).removeResourcesFromRenderer(renderer);
166 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer& resource)
168 auto& cache = resourcesCacheFromRenderer(resource);
170 // The resource itself may have clients, that need to be notified.
171 cache.removeResourcesFromRenderer(resource);
173 for (auto& it : cache.m_cache) {
174 it.value->resourceDestroyed(resource);
176 // Mark users of destroyed resources as pending resolution based on the id of the old resource.
177 Element& resourceElement = resource.element();
178 Element* clientElement = toElement(it.key->node());
179 SVGDocumentExtensions* extensions = clientElement->document().accessSVGExtensions();
181 extensions->addPendingResource(resourceElement.getIdAttribute(), clientElement);