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 "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 WebCore { 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 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()).iterator->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 69 void 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 84 static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject* renderer) 85 { 86 Document& document = renderer->document(); 87 88 SVGDocumentExtensions* extensions = document.accessSVGExtensions(); 89 ASSERT(extensions); 90 91 SVGResourcesCache* cache = extensions->resourcesCache(); 92 ASSERT(cache); 93 94 return cache; 95 } 96 97 SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer) 98 { 99 ASSERT(renderer); 100 return resourcesCacheFromRenderObject(renderer)->m_cache.get(renderer); 101 } 102 103 void SVGResourcesCache::clientLayoutChanged(RenderObject* object) 104 { 105 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 106 if (!resources) 107 return; 108 109 // Invalidate the resources if either the RenderObject itself changed, 110 // or we have filter resources, which could depend on the layout of children. 111 if (object->selfNeedsLayout() || resources->filter()) 112 resources->removeClientFromCache(object); 113 } 114 115 static inline bool rendererCanHaveResources(RenderObject* renderer) 116 { 117 ASSERT(renderer); 118 return renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGInlineText(); 119 } 120 121 void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle) 122 { 123 ASSERT(renderer); 124 ASSERT(renderer->node()); 125 ASSERT(renderer->node()->isSVGElement()); 126 127 if (diff == StyleDifferenceEqual || !renderer->parent()) 128 return; 129 130 // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint. 131 if (renderer->isSVGResourceFilterPrimitive() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfTextOrColorChange)) 132 return; 133 134 // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer. 135 // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed 136 // to be able to selectively rebuild individual resources, instead of all of them. 137 if (rendererCanHaveResources(renderer)) { 138 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 139 cache->removeResourcesFromRenderObject(renderer); 140 cache->addResourcesFromRenderObject(renderer, newStyle); 141 } 142 143 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 144 } 145 146 void SVGResourcesCache::clientWasAddedToTree(RenderObject* renderer, const RenderStyle* newStyle) 147 { 148 if (!renderer->node()) 149 return; 150 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 151 152 if (!rendererCanHaveResources(renderer)) 153 return; 154 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 155 cache->addResourcesFromRenderObject(renderer, newStyle); 156 } 157 158 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject* renderer) 159 { 160 if (!renderer->node()) 161 return; 162 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false); 163 164 if (!rendererCanHaveResources(renderer)) 165 return; 166 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 167 cache->removeResourcesFromRenderObject(renderer); 168 } 169 170 void SVGResourcesCache::clientDestroyed(RenderObject* renderer) 171 { 172 ASSERT(renderer); 173 174 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 175 if (resources) 176 resources->removeClientFromCache(renderer); 177 178 SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer); 179 cache->removeResourcesFromRenderObject(renderer); 180 } 181 182 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource) 183 { 184 ASSERT(resource); 185 SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource); 186 187 // The resource itself may have clients, that need to be notified. 188 cache->removeResourcesFromRenderObject(resource); 189 190 CacheMap::iterator end = cache->m_cache.end(); 191 for (CacheMap::iterator it = cache->m_cache.begin(); it != end; ++it) { 192 it->value->resourceDestroyed(resource); 193 194 // Mark users of destroyed resources as pending resolution based on the id of the old resource. 195 Element* resourceElement = resource->element(); 196 Element* clientElement = toElement(it->key->node()); 197 SVGDocumentExtensions* extensions = clientElement->document().accessSVGExtensions(); 198 199 extensions->addPendingResource(resourceElement->fastGetAttribute(HTMLNames::idAttr), clientElement); 200 } 201 } 202 203 } 204