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