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