• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
svgExtensionsFromElement(SVGElement * element)35 static inline SVGDocumentExtensions* svgExtensionsFromElement(SVGElement* element)
36 {
37     ASSERT(element);
38     return element->document().accessSVGExtensions();
39 }
40 
RenderSVGResourceContainer(SVGElement * node)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 
~RenderSVGResourceContainer()51 RenderSVGResourceContainer::~RenderSVGResourceContainer()
52 {
53     if (m_registered)
54         svgExtensionsFromElement(element())->removeResource(m_id);
55 }
56 
layout()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 
willBeDestroyed()73 void RenderSVGResourceContainer::willBeDestroyed()
74 {
75     SVGResourcesCache::resourceDestroyed(this);
76     RenderSVGHiddenContainer::willBeDestroyed();
77 }
78 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)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 
idChanged()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 
markAllClientsForInvalidation(InvalidationMode mode)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 
markAllClientLayersForInvalidation()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 
markClientForInvalidation(RenderObject * client,InvalidationMode mode)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 
addClient(RenderObject * client)164 void RenderSVGResourceContainer::addClient(RenderObject* client)
165 {
166     ASSERT(client);
167     m_clients.add(client);
168     clearInvalidationMask();
169 }
170 
removeClient(RenderObject * client)171 void RenderSVGResourceContainer::removeClient(RenderObject* client)
172 {
173     ASSERT(client);
174     removeClientFromCache(client, false);
175     m_clients.remove(client);
176 }
177 
addClientRenderLayer(Node * node)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 
addClientRenderLayer(RenderLayer * client)187 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client)
188 {
189     ASSERT(client);
190     m_clientLayers.add(client);
191     clearInvalidationMask();
192 }
193 
removeClientRenderLayer(RenderLayer * client)194 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client)
195 {
196     ASSERT(client);
197     m_clientLayers.remove(client);
198 }
199 
invalidateCacheAndMarkForLayout(SubtreeLayoutScope * layoutScope)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 
registerResource()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 
shouldTransformOnTextPainting(RenderObject * object,AffineTransform & resourceTransform)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.
transformOnNonScalingStroke(RenderObject * object,const AffineTransform & resourceTransform)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