blob: 8f28c1cd1855f1518d6b6c4d0138d13c2397cd03 [file] [log] [blame]
/*
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "core/rendering/svg/SVGResourcesCache.h"
#include "core/HTMLNames.h"
#include "core/rendering/svg/RenderSVGResourceContainer.h"
#include "core/rendering/svg/SVGResources.h"
#include "core/rendering/svg/SVGResourcesCycleSolver.h"
#include "core/svg/SVGDocumentExtensions.h"
namespace blink {
SVGResourcesCache::SVGResourcesCache()
{
}
SVGResourcesCache::~SVGResourcesCache()
{
}
void SVGResourcesCache::addResourcesFromRenderObject(RenderObject* object, const RenderStyle* style)
{
ASSERT(object);
ASSERT(style);
ASSERT(!m_cache.contains(object));
const SVGRenderStyle& svgStyle = style->svgStyle();
// Build a list of all resources associated with the passed RenderObject
OwnPtr<SVGResources> newResources = SVGResources::buildResources(object, svgStyle);
if (!newResources)
return;
// Put object in cache.
SVGResources* resources = m_cache.set(object, newResources.release()).storedValue->value.get();
// Run cycle-detection _afterwards_, so self-references can be caught as well.
SVGResourcesCycleSolver solver(object, resources);
solver.resolveCycles();
// Walk resources and register the render object at each resources.
HashSet<RenderSVGResourceContainer*> resourceSet;
resources->buildSetOfResources(resourceSet);
HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
(*it)->addClient(object);
}
void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object)
{
OwnPtr<SVGResources> resources = m_cache.take(object);
if (!resources)
return;
// Walk resources and register the render object at each resources.
HashSet<RenderSVGResourceContainer*> resourceSet;
resources->buildSetOfResources(resourceSet);
HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
(*it)->removeClient(object);
}
static inline SVGResourcesCache* resourcesCacheFromRenderObject(const RenderObject* renderer)
{
Document& document = renderer->document();
SVGDocumentExtensions& extensions = document.accessSVGExtensions();
SVGResourcesCache* cache = extensions.resourcesCache();
ASSERT(cache);
return cache;
}
SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(const RenderObject* renderer)
{
ASSERT(renderer);
return resourcesCacheFromRenderObject(renderer)->m_cache.get(renderer);
}
void SVGResourcesCache::clientLayoutChanged(RenderObject* object)
{
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
if (!resources)
return;
// Invalidate the resources if either the RenderObject itself changed,
// or we have filter resources, which could depend on the layout of children.
if (object->selfNeedsLayout() || resources->filter())
resources->removeClientFromCache(object);
}
static inline bool rendererCanHaveResources(RenderObject* renderer)
{
ASSERT(renderer);
return renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGInlineText();
}
void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle)
{
ASSERT(renderer);
ASSERT(renderer->node());
ASSERT(renderer->node()->isSVGElement());
if (!diff.hasDifference() || !renderer->parent())
return;
// In this case the proper SVGFE*Element will decide whether the modified CSS properties require a relayout or paintInvalidation.
if (renderer->isSVGResourceFilterPrimitive() && !diff.needsLayout())
return;
// Dynamic changes of CSS properties like 'clip-path' may require us to recompute the associated resources for a renderer.
// FIXME: Avoid passing in a useless StyleDifference, but instead compare oldStyle/newStyle to see which resources changed
// to be able to selectively rebuild individual resources, instead of all of them.
if (rendererCanHaveResources(renderer)) {
SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
cache->removeResourcesFromRenderObject(renderer);
cache->addResourcesFromRenderObject(renderer, newStyle);
}
RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
}
void SVGResourcesCache::clientWasAddedToTree(RenderObject* renderer, const RenderStyle* newStyle)
{
if (!renderer->node())
return;
RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
if (!rendererCanHaveResources(renderer))
return;
SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
cache->addResourcesFromRenderObject(renderer, newStyle);
}
void SVGResourcesCache::clientWillBeRemovedFromTree(RenderObject* renderer)
{
if (!renderer->node())
return;
RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
if (!rendererCanHaveResources(renderer))
return;
SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
cache->removeResourcesFromRenderObject(renderer);
}
void SVGResourcesCache::clientDestroyed(RenderObject* renderer)
{
ASSERT(renderer);
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
if (resources)
resources->removeClientFromCache(renderer);
SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
cache->removeResourcesFromRenderObject(renderer);
}
void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource)
{
ASSERT(resource);
SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource);
// The resource itself may have clients, that need to be notified.
cache->removeResourcesFromRenderObject(resource);
CacheMap::iterator end = cache->m_cache.end();
for (CacheMap::iterator it = cache->m_cache.begin(); it != end; ++it) {
it->value->resourceDestroyed(resource);
// Mark users of destroyed resources as pending resolution based on the id of the old resource.
Element* resourceElement = resource->element();
Element* clientElement = toElement(it->key->node());
SVGDocumentExtensions& extensions = clientElement->document().accessSVGExtensions();
extensions.addPendingResource(resourceElement->fastGetAttribute(HTMLNames::idAttr), clientElement);
}
}
}