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 "core/rendering/svg/SVGResourcesCache.h" 22 23#include "core/HTMLNames.h" 24#include "core/rendering/svg/RenderSVGResourceContainer.h" 25#include "core/rendering/svg/SVGResources.h" 26#include "core/rendering/svg/SVGResourcesCycleSolver.h" 27#include "core/svg/SVGDocumentExtensions.h" 28 29namespace WebCore { 30 31SVGResourcesCache::SVGResourcesCache() 32{ 33} 34 35SVGResourcesCache::~SVGResourcesCache() 36{ 37} 38 39void SVGResourcesCache::addResourcesFromRenderObject(RenderObject* object, const RenderStyle* style) 40{ 41 ASSERT(object); 42 ASSERT(style); 43 ASSERT(!m_cache.contains(object)); 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 = SVGResources::buildResources(object, svgStyle); 50 if (!newResources) 51 return; 52 53 // Put object in cache. 54 SVGResources* resources = m_cache.set(object, newResources.release()).storedValue->value.get(); 55 56 // Run cycle-detection _afterwards_, so self-references can be caught as well. 57 SVGResourcesCycleSolver solver(object, 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 HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end(); 65 for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) 66 (*it)->addClient(object); 67} 68 69void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object) 70{ 71 OwnPtr<SVGResources> resources = m_cache.take(object); 72 if (!resources) 73 return; 74 75 // Walk resources and register the render object at each resources. 76 HashSet<RenderSVGResourceContainer*> resourceSet; 77 resources->buildSetOfResources(resourceSet); 78 79 HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end(); 80 for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) 81 (*it)->removeClient(object); 82} 83 84static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject* renderer) 85{ 86 Document& document = renderer->document(); 87 88 SVGDocumentExtensions& extensions = document.accessSVGExtensions(); 89 SVGResourcesCache* cache = extensions.resourcesCache(); 90 ASSERT(cache); 91 92 return cache; 93} 94 95SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer) 96{ 97 ASSERT(renderer); 98 return resourcesCacheFromRenderObject(renderer)->m_cache.get(renderer); 99} 100 101void SVGResourcesCache::clientLayoutChanged(RenderObject* object) 102{ 103 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 104 if (!resources) 105 return; 106 107 // Invalidate the resources if either the RenderObject itself changed, 108 // or we have filter resources, which could depend on the layout of children. 109 if (object->selfNeedsLayout() || resources->filter()) 110 resources->removeClientFromCache(object); 111} 112 113static inline bool rendererCanHaveResources(RenderObject* renderer) 114{ 115 ASSERT(renderer); 116 return renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGInlineText(); 117} 118 119void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle) 120{ 121 ASSERT(renderer); 122 ASSERT(renderer->node()); 123 ASSERT(renderer->node()->isSVGElement()); 124 125 if (diff.hasNoChange() || !renderer->parent()) 126 return; 127 128 // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint. 129 if (renderer->isSVGResourceFilterPrimitive() && !diff.needsLayout()) 130 return; 131 132 // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer. 133 // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed 134 // to be able to selectively rebuild individual resources, instead of all of them. 135 if (rendererCanHaveResources(renderer)) { 136 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 137 cache->removeResourcesFromRenderObject(renderer); 138 cache->addResourcesFromRenderObject(renderer, newStyle); 139 } 140 141 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 142} 143 144void SVGResourcesCache::clientWasAddedToTree(RenderObject* renderer, const RenderStyle* newStyle) 145{ 146 if (!renderer->node()) 147 return; 148 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 149 150 if (!rendererCanHaveResources(renderer)) 151 return; 152 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 153 cache->addResourcesFromRenderObject(renderer, newStyle); 154} 155 156void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject* renderer) 157{ 158 if (!renderer->node()) 159 return; 160 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 161 162 if (!rendererCanHaveResources(renderer)) 163 return; 164 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 165 cache->removeResourcesFromRenderObject(renderer); 166} 167 168void SVGResourcesCache::clientDestroyed(RenderObject* renderer) 169{ 170 ASSERT(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 180void 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