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 28namespace WebCore { 29 30SVGResourcesCache::SVGResourcesCache() 31{ 32} 33 34SVGResourcesCache::~SVGResourcesCache() 35{ 36} 37 38void 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 64void 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 78static inline SVGResourcesCache& resourcesCacheFromRenderer(const RenderObject& renderer) 79{ 80 SVGDocumentExtensions* extensions = renderer.document().accessSVGExtensions(); 81 ASSERT(extensions); 82 return extensions->resourcesCache(); 83} 84 85SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject& renderer) 86{ 87 return resourcesCacheFromRenderer(renderer).m_cache.get(&renderer); 88} 89 90void 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 102static inline bool rendererCanHaveResources(RenderObject& renderer) 103{ 104 return renderer.node() && renderer.node()->isSVGElement() && !renderer.isSVGInlineText(); 105} 106 107void 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 131void 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 144void 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 157void 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 166void 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} 186