Have Document::accessSVGExtensions() return a reference
[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 #include "HTMLNames.h"
24 #include "RenderSVGResourceContainer.h"
25 #include "SVGResources.h"
26 #include "SVGResourcesCycleSolver.h"
27
28 namespace WebCore {
29
30 SVGResourcesCache::SVGResourcesCache()
31 {
32 }
33
34 SVGResourcesCache::~SVGResourcesCache()
35 {
36 }
37
38 void SVGResourcesCache::addResourcesFromRenderer(RenderElement& renderer, const RenderStyle& style)
39 {
40     ASSERT(!m_cache.contains(&renderer));
41
42     const SVGRenderStyle& svgStyle = style.svgStyle();
43
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))
47         return;
48
49     // Put object in cache.
50     SVGResources& resources = *m_cache.add(&renderer, WTF::move(newResources)).iterator->value;
51
52     // Run cycle-detection _afterwards_, so self-references can be caught as well.
53     SVGResourcesCycleSolver solver(renderer, resources);
54     solver.resolveCycles();
55
56     // Walk resources and register the render object at each resources.
57     HashSet<RenderSVGResourceContainer*> resourceSet;
58     resources.buildSetOfResources(resourceSet);
59
60     for (auto* resourceContainer : resourceSet)
61         resourceContainer->addClient(renderer);
62 }
63
64 void SVGResourcesCache::removeResourcesFromRenderer(RenderElement& renderer)
65 {
66     std::unique_ptr<SVGResources> resources = m_cache.take(&renderer);
67     if (!resources)
68         return;
69
70     // Walk resources and register the render object at each resources.
71     HashSet<RenderSVGResourceContainer*> resourceSet;
72     resources->buildSetOfResources(resourceSet);
73
74     for (auto* resourceContainer : resourceSet)
75         resourceContainer->removeClient(renderer);
76 }
77
78 static inline SVGResourcesCache& resourcesCacheFromRenderer(const RenderElement& renderer)
79 {
80     return renderer.document().accessSVGExtensions().resourcesCache();
81 }
82
83 SVGResources* SVGResourcesCache::cachedResourcesForRenderer(const RenderElement& renderer)
84 {
85     return resourcesCacheFromRenderer(renderer).m_cache.get(&renderer);
86 }
87
88 void SVGResourcesCache::clientLayoutChanged(RenderElement& renderer)
89 {
90     auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer);
91     if (!resources)
92         return;
93
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);
98 }
99
100 static inline bool rendererCanHaveResources(RenderObject& renderer)
101 {
102     return renderer.node() && renderer.node()->isSVGElement() && !renderer.isSVGInlineText();
103 }
104
105 void SVGResourcesCache::clientStyleChanged(RenderElement& renderer, StyleDifference diff, const RenderStyle& newStyle)
106 {
107     if (diff == StyleDifferenceEqual || !renderer.parent())
108         return;
109
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))
112         return;
113
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);
121     }
122
123     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
124
125     if (renderer.element() && !renderer.element()->isSVGElement())
126         renderer.element()->setNeedsStyleRecalc(SyntheticStyleChange);
127 }
128
129 void SVGResourcesCache::clientWasAddedToTree(RenderObject& renderer)
130 {
131     if (renderer.isAnonymous())
132         return;
133
134     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
135
136     if (!rendererCanHaveResources(renderer))
137         return;
138     RenderElement& elementRenderer = toRenderElement(renderer);
139     resourcesCacheFromRenderer(elementRenderer).addResourcesFromRenderer(elementRenderer, elementRenderer.style());
140 }
141
142 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject& renderer)
143 {
144     if (renderer.isAnonymous())
145         return;
146
147     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
148
149     if (!rendererCanHaveResources(renderer))
150         return;
151     RenderElement& elementRenderer = toRenderElement(renderer);
152     resourcesCacheFromRenderer(elementRenderer).removeResourcesFromRenderer(elementRenderer);
153 }
154
155 void SVGResourcesCache::clientDestroyed(RenderElement& renderer)
156 {
157     if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer))
158         resources->removeClientFromCache(renderer);
159
160     resourcesCacheFromRenderer(renderer).removeResourcesFromRenderer(renderer);
161 }
162
163 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer& resource)
164 {
165     auto& cache = resourcesCacheFromRenderer(resource);
166
167     // The resource itself may have clients, that need to be notified.
168     cache.removeResourcesFromRenderer(resource);
169
170     for (auto& it : cache.m_cache) {
171         it.value->resourceDestroyed(resource);
172
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);
177     }
178 }
179
180 }