2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
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.
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.
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.
21 #include "SVGResourcesCycleSolver.h"
23 // Set to a value > 0, to debug the resource cache.
24 #define DEBUG_CYCLE_DETECTION 0
26 #include "RenderAncestorIterator.h"
27 #include "RenderSVGResourceClipper.h"
28 #include "RenderSVGResourceFilter.h"
29 #include "RenderSVGResourceMarker.h"
30 #include "RenderSVGResourceMasker.h"
31 #include "SVGGradientElement.h"
32 #include "SVGPatternElement.h"
33 #include "SVGResources.h"
34 #include "SVGResourcesCache.h"
38 SVGResourcesCycleSolver::SVGResourcesCycleSolver(RenderElement& renderer, SVGResources& resources)
39 : m_renderer(renderer)
40 , m_resources(resources)
44 SVGResourcesCycleSolver::~SVGResourcesCycleSolver()
48 bool SVGResourcesCycleSolver::resourceContainsCycles(RenderElement& renderer) const
50 // First operate on the resources of the given renderer.
51 // <marker id="a"> <path marker-start="url(#b)"/> ...
52 // <marker id="b" marker-start="url(#a)"/>
53 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer)) {
54 HashSet<RenderSVGResourceContainer*> resourceSet;
55 resources->buildSetOfResources(resourceSet);
57 // Walk all resources and check wheter they reference any resource contained in the resources set.
58 for (auto* resource : resourceSet) {
59 if (m_allResources.contains(resource))
64 // Then operate on the child resources of the given renderer.
65 // <marker id="a"> <path marker-start="url(#b)"/> ...
66 // <marker id="b"> <path marker-start="url(#a)"/> ...
67 for (auto& child : childrenOfType<RenderElement>(renderer)) {
68 SVGResources* childResources = SVGResourcesCache::cachedResourcesForRenderObject(child);
72 // A child of the given 'resource' contains resources.
73 HashSet<RenderSVGResourceContainer*> childResourceSet;
74 childResources->buildSetOfResources(childResourceSet);
76 // Walk all child resources and check wheter they reference any resource contained in the resources set.
77 for (auto* resource : childResourceSet) {
78 if (m_allResources.contains(resource))
82 // Walk children recursively, stop immediately if we found a cycle
83 if (resourceContainsCycles(child))
90 void SVGResourcesCycleSolver::resolveCycles()
92 ASSERT(m_allResources.isEmpty());
94 #if DEBUG_CYCLE_DETECTION > 0
95 fprintf(stderr, "\nBefore cycle detection:\n");
96 m_resources.dump(&m_renderer);
99 // Stash all resources into a HashSet for the ease of traversing.
100 HashSet<RenderSVGResourceContainer*> localResources;
101 m_resources.buildSetOfResources(localResources);
102 ASSERT(!localResources.isEmpty());
104 // Add all parent resource containers to the HashSet.
105 HashSet<RenderSVGResourceContainer*> ancestorResources;
106 for (auto& resource : ancestorsOfType<RenderSVGResourceContainer>(m_renderer))
107 ancestorResources.add(&resource);
109 #if DEBUG_CYCLE_DETECTION > 0
110 fprintf(stderr, "\nDetecting wheter any resources references any of following objects:\n");
112 fprintf(stderr, "Local resources:\n");
113 for (auto* resource : localResources)
114 fprintf(stderr, "|> %s: object=%p (node=%p)\n", resource->renderName(), resource, resource->node());
116 fprintf(stderr, "Parent resources:\n");
117 for (auto* resource : ancestorResources)
118 fprintf(stderr, "|> %s: object=%p (node=%p)\n", resource->renderName(), resource, resource->node());
122 // Build combined set of local and parent resources.
123 m_allResources = localResources;
124 for (auto* resource : ancestorResources)
125 m_allResources.add(resource);
127 // If we're a resource, add ourselves to the HashSet.
128 if (m_renderer.isSVGResourceContainer())
129 m_allResources.add(&toRenderSVGResourceContainer(m_renderer));
131 ASSERT(!m_allResources.isEmpty());
133 // The job of this function is to determine wheter any of the 'resources' associated with the given 'renderer'
134 // references us (or wheter any of its kids references us) -> that's a cycle, we need to find and break it.
135 for (auto* resource : localResources) {
136 if (ancestorResources.contains(resource) || resourceContainsCycles(*resource))
137 breakCycle(*resource);
140 #if DEBUG_CYCLE_DETECTION > 0
141 fprintf(stderr, "\nAfter cycle detection:\n");
142 m_resources.dump(m_renderer);
145 m_allResources.clear();
148 void SVGResourcesCycleSolver::breakCycle(RenderSVGResourceContainer& resourceLeadingToCycle)
150 if (&resourceLeadingToCycle == m_resources.linkedResource()) {
151 m_resources.resetLinkedResource();
155 switch (resourceLeadingToCycle.resourceType()) {
156 case MaskerResourceType:
157 ASSERT(&resourceLeadingToCycle == m_resources.masker());
158 m_resources.resetMasker();
160 case MarkerResourceType:
161 ASSERT(&resourceLeadingToCycle == m_resources.markerStart() || &resourceLeadingToCycle == m_resources.markerMid() || &resourceLeadingToCycle == m_resources.markerEnd());
162 if (m_resources.markerStart() == &resourceLeadingToCycle)
163 m_resources.resetMarkerStart();
164 if (m_resources.markerMid() == &resourceLeadingToCycle)
165 m_resources.resetMarkerMid();
166 if (m_resources.markerEnd() == &resourceLeadingToCycle)
167 m_resources.resetMarkerEnd();
169 case PatternResourceType:
170 case LinearGradientResourceType:
171 case RadialGradientResourceType:
172 ASSERT(&resourceLeadingToCycle == m_resources.fill() || &resourceLeadingToCycle == m_resources.stroke());
173 if (m_resources.fill() == &resourceLeadingToCycle)
174 m_resources.resetFill();
175 if (m_resources.stroke() == &resourceLeadingToCycle)
176 m_resources.resetStroke();
178 case FilterResourceType:
180 ASSERT(&resourceLeadingToCycle == m_resources.filter());
181 m_resources.resetFilter();
184 case ClipperResourceType:
185 ASSERT(&resourceLeadingToCycle == m_resources.clipper());
186 m_resources.resetClipper();
188 case SolidColorResourceType:
189 ASSERT_NOT_REACHED();