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 
     22 #include "core/rendering/svg/RenderSVGResourceContainer.h"
     23 
     24 #include "core/rendering/LayoutRectRecorder.h"
     25 #include "core/rendering/RenderLayer.h"
     26 #include "core/rendering/RenderView.h"
     27 #include "core/rendering/svg/SVGRenderingContext.h"
     28 #include "core/rendering/svg/SVGResourcesCache.h"
     29 #include "core/svg/SVGGraphicsElement.h"
     30 
     31 #include "wtf/TemporaryChange.h"
     32 
     33 namespace WebCore {
     34 
     35 static inline SVGDocumentExtensions* svgExtensionsFromElement(SVGElement* element)
     36 {
     37     ASSERT(element);
     38     return element->document().accessSVGExtensions();
     39 }
     40 
     41 RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement* node)
     42     : RenderSVGHiddenContainer(node)
     43     , m_isInLayout(false)
     44     , m_id(node->getIdAttribute())
     45     , m_invalidationMask(0)
     46     , m_registered(false)
     47     , m_isInvalidating(false)
     48 {
     49 }
     50 
     51 RenderSVGResourceContainer::~RenderSVGResourceContainer()
     52 {
     53     if (m_registered)
     54         svgExtensionsFromElement(element())->removeResource(m_id);
     55 }
     56 
     57 void RenderSVGResourceContainer::layout()
     58 {
     59     // FIXME: Investigate a way to detect and break resource layout dependency cycles early.
     60     // Then we can remove this method altogether, and fall back onto RenderSVGHiddenContainer::layout().
     61     ASSERT(needsLayout());
     62     if (m_isInLayout)
     63         return;
     64 
     65     LayoutRectRecorder recorder(*this);
     66     TemporaryChange<bool> inLayoutChange(m_isInLayout, true);
     67 
     68     RenderSVGHiddenContainer::layout();
     69 
     70     clearInvalidationMask();
     71 }
     72 
     73 void RenderSVGResourceContainer::willBeDestroyed()
     74 {
     75     SVGResourcesCache::resourceDestroyed(this);
     76     RenderSVGHiddenContainer::willBeDestroyed();
     77 }
     78 
     79 void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     80 {
     81     RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
     82 
     83     if (!m_registered) {
     84         m_registered = true;
     85         registerResource();
     86     }
     87 }
     88 
     89 void RenderSVGResourceContainer::idChanged()
     90 {
     91     // Invalidate all our current clients.
     92     removeAllClientsFromCache();
     93 
     94     // Remove old id, that is guaranteed to be present in cache.
     95     SVGDocumentExtensions* extensions = svgExtensionsFromElement(element());
     96     extensions->removeResource(m_id);
     97     m_id = element()->getIdAttribute();
     98 
     99     registerResource();
    100 }
    101 
    102 void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
    103 {
    104     if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating)
    105         return;
    106 
    107     if (m_invalidationMask & mode)
    108         return;
    109 
    110     m_invalidationMask |= mode;
    111     m_isInvalidating = true;
    112     bool needsLayout = mode == LayoutAndBoundariesInvalidation;
    113     bool markForInvalidation = mode != ParentOnlyInvalidation;
    114 
    115     HashSet<RenderObject*>::iterator end = m_clients.end();
    116     for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) {
    117         RenderObject* client = *it;
    118         if (client->isSVGResourceContainer()) {
    119             toRenderSVGResourceContainer(client)->removeAllClientsFromCache(markForInvalidation);
    120             continue;
    121         }
    122 
    123         if (markForInvalidation)
    124             markClientForInvalidation(client, mode);
    125 
    126         RenderSVGResource::markForLayoutAndParentResourceInvalidation(client, needsLayout);
    127     }
    128 
    129     markAllClientLayersForInvalidation();
    130 
    131     m_isInvalidating = false;
    132 }
    133 
    134 void RenderSVGResourceContainer::markAllClientLayersForInvalidation()
    135 {
    136     HashSet<RenderLayer*>::iterator layerEnd = m_clientLayers.end();
    137     for (HashSet<RenderLayer*>::iterator it = m_clientLayers.begin(); it != layerEnd; ++it)
    138         (*it)->filterNeedsRepaint();
    139 }
    140 
    141 void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
    142 {
    143     ASSERT(client);
    144     ASSERT(!m_clients.isEmpty());
    145 
    146     switch (mode) {
    147     case LayoutAndBoundariesInvalidation:
    148     case BoundariesInvalidation:
    149         client->setNeedsBoundariesUpdate();
    150         break;
    151     case RepaintInvalidation:
    152         if (client->view()) {
    153             if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isInLayout())
    154                 client->setShouldDoFullRepaintAfterLayout(true);
    155             else
    156                 client->repaint();
    157         }
    158         break;
    159     case ParentOnlyInvalidation:
    160         break;
    161     }
    162 }
    163 
    164 void RenderSVGResourceContainer::addClient(RenderObject* client)
    165 {
    166     ASSERT(client);
    167     m_clients.add(client);
    168     clearInvalidationMask();
    169 }
    170 
    171 void RenderSVGResourceContainer::removeClient(RenderObject* client)
    172 {
    173     ASSERT(client);
    174     removeClientFromCache(client, false);
    175     m_clients.remove(client);
    176 }
    177 
    178 void RenderSVGResourceContainer::addClientRenderLayer(Node* node)
    179 {
    180     ASSERT(node);
    181     if (!node->renderer() || !node->renderer()->hasLayer())
    182         return;
    183     m_clientLayers.add(toRenderLayerModelObject(node->renderer())->layer());
    184     clearInvalidationMask();
    185 }
    186 
    187 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client)
    188 {
    189     ASSERT(client);
    190     m_clientLayers.add(client);
    191     clearInvalidationMask();
    192 }
    193 
    194 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client)
    195 {
    196     ASSERT(client);
    197     m_clientLayers.remove(client);
    198 }
    199 
    200 void RenderSVGResourceContainer::invalidateCacheAndMarkForLayout(SubtreeLayoutScope* layoutScope)
    201 {
    202     if (selfNeedsLayout())
    203         return;
    204 
    205     setNeedsLayout(MarkContainingBlockChain, layoutScope);
    206 
    207     if (everHadLayout())
    208         removeAllClientsFromCache();
    209 }
    210 
    211 void RenderSVGResourceContainer::registerResource()
    212 {
    213     SVGDocumentExtensions* extensions = svgExtensionsFromElement(element());
    214     if (!extensions->hasPendingResource(m_id)) {
    215         extensions->addResource(m_id, this);
    216         return;
    217     }
    218 
    219     OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(m_id));
    220 
    221     // Cache us with the new id.
    222     extensions->addResource(m_id, this);
    223 
    224     // Update cached resources of pending clients.
    225     const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
    226     for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
    227         ASSERT((*it)->hasPendingResources());
    228         extensions->clearHasPendingResourcesIfPossible(*it);
    229         RenderObject* renderer = (*it)->renderer();
    230         if (!renderer)
    231             continue;
    232         SVGResourcesCache::clientStyleChanged(renderer, StyleDifferenceLayout, renderer->style());
    233         renderer->setNeedsLayout();
    234     }
    235 }
    236 
    237 bool RenderSVGResourceContainer::shouldTransformOnTextPainting(RenderObject* object, AffineTransform& resourceTransform)
    238 {
    239     ASSERT_UNUSED(object, object);
    240 
    241     // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods.
    242     ASSERT(object->isSVGText() || object->isSVGTextPath() || object->isSVGInline());
    243 
    244     // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows.
    245     // So, we use that scaling factor here, too, and then push it down to pattern or gradient space
    246     // in order to keep the pattern or gradient correctly scaled.
    247     float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(object);
    248     if (scalingFactor == 1)
    249         return false;
    250     resourceTransform.scale(scalingFactor);
    251     return true;
    252 }
    253 
    254 // FIXME: This does not belong here.
    255 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
    256 {
    257     if (!object->isSVGShape())
    258         return resourceTransform;
    259 
    260     SVGGraphicsElement* element = toSVGGraphicsElement(object->node());
    261     AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
    262     transform *= resourceTransform;
    263     return transform;
    264 }
    265 
    266 }
    267