2f27f21cd4619e5cb75d5cae6e854e9072bade77
[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 RenderObject& renderer)
79 {
80     SVGDocumentExtensions* extensions = renderer.document().accessSVGExtensions();
81     ASSERT(extensions);
82     return extensions->resourcesCache();
83 }
84
85 SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject& renderer)
86 {
87     return resourcesCacheFromRenderer(renderer).m_cache.get(&renderer);
88 }
89
90 void SVGResourcesCache::clientLayoutChanged(RenderElement& renderer)
91 {
92     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
93     if (!resources)
94         return;
95
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);
100 }
101
102 static inline bool rendererCanHaveResources(RenderObject& renderer)
103 {
104     return renderer.node() && renderer.node()->isSVGElement() && !renderer.isSVGInlineText();
105 }
106
107 void SVGResourcesCache::clientStyleChanged(RenderElement& renderer, StyleDifference diff, const RenderStyle& newStyle)
108 {
109     if (diff == StyleDifferenceEqual || !renderer.parent())
110         return;
111
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))
114         return;
115
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);
123     }
124
125     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
126
127     if (renderer.element() && !renderer.element()->isSVGElement())
128         renderer.element()->setNeedsStyleRecalc(SyntheticStyleChange);
129 }
130
131 void SVGResourcesCache::clientWasAddedToTree(RenderObject& renderer)
132 {
133     if (renderer.isAnonymous())
134         return;
135
136     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
137
138     if (!rendererCanHaveResources(renderer))
139         return;
140     RenderElement& elementRenderer = toRenderElement(renderer);
141     resourcesCacheFromRenderer(elementRenderer).addResourcesFromRenderer(elementRenderer, elementRenderer.style());
142 }
143
144 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject& renderer)
145 {
146     if (renderer.isAnonymous())
147         return;
148
149     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
150
151     if (!rendererCanHaveResources(renderer))
152         return;
153     RenderElement& elementRenderer = toRenderElement(renderer);
154     resourcesCacheFromRenderer(elementRenderer).removeResourcesFromRenderer(elementRenderer);
155 }
156
157 void SVGResourcesCache::clientDestroyed(RenderElement& renderer)
158 {
159     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
160     if (resources)
161         resources->removeClientFromCache(renderer);
162
163     resourcesCacheFromRenderer(renderer).removeResourcesFromRenderer(renderer);
164 }
165
166 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer& resource)
167 {
168     auto& cache = resourcesCacheFromRenderer(resource);
169
170     // The resource itself may have clients, that need to be notified.
171     cache.removeResourcesFromRenderer(resource);
172
173     for (auto& it : cache.m_cache) {
174         it.value->resourceDestroyed(resource);
175
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();
180
181         extensions->addPendingResource(resourceElement.getIdAttribute(), clientElement);
182     }
183 }
184
185 }