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 blink { 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 struct ActiveFrame { 51 typedef SVGResourcesCycleSolver::ResourceSet ResourceSet; 52 53 ActiveFrame(ResourceSet& activeSet, RenderSVGResourceContainer* resource) 54 : m_activeSet(activeSet) 55 , m_resource(resource) 56 { 57 m_activeSet.add(m_resource); 58 } 59 ~ActiveFrame() 60 { 61 m_activeSet.remove(m_resource); 62 } 63 64 ResourceSet& m_activeSet; 65 RenderSVGResourceContainer* m_resource; 66 }; 67 68 bool SVGResourcesCycleSolver::resourceContainsCycles(RenderSVGResourceContainer* resource) 69 { 70 // If we've traversed this sub-graph before and no cycles were observed, then 71 // reuse that result. 72 if (m_dagCache.contains(resource)) 73 return false; 74 75 ActiveFrame frame(m_activeResources, resource); 76 77 RenderObject* node = resource; 78 while (node) { 79 // Skip subtrees which are themselves resources. (They will be 80 // processed - if needed - when they are actually referenced.) 81 if (node != resource && node->isSVGResourceContainer()) { 82 node = node->nextInPreOrderAfterChildren(resource); 83 continue; 84 } 85 if (SVGResources* nodeResources = SVGResourcesCache::cachedResourcesForRenderObject(node)) { 86 // Fetch all the resources referenced by |node|. 87 ResourceSet nodeSet; 88 nodeResources->buildSetOfResources(nodeSet); 89 90 // Iterate resources referenced by |node|. 91 ResourceSet::iterator end = nodeSet.end(); 92 for (ResourceSet::iterator it = nodeSet.begin(); it != end; ++it) { 93 if (m_activeResources.contains(*it) || resourceContainsCycles(*it)) 94 return true; 95 } 96 } 97 node = node->nextInPreOrder(resource); 98 } 99 100 // No cycles found in (or from) this resource. Add it to the "DAG cache". 101 m_dagCache.add(resource); 102 return false; 103 } 104 105 void SVGResourcesCycleSolver::resolveCycles() 106 { 107 ASSERT(m_activeResources.isEmpty()); 108 109 // If the starting RenderObject is a resource container itself, then add it 110 // to the active set (to break direct self-references.) 111 if (m_renderer->isSVGResourceContainer()) 112 m_activeResources.add(toRenderSVGResourceContainer(m_renderer)); 113 114 ResourceSet localResources; 115 m_resources->buildSetOfResources(localResources); 116 117 // This performs a depth-first search for a back-edge in all the 118 // (potentially disjoint) graphs formed by the resources referenced by 119 // |m_renderer|. 120 ResourceSet::iterator end = localResources.end(); 121 for (ResourceSet::iterator it = localResources.begin(); it != end; ++it) { 122 if (m_activeResources.contains(*it) || resourceContainsCycles(*it)) 123 breakCycle(*it); 124 } 125 126 m_activeResources.clear(); 127 } 128 129 void SVGResourcesCycleSolver::breakCycle(RenderSVGResourceContainer* resourceLeadingToCycle) 130 { 131 ASSERT(resourceLeadingToCycle); 132 if (resourceLeadingToCycle == m_resources->linkedResource()) { 133 m_resources->resetLinkedResource(); 134 return; 135 } 136 137 switch (resourceLeadingToCycle->resourceType()) { 138 case MaskerResourceType: 139 ASSERT(resourceLeadingToCycle == m_resources->masker()); 140 m_resources->resetMasker(); 141 break; 142 case MarkerResourceType: 143 ASSERT(resourceLeadingToCycle == m_resources->markerStart() || resourceLeadingToCycle == m_resources->markerMid() || resourceLeadingToCycle == m_resources->markerEnd()); 144 if (m_resources->markerStart() == resourceLeadingToCycle) 145 m_resources->resetMarkerStart(); 146 if (m_resources->markerMid() == resourceLeadingToCycle) 147 m_resources->resetMarkerMid(); 148 if (m_resources->markerEnd() == resourceLeadingToCycle) 149 m_resources->resetMarkerEnd(); 150 break; 151 case PatternResourceType: 152 case LinearGradientResourceType: 153 case RadialGradientResourceType: 154 ASSERT(resourceLeadingToCycle == m_resources->fill() || resourceLeadingToCycle == m_resources->stroke()); 155 if (m_resources->fill() == resourceLeadingToCycle) 156 m_resources->resetFill(); 157 if (m_resources->stroke() == resourceLeadingToCycle) 158 m_resources->resetStroke(); 159 break; 160 case FilterResourceType: 161 ASSERT(resourceLeadingToCycle == m_resources->filter()); 162 m_resources->resetFilter(); 163 break; 164 case ClipperResourceType: 165 ASSERT(resourceLeadingToCycle == m_resources->clipper()); 166 m_resources->resetClipper(); 167 break; 168 case SolidColorResourceType: 169 ASSERT_NOT_REACHED(); 170 break; 171 } 172 } 173 174 } 175