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 "SVGResourcesCache.h"
22
23#if ENABLE(SVG)
24#include "RenderSVGResourceContainer.h"
25#include "SVGDocumentExtensions.h"
26#include "SVGResources.h"
27#include "SVGResourcesCycleSolver.h"
28
29namespace WebCore {
30
31SVGResourcesCache::SVGResourcesCache()
32{
33}
34
35SVGResourcesCache::~SVGResourcesCache()
36{
37    deleteAllValues(m_cache);
38}
39
40void SVGResourcesCache::addResourcesFromRenderObject(RenderObject* object, const RenderStyle* style)
41{
42    ASSERT(object);
43    ASSERT(style);
44    ASSERT(!m_cache.contains(object));
45
46    const SVGRenderStyle* svgStyle = style->svgStyle();
47    ASSERT(svgStyle);
48
49    // Build a list of all resources associated with the passed RenderObject
50    SVGResources* resources = new SVGResources;
51    if (!resources->buildCachedResources(object, svgStyle)) {
52        delete resources;
53        return;
54    }
55
56    // Put object in cache.
57    m_cache.set(object, resources);
58
59    // Run cycle-detection _afterwards_, so self-references can be caught as well.
60    SVGResourcesCycleSolver solver(object, resources);
61    solver.resolveCycles();
62
63    // Walk resources and register the render object at each resources.
64    HashSet<RenderSVGResourceContainer*> resourceSet;
65    resources->buildSetOfResources(resourceSet);
66
67    HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
68    for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
69        (*it)->addClient(object);
70}
71
72void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object)
73{
74    if (!m_cache.contains(object))
75        return;
76
77    SVGResources* resources = m_cache.get(object);
78
79    // Walk resources and register the render object at each resources.
80    HashSet<RenderSVGResourceContainer*> resourceSet;
81    resources->buildSetOfResources(resourceSet);
82
83    HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
84    for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
85        (*it)->removeClient(object);
86
87    delete m_cache.take(object);
88}
89
90static inline SVGResourcesCache* resourcesCacheFromRenderObject(RenderObject* renderer)
91{
92    Document* document = renderer->document();
93    ASSERT(document);
94
95    SVGDocumentExtensions* extensions = document->accessSVGExtensions();
96    ASSERT(extensions);
97
98    SVGResourcesCache* cache = extensions->resourcesCache();
99    ASSERT(cache);
100
101    return cache;
102}
103
104SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(RenderObject* renderer)
105{
106    ASSERT(renderer);
107    SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
108    if (!cache->m_cache.contains(renderer))
109        return 0;
110
111    return cache->m_cache.get(renderer);
112}
113
114void SVGResourcesCache::clientLayoutChanged(RenderObject* object)
115{
116    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
117    if (!resources)
118        return;
119
120    resources->removeClientFromCache(object);
121}
122
123void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle)
124{
125    ASSERT(renderer);
126    if (diff == StyleDifferenceEqual)
127        return;
128
129    // In this case the proper SVGFE*Element will imply whether the modifided CSS properties implies a relayout or repaint.
130    if (renderer->isSVGResourceFilterPrimitive() && diff == StyleDifferenceRepaint)
131        return;
132
133    clientUpdatedFromElement(renderer, newStyle);
134    RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
135}
136
137void SVGResourcesCache::clientUpdatedFromElement(RenderObject* renderer, const RenderStyle* newStyle)
138{
139    ASSERT(renderer);
140    ASSERT(renderer->parent());
141
142    SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
143    cache->removeResourcesFromRenderObject(renderer);
144    cache->addResourcesFromRenderObject(renderer, newStyle);
145}
146
147void SVGResourcesCache::clientDestroyed(RenderObject* renderer)
148{
149    ASSERT(renderer);
150    SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
151    cache->removeResourcesFromRenderObject(renderer);
152}
153
154void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource)
155{
156    ASSERT(resource);
157    SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource);
158
159    // The resource itself may have clients, that need to be notified.
160    cache->removeResourcesFromRenderObject(resource);
161
162    HashMap<RenderObject*, SVGResources*>::iterator end = cache->m_cache.end();
163    for (HashMap<RenderObject*, SVGResources*>::iterator it = cache->m_cache.begin(); it != end; ++it)
164        it->second->resourceDestroyed(resource);
165}
166
167}
168
169#endif
170