Move more of SVG resources cache to using RenderElement.
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGResourcesCache.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #include "config.h"
21 #include "SVGResourcesCache.h"
22
23 #if ENABLE(SVG)
24 #include "HTMLNames.h"
25 #include "RenderSVGResourceContainer.h"
26 #include "SVGDocumentExtensions.h"
27 #include "SVGElement.h"
28 #include "SVGResources.h"
29 #include "SVGResourcesCycleSolver.h"
30
31 namespace WebCore {
32
33 SVGResourcesCache::SVGResourcesCache()
34 {
35 }
36
37 SVGResourcesCache::~SVGResourcesCache()
38 {
39 }
40
41 void SVGResourcesCache::addResourcesFromRenderer(RenderElement& renderer, const RenderStyle& style)
42 {
43     ASSERT(!m_cache.contains(&renderer));
44
45     const SVGRenderStyle* svgStyle = style.svgStyle();
46     ASSERT(svgStyle);
47
48     // Build a list of all resources associated with the passed RenderObject
49     OwnPtr<SVGResources> newResources = adoptPtr(new SVGResources);
50     if (!newResources->buildCachedResources(&renderer, svgStyle))
51         return;
52
53     // Put object in cache.
54     SVGResources* resources = m_cache.add(&renderer, newResources.release()).iterator->value.get();
55
56     // Run cycle-detection _afterwards_, so self-references can be caught as well.
57     SVGResourcesCycleSolver solver(&renderer, resources);
58     solver.resolveCycles();
59
60     // Walk resources and register the render object at each resources.
61     HashSet<RenderSVGResourceContainer*> resourceSet;
62     resources->buildSetOfResources(resourceSet);
63
64     for (auto it = resourceSet.begin(), end = resourceSet.end(); it != end; ++it)
65         (*it)->addClient(&renderer);
66 }
67
68 void SVGResourcesCache::removeResourcesFromRenderer(RenderElement& renderer)
69 {
70     OwnPtr<SVGResources> resources = m_cache.take(&renderer);
71     if (!resources)
72         return;
73
74     // Walk resources and register the render object at each resources.
75     HashSet<RenderSVGResourceContainer*> resourceSet;
76     resources->buildSetOfResources(resourceSet);
77
78     for (auto it = resourceSet.begin(), end = resourceSet.end(); it != end; ++it)
79         (*it)->removeClient(&renderer);
80 }
81
82 static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject& renderer)
83 {
84     SVGDocumentExtensions* extensions = renderer.document().accessSVGExtensions();
85     ASSERT(extensions);
86
87     SVGResourcesCache* cache = extensions->resourcesCache();
88     ASSERT(cache);
89
90     return cache;
91 }
92
93 SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer)
94 {
95     ASSERT(renderer);
96     return resourcesCacheFromRenderObject(*renderer)->m_cache.get(renderer);
97 }
98
99 void SVGResourcesCache::clientLayoutChanged(RenderElement& renderer)
100 {
101     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(&renderer);
102     if (!resources)
103         return;
104
105     // Invalidate the resources if either the RenderElement itself changed,
106     // or we have filter resources, which could depend on the layout of children.
107     if (renderer.selfNeedsLayout())
108         resources->removeClientFromCache(&renderer);
109 }
110
111 static inline bool rendererCanHaveResources(RenderObject& renderer)
112 {
113     return renderer.node() && renderer.node()->isSVGElement() && !renderer.isSVGInlineText();
114 }
115
116 void SVGResourcesCache::clientStyleChanged(RenderElement& renderer, StyleDifference diff, const RenderStyle& newStyle)
117 {
118     if (diff == StyleDifferenceEqual || !renderer.parent())
119         return;
120
121     // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint.
122     if (renderer.isSVGResourceFilterPrimitive() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfTextOrBorderOrOutline))
123         return;
124
125     // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer.
126     // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed
127     // to be able to selectively rebuild individual resources, instead of all of them.
128     if (rendererCanHaveResources(renderer)) {
129         SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
130         cache->removeResourcesFromRenderer(renderer);
131         cache->addResourcesFromRenderer(renderer, newStyle);
132     }
133
134     RenderSVGResource::markForLayoutAndParentResourceInvalidation(&renderer, false);
135
136     if (renderer.element() && !renderer.element()->isSVGElement())
137         renderer.element()->setNeedsStyleRecalc(SyntheticStyleChange);
138 }
139
140 void SVGResourcesCache::clientWasAddedToTree(RenderObject& renderer)
141 {
142     if (renderer.isAnonymous())
143         return;
144
145     RenderSVGResource::markForLayoutAndParentResourceInvalidation(&renderer, false);
146
147     if (!rendererCanHaveResources(renderer))
148         return;
149     RenderElement& elementRenderer = toRenderElement(renderer);
150     SVGResourcesCache* cache = resourcesCacheFromRenderObject(elementRenderer);
151     cache->addResourcesFromRenderer(elementRenderer, elementRenderer.style());
152 }
153
154 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject& renderer)
155 {
156     if (renderer.isAnonymous())
157         return;
158
159     RenderSVGResource::markForLayoutAndParentResourceInvalidation(&renderer, false);
160
161     if (!rendererCanHaveResources(renderer))
162         return;
163     RenderElement& elementRenderer = toRenderElement(renderer);
164     SVGResourcesCache* cache = resourcesCacheFromRenderObject(elementRenderer);
165     cache->removeResourcesFromRenderer(elementRenderer);
166 }
167
168 void SVGResourcesCache::clientDestroyed(RenderElement& renderer)
169 {
170     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(&renderer);
171     if (resources)
172         resources->removeClientFromCache(&renderer);
173
174     SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
175     cache->removeResourcesFromRenderer(renderer);
176 }
177
178 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer& resource)
179 {
180     SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource);
181
182     // The resource itself may have clients, that need to be notified.
183     cache->removeResourcesFromRenderer(resource);
184
185     for (auto it = cache->m_cache.begin(), end = cache->m_cache.end(); it != end; ++it) {
186         it->value->resourceDestroyed(resource);
187
188         // Mark users of destroyed resources as pending resolution based on the id of the old resource.
189         Element& resourceElement = resource.element();
190         Element* clientElement = toElement(it->key->node());
191         SVGDocumentExtensions* extensions = clientElement->document().accessSVGExtensions();
192
193         extensions->addPendingResource(resourceElement.getIdAttribute(), clientElement);
194     }
195 }
196
197 }
198
199 #endif