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/SVGResourcesCycleSolver.h" 22 23 // Set to a value > 0, to debug the resource cache. 24 #define DEBUG_CYCLE_DETECTION 0 25 26 #include "core/rendering/svg/RenderSVGResourceClipper.h" 27 #include "core/rendering/svg/RenderSVGResourceFilter.h" 28 #include "core/rendering/svg/RenderSVGResourceMarker.h" 29 #include "core/rendering/svg/RenderSVGResourceMasker.h" 30 #include "core/rendering/svg/SVGResources.h" 31 #include "core/rendering/svg/SVGResourcesCache.h" 32 #include "core/svg/SVGFilterElement.h" 33 #include "core/svg/SVGGradientElement.h" 34 #include "core/svg/SVGPatternElement.h" 35 36 namespace WebCore { 37 38 SVGResourcesCycleSolver::SVGResourcesCycleSolver(RenderObject* renderer, SVGResources* resources) 39 : m_renderer(renderer) 40 , m_resources(resources) 41 { 42 ASSERT(m_renderer); 43 ASSERT(m_resources); 44 } 45 46 SVGResourcesCycleSolver::~SVGResourcesCycleSolver() 47 { 48 } 49 50 bool SVGResourcesCycleSolver::resourceContainsCycles(RenderObject* renderer) const 51 { 52 ASSERT(renderer); 53 54 // First operate on the resources of the given renderer. 55 // <marker id="a"> <path marker-start="url(#b)"/> ... 56 // <marker id="b" marker-start="url(#a)"/> 57 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer)) { 58 HashSet<RenderSVGResourceContainer*> resourceSet; 59 resources->buildSetOfResources(resourceSet); 60 61 // Walk all resources and check wheter they reference any resource contained in the resources set. 62 HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end(); 63 for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) { 64 if (m_allResources.contains(*it)) 65 return true; 66 } 67 } 68 69 // Then operate on the child resources of the given renderer. 70 // <marker id="a"> <path marker-start="url(#b)"/> ... 71 // <marker id="b"> <path marker-start="url(#a)"/> ... 72 for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) { 73 SVGResources* childResources = SVGResourcesCache::cachedResourcesForRenderObject(child); 74 if (!childResources) 75 continue; 76 77 // A child of the given 'resource' contains resources. 78 HashSet<RenderSVGResourceContainer*> childSet; 79 childResources->buildSetOfResources(childSet); 80 81 // Walk all child resources and check wheter they reference any resource contained in the resources set. 82 HashSet<RenderSVGResourceContainer*>::iterator end = childSet.end(); 83 for (HashSet<RenderSVGResourceContainer*>::iterator it = childSet.begin(); it != end; ++it) { 84 if (m_allResources.contains(*it)) 85 return true; 86 } 87 88 // Walk children recursively, stop immediately if we found a cycle 89 if (resourceContainsCycles(child)) 90 return true; 91 } 92 93 return false; 94 } 95 96 void SVGResourcesCycleSolver::resolveCycles() 97 { 98 ASSERT(m_allResources.isEmpty()); 99 100 #if DEBUG_CYCLE_DETECTION > 0 101 fprintf(stderr, "\nBefore cycle detection:\n"); 102 m_resources->dump(m_renderer); 103 #endif 104 105 // Stash all resources into a HashSet for the ease of traversing. 106 HashSet<RenderSVGResourceContainer*> localResources; 107 m_resources->buildSetOfResources(localResources); 108 ASSERT(!localResources.isEmpty()); 109 110 // Add all parent resource containers to the HashSet. 111 HashSet<RenderSVGResourceContainer*> parentResources; 112 RenderObject* parent = m_renderer->parent(); 113 while (parent) { 114 if (parent->isSVGResourceContainer()) 115 parentResources.add(parent->toRenderSVGResourceContainer()); 116 parent = parent->parent(); 117 } 118 119 #if DEBUG_CYCLE_DETECTION > 0 120 fprintf(stderr, "\nDetecting wheter any resources references any of following objects:\n"); 121 { 122 fprintf(stderr, "Local resources:\n"); 123 HashSet<RenderSVGResourceContainer*>::iterator end = localResources.end(); 124 for (HashSet<RenderSVGResourceContainer*>::iterator it = localResources.begin(); it != end; ++it) 125 fprintf(stderr, "|> %s: object=%p (node=%p)\n", (*it)->renderName(), *it, (*it)->node()); 126 127 fprintf(stderr, "Parent resources:\n"); 128 end = parentResources.end(); 129 for (HashSet<RenderSVGResourceContainer*>::iterator it = parentResources.begin(); it != end; ++it) 130 fprintf(stderr, "|> %s: object=%p (node=%p)\n", (*it)->renderName(), *it, (*it)->node()); 131 } 132 #endif 133 134 // Build combined set of local and parent resources. 135 m_allResources = localResources; 136 HashSet<RenderSVGResourceContainer*>::iterator end = parentResources.end(); 137 for (HashSet<RenderSVGResourceContainer*>::iterator it = parentResources.begin(); it != end; ++it) 138 m_allResources.add(*it); 139 140 // If we're a resource, add ourselves to the HashSet. 141 if (m_renderer->isSVGResourceContainer()) 142 m_allResources.add(m_renderer->toRenderSVGResourceContainer()); 143 144 ASSERT(!m_allResources.isEmpty()); 145 146 // The job of this function is to determine wheter any of the 'resources' associated with the given 'renderer' 147 // references us (or wheter any of its kids references us) -> that's a cycle, we need to find and break it. 148 end = localResources.end(); 149 for (HashSet<RenderSVGResourceContainer*>::iterator it = localResources.begin(); it != end; ++it) { 150 RenderSVGResourceContainer* resource = *it; 151 if (parentResources.contains(resource) || resourceContainsCycles(resource)) 152 breakCycle(resource); 153 } 154 155 #if DEBUG_CYCLE_DETECTION > 0 156 fprintf(stderr, "\nAfter cycle detection:\n"); 157 m_resources->dump(m_renderer); 158 #endif 159 160 m_allResources.clear(); 161 } 162 163 void SVGResourcesCycleSolver::breakCycle(RenderSVGResourceContainer* resourceLeadingToCycle) 164 { 165 ASSERT(resourceLeadingToCycle); 166 if (resourceLeadingToCycle == m_resources->linkedResource()) { 167 m_resources->resetLinkedResource(); 168 return; 169 } 170 171 switch (resourceLeadingToCycle->resourceType()) { 172 case MaskerResourceType: 173 ASSERT(resourceLeadingToCycle == m_resources->masker()); 174 m_resources->resetMasker(); 175 break; 176 case MarkerResourceType: 177 ASSERT(resourceLeadingToCycle == m_resources->markerStart() || resourceLeadingToCycle == m_resources->markerMid() || resourceLeadingToCycle == m_resources->markerEnd()); 178 if (m_resources->markerStart() == resourceLeadingToCycle) 179 m_resources->resetMarkerStart(); 180 if (m_resources->markerMid() == resourceLeadingToCycle) 181 m_resources->resetMarkerMid(); 182 if (m_resources->markerEnd() == resourceLeadingToCycle) 183 m_resources->resetMarkerEnd(); 184 break; 185 case PatternResourceType: 186 case LinearGradientResourceType: 187 case RadialGradientResourceType: 188 ASSERT(resourceLeadingToCycle == m_resources->fill() || resourceLeadingToCycle == m_resources->stroke()); 189 if (m_resources->fill() == resourceLeadingToCycle) 190 m_resources->resetFill(); 191 if (m_resources->stroke() == resourceLeadingToCycle) 192 m_resources->resetStroke(); 193 break; 194 case FilterResourceType: 195 ASSERT(resourceLeadingToCycle == m_resources->filter()); 196 m_resources->resetFilter(); 197 break; 198 case ClipperResourceType: 199 ASSERT(resourceLeadingToCycle == m_resources->clipper()); 200 m_resources->resetClipper(); 201 break; 202 case SolidColorResourceType: 203 ASSERT_NOT_REACHED(); 204 break; 205 } 206 } 207 208 } 209