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