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