Home | History | Annotate | Download | only in svg
      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 = adoptPtr(new SVGResources);
     50     if (!newResources->buildResources(object, svgStyle))
     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     if (!m_cache.contains(object))
     72         return;
     73 
     74     OwnPtr<SVGResources> resources = m_cache.take(object);
     75 
     76     // Walk resources and register the render object at each resources.
     77     HashSet<RenderSVGResourceContainer*> resourceSet;
     78     resources->buildSetOfResources(resourceSet);
     79 
     80     HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
     81     for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
     82         (*it)->removeClient(object);
     83 }
     84 
     85 static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject* renderer)
     86 {
     87     Document* document = renderer->document();
     88     ASSERT(document);
     89 
     90     SVGDocumentExtensions* extensions = document->accessSVGExtensions();
     91     ASSERT(extensions);
     92 
     93     SVGResourcesCache* cache = extensions->resourcesCache();
     94     ASSERT(cache);
     95 
     96     return cache;
     97 }
     98 
     99 SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer)
    100 {
    101     ASSERT(renderer);
    102     return resourcesCacheFromRenderObject(renderer)->m_cache.get(renderer);
    103 }
    104 
    105 void SVGResourcesCache::clientLayoutChanged(RenderObject* object)
    106 {
    107     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
    108     if (!resources)
    109         return;
    110 
    111     // Invalidate the resources if either the RenderObject itself changed,
    112     // or we have filter resources, which could depend on the layout of children.
    113     if (object->selfNeedsLayout() || resources->filter())
    114         resources->removeClientFromCache(object);
    115 }
    116 
    117 static inline bool rendererCanHaveResources(RenderObject* renderer)
    118 {
    119     ASSERT(renderer);
    120     return renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGInlineText();
    121 }
    122 
    123 void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle)
    124 {
    125     ASSERT(renderer);
    126     if (diff == StyleDifferenceEqual || !renderer->parent())
    127         return;
    128 
    129     // In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or repaint.
    130     if (renderer->isSVGResourceFilterPrimitive() && (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfText))
    131         return;
    132 
    133     // Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer.
    134     // FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed
    135     // to be able to selectively rebuild individual resources, instead of all of them.
    136     if (rendererCanHaveResources(renderer)) {
    137         SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
    138         cache->removeResourcesFromRenderObject(renderer);
    139         cache->addResourcesFromRenderObject(renderer, newStyle);
    140     }
    141 
    142     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
    143 
    144     // FIXME: This doesn't look right, we often go through here in styleDidChange which means
    145     // we're changing the needsStyleRecalc bits in the middle of recalcStyle on ourself which
    146     // makes no sense. It's also not clear why we'd go through here for non-SVG elements.
    147     if (renderer->node() && !renderer->node()->isSVGElement())
    148         renderer->node()->setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer);
    149 }
    150 
    151 void SVGResourcesCache::clientWasAddedToTree(RenderObject* renderer, const RenderStyle* newStyle)
    152 {
    153     if (!renderer->node())
    154         return;
    155     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
    156 
    157     if (!rendererCanHaveResources(renderer))
    158         return;
    159     SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
    160     cache->addResourcesFromRenderObject(renderer, newStyle);
    161 }
    162 
    163 void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject* renderer)
    164 {
    165     if (!renderer->node())
    166         return;
    167     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
    168 
    169     if (!rendererCanHaveResources(renderer))
    170         return;
    171     SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
    172     cache->removeResourcesFromRenderObject(renderer);
    173 }
    174 
    175 void SVGResourcesCache::clientDestroyed(RenderObject* renderer)
    176 {
    177     ASSERT(renderer);
    178 
    179     SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
    180     if (resources)
    181         resources->removeClientFromCache(renderer);
    182 
    183     SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
    184     cache->removeResourcesFromRenderObject(renderer);
    185 }
    186 
    187 void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource)
    188 {
    189     ASSERT(resource);
    190     SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource);
    191 
    192     // The resource itself may have clients, that need to be notified.
    193     cache->removeResourcesFromRenderObject(resource);
    194 
    195         CacheMap::iterator end = cache->m_cache.end();
    196         for (CacheMap::iterator it = cache->m_cache.begin(); it != end; ++it) {
    197         it->value->resourceDestroyed(resource);
    198 
    199         // Mark users of destroyed resources as pending resolution based on the id of the old resource.
    200         Element* resourceElement = toElement(resource->node());
    201         Element* clientElement = toElement(it->key->node());
    202         SVGDocumentExtensions* extensions = clientElement->document()->accessSVGExtensions();
    203 
    204         extensions->addPendingResource(resourceElement->fastGetAttribute(HTMLNames::idAttr), clientElement);
    205     }
    206 }
    207 
    208 }
    209