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