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 #if ENABLE(SVG)
23 #include "RenderSVGResourceContainer.h"
24 
25 #include "RenderSVGShadowTreeRootContainer.h"
26 #include "SVGStyledTransformableElement.h"
27 
28 namespace WebCore {
29 
svgExtensionsFromNode(Node * node)30 static inline SVGDocumentExtensions* svgExtensionsFromNode(Node* node)
31 {
32     ASSERT(node);
33     ASSERT(node->document());
34     return node->document()->accessSVGExtensions();
35 }
36 
RenderSVGResourceContainer(SVGStyledElement * node)37 RenderSVGResourceContainer::RenderSVGResourceContainer(SVGStyledElement* node)
38     : RenderSVGHiddenContainer(node)
39     , m_id(node->hasID() ? node->getIdAttribute() : nullAtom)
40     , m_registered(false)
41 {
42 }
43 
~RenderSVGResourceContainer()44 RenderSVGResourceContainer::~RenderSVGResourceContainer()
45 {
46     if (m_registered)
47         svgExtensionsFromNode(node())->removeResource(m_id);
48 }
49 
layout()50 void RenderSVGResourceContainer::layout()
51 {
52     // Invalidate all resources if our layout changed.
53     if (m_everHadLayout && selfNeedsLayout())
54         removeAllClientsFromCache();
55 
56     RenderSVGHiddenContainer::layout();
57 }
58 
destroy()59 void RenderSVGResourceContainer::destroy()
60 {
61     SVGResourcesCache::resourceDestroyed(this);
62     RenderSVGHiddenContainer::destroy();
63 }
64 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)65 void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
66 {
67     RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
68 
69     if (!m_registered) {
70         m_registered = true;
71         registerResource();
72     }
73 }
74 
idChanged()75 void RenderSVGResourceContainer::idChanged()
76 {
77     // Invalidate all our current clients.
78     removeAllClientsFromCache();
79 
80     // Remove old id, that is guaranteed to be present in cache.
81     SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
82     extensions->removeResource(m_id);
83     m_id = static_cast<Element*>(node())->getIdAttribute();
84 
85     registerResource();
86 }
87 
markAllClientsForInvalidation(InvalidationMode mode)88 void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
89 {
90     if (m_clients.isEmpty())
91         return;
92 
93     bool needsLayout = mode == LayoutAndBoundariesInvalidation;
94     bool markForInvalidation = mode != ParentOnlyInvalidation;
95 
96     HashSet<RenderObject*>::iterator end = m_clients.end();
97     for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) {
98         RenderObject* client = *it;
99         if (client->isSVGResourceContainer()) {
100             client->toRenderSVGResourceContainer()->removeAllClientsFromCache(markForInvalidation);
101             continue;
102         }
103 
104         if (markForInvalidation)
105             markClientForInvalidation(client, mode);
106 
107         if (needsLayout)
108             client->setNeedsLayout(true);
109 
110         // Invalidate resources in ancestor chain, if needed.
111         RenderObject* current = client->parent();
112         while (current) {
113             if (current->isSVGResourceContainer()) {
114                 current->toRenderSVGResourceContainer()->removeAllClientsFromCache(markForInvalidation);
115                 break;
116             }
117 
118             current = current->parent();
119         }
120     }
121 }
122 
markClientForInvalidation(RenderObject * client,InvalidationMode mode)123 void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
124 {
125     ASSERT(client);
126     ASSERT(!m_clients.isEmpty());
127 
128     switch (mode) {
129     case LayoutAndBoundariesInvalidation:
130     case BoundariesInvalidation:
131         client->setNeedsBoundariesUpdate();
132         break;
133     case RepaintInvalidation:
134         if (client->view())
135             client->repaint();
136         break;
137     case ParentOnlyInvalidation:
138         break;
139     }
140 }
141 
addClient(RenderObject * client)142 void RenderSVGResourceContainer::addClient(RenderObject* client)
143 {
144     ASSERT(client);
145     m_clients.add(client);
146 }
147 
removeClient(RenderObject * client)148 void RenderSVGResourceContainer::removeClient(RenderObject* client)
149 {
150     ASSERT(client);
151     m_clients.remove(client);
152 }
153 
registerResource()154 void RenderSVGResourceContainer::registerResource()
155 {
156     SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
157     if (!extensions->hasPendingResources(m_id)) {
158         extensions->addResource(m_id, this);
159         return;
160     }
161 
162     OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(m_id));
163 
164     // Cache us with the new id.
165     extensions->addResource(m_id, this);
166 
167     // Update cached resources of pending clients.
168     const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
169     for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
170         ASSERT((*it)->hasPendingResources());
171         (*it)->clearHasPendingResourcesIfPossible();
172         RenderObject* renderer = (*it)->renderer();
173         if (!renderer)
174             continue;
175         SVGResourcesCache::clientUpdatedFromElement(renderer, renderer->style());
176         renderer->setNeedsLayout(true);
177     }
178 }
179 
180 // FIXME: This does not belong here.
transformOnNonScalingStroke(RenderObject * object,const AffineTransform & resourceTransform)181 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
182 {
183     if (!object->isSVGPath())
184         return resourceTransform;
185 
186     SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(object->node());
187     AffineTransform transform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
188     transform *= resourceTransform;
189     return transform;
190 }
191 
192 }
193 
194 #endif
195