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 29 namespace blink { 30 31 SVGResourcesCache::SVGResourcesCache() 32 { 33 } 34 35 SVGResourcesCache::~SVGResourcesCache() 36 { 37 } 38 39 void 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 47 // Build a list of all resources associated with the passed RenderObject 48 OwnPtr<SVGResources> newResources = SVGResources::buildResources(object, svgStyle); 49 if (!newResources) 50 return; 51 52 // Put object in cache. 53 SVGResources* resources = m_cache.set(object, newResources.release()).storedValue->value.get(); 54 55 // Run cycle-detection _afterwards_, so self-references can be caught as well. 56 SVGResourcesCycleSolver solver(object, resources); 57 solver.resolveCycles(); 58 59 // Walk resources and register the render object at each resources. 60 HashSet<RenderSVGResourceContainer*> resourceSet; 61 resources->buildSetOfResources(resourceSet); 62 63 HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end(); 64 for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) 65 (*it)->addClient(object); 66 } 67 68 void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object) 69 { 70 OwnPtr<SVGResources> resources = m_cache.take(object); 71 if (!resources) 72 return; 73 74 // Walk resources and register the render object at each resources. 75 HashSet<RenderSVGResourceContainer*> resourceSet; 76 resources->buildSetOfResources(resourceSet); 77 78 HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end(); 79 for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) 80 (*it)->removeClient(object); 81 } 82 83 static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject* renderer) 84 { 85 Document& document = renderer->document(); 86 87 SVGDocumentExtensions& extensions = document.accessSVGExtensions(); 88 SVGResourcesCache* cache = extensions.resourcesCache(); 89 ASSERT(cache); 90 91 return cache; 92 } 93 94 SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer) 95 { 96 ASSERT(renderer); 97 return resourcesCacheFromRenderObject(renderer)->m_cache.get(renderer); 98 } 99 100 void SVGResourcesCache::clientLayoutChanged(RenderObject* object) 101 { 102 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 103 if (!resources) 104 return; 105 106 // Invalidate the resources if either the RenderObject itself changed, 107 // or we have filter resources, which could depend on the layout of children. 108 if (object->selfNeedsLayout() || resources->filter()) 109 resources->removeClientFromCache(object); 110 } 111 112 static inline bool rendererCanHaveResources(RenderObject* renderer) 113 { 114 ASSERT(renderer); 115 return renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGInlineText(); 116 } 117 118 void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle) 119 { 120 ASSERT(renderer); 121 ASSERT(renderer->node()); 122 ASSERT(renderer->node()->isSVGElement()); 123 124 if (!diff.hasDifference() || !renderer->parent()) 125 return; 126 127 // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or paintInvalidation. 128 if (renderer->isSVGResourceFilterPrimitive() && !diff.needsLayout()) 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 143 void SVGResourcesCache::clientWasAddedToTree(RenderObject* renderer, const RenderStyle* newStyle) 144 { 145 if (!renderer->node()) 146 return; 147 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 148 149 if (!rendererCanHaveResources(renderer)) 150 return; 151 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 152 cache->addResourcesFromRenderObject(renderer, newStyle); 153 } 154 155 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject* renderer) 156 { 157 if (!renderer->node()) 158 return; 159 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 160 161 if (!rendererCanHaveResources(renderer)) 162 return; 163 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 164 cache->removeResourcesFromRenderObject(renderer); 165 } 166 167 void SVGResourcesCache::clientDestroyed(RenderObject* renderer) 168 { 169 ASSERT(renderer); 170 171 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 172 if (resources) 173 resources->removeClientFromCache(renderer); 174 175 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 176 cache->removeResourcesFromRenderObject(renderer); 177 } 178 179 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource) 180 { 181 ASSERT(resource); 182 SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource); 183 184 // The resource itself may have clients, that need to be notified. 185 cache->removeResourcesFromRenderObject(resource); 186 187 CacheMap::iterator end = cache->m_cache.end(); 188 for (CacheMap::iterator it = cache->m_cache.begin(); it != end; ++it) { 189 it->value->resourceDestroyed(resource); 190 191 // Mark users of destroyed resources as pending resolution based on the id of the old resource. 192 Element* resourceElement = resource->element(); 193 Element* clientElement = toElement(it->key->node()); 194 SVGDocumentExtensions& extensions = clientElement->document().accessSVGExtensions(); 195 196 extensions.addPendingResource(resourceElement->fastGetAttribute(HTMLNames::idAttr), clientElement); 197 } 198 } 199 200 } 201