2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
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.
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.
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.
22 #include "core/rendering/svg/RenderSVGResourceContainer.h"
24 #include "core/rendering/RenderLayer.h"
25 #include "core/rendering/RenderView.h"
26 #include "core/rendering/svg/SVGRenderingContext.h"
27 #include "core/rendering/svg/SVGResourcesCache.h"
28 #include "core/svg/SVGGraphicsElement.h"
30 #include "wtf/TemporaryChange.h"
34 static inline SVGDocumentExtensions& svgExtensionsFromElement(SVGElement* element)
37 return element->document().accessSVGExtensions();
40 RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement* node)
41 : RenderSVGHiddenContainer(node)
43 , m_id(node->getIdAttribute())
44 , m_invalidationMask(0)
46 , m_isInvalidating(false)
50 RenderSVGResourceContainer::~RenderSVGResourceContainer()
53 svgExtensionsFromElement(element()).removeResource(m_id);
56 void RenderSVGResourceContainer::layout()
58 // FIXME: Investigate a way to detect and break resource layout dependency cycles early.
59 // Then we can remove this method altogether, and fall back onto RenderSVGHiddenContainer::layout().
60 ASSERT(needsLayout());
64 TemporaryChange<bool> inLayoutChange(m_isInLayout, true);
66 RenderSVGHiddenContainer::layout();
68 clearInvalidationMask();
71 void RenderSVGResourceContainer::willBeDestroyed()
73 SVGResourcesCache::resourceDestroyed(this);
74 RenderSVGHiddenContainer::willBeDestroyed();
77 void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
79 RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
87 void RenderSVGResourceContainer::idChanged()
89 // Invalidate all our current clients.
90 removeAllClientsFromCache();
92 // Remove old id, that is guaranteed to be present in cache.
93 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element());
94 extensions.removeResource(m_id);
95 m_id = element()->getIdAttribute();
100 void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
102 if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating)
105 if (m_invalidationMask & mode)
108 m_invalidationMask |= mode;
109 m_isInvalidating = true;
110 bool needsLayout = mode == LayoutAndBoundariesInvalidation;
111 bool markForInvalidation = mode != ParentOnlyInvalidation;
113 HashSet<RenderObject*>::iterator end = m_clients.end();
114 for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) {
115 RenderObject* client = *it;
116 if (client->isSVGResourceContainer()) {
117 toRenderSVGResourceContainer(client)->removeAllClientsFromCache(markForInvalidation);
121 if (markForInvalidation)
122 markClientForInvalidation(client, mode);
124 RenderSVGResource::markForLayoutAndParentResourceInvalidation(client, needsLayout);
127 markAllClientLayersForInvalidation();
129 m_isInvalidating = false;
132 void RenderSVGResourceContainer::markAllClientLayersForInvalidation()
134 HashSet<RenderLayer*>::iterator layerEnd = m_clientLayers.end();
135 for (HashSet<RenderLayer*>::iterator it = m_clientLayers.begin(); it != layerEnd; ++it)
136 (*it)->filterNeedsRepaint();
139 void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
142 ASSERT(!m_clients.isEmpty());
145 case LayoutAndBoundariesInvalidation:
146 case BoundariesInvalidation:
147 client->setNeedsBoundariesUpdate();
149 case RepaintInvalidation:
150 if (client->view()) {
151 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isInPerformLayout())
152 client->setShouldDoFullRepaintAfterLayout(true);
157 case ParentOnlyInvalidation:
162 void RenderSVGResourceContainer::addClient(RenderObject* client)
165 m_clients.add(client);
166 clearInvalidationMask();
169 void RenderSVGResourceContainer::removeClient(RenderObject* client)
172 removeClientFromCache(client, false);
173 m_clients.remove(client);
176 void RenderSVGResourceContainer::addClientRenderLayer(Node* node)
179 if (!node->renderer() || !node->renderer()->hasLayer())
181 m_clientLayers.add(toRenderLayerModelObject(node->renderer())->layer());
182 clearInvalidationMask();
185 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client)
188 m_clientLayers.add(client);
189 clearInvalidationMask();
192 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client)
195 m_clientLayers.remove(client);
198 void RenderSVGResourceContainer::invalidateCacheAndMarkForLayout(SubtreeLayoutScope* layoutScope)
200 if (selfNeedsLayout())
203 setNeedsLayout(MarkContainingBlockChain, layoutScope);
206 removeAllClientsFromCache();
209 void RenderSVGResourceContainer::registerResource()
211 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element());
212 if (!extensions.hasPendingResource(m_id)) {
213 extensions.addResource(m_id, this);
217 OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions.removePendingResource(m_id));
219 // Cache us with the new id.
220 extensions.addResource(m_id, this);
222 // Update cached resources of pending clients.
223 const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
224 for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
225 ASSERT((*it)->hasPendingResources());
226 extensions.clearHasPendingResourcesIfPossible(*it);
227 RenderObject* renderer = (*it)->renderer();
231 StyleDifference diff;
232 diff.setNeedsFullLayout();
233 SVGResourcesCache::clientStyleChanged(renderer, diff, renderer->style());
234 renderer->setNeedsLayout();
238 AffineTransform RenderSVGResourceContainer::computeResourceSpaceTransform(RenderObject* object, const AffineTransform& baseTransform, const SVGRenderStyle* svgStyle, unsigned short resourceMode)
240 AffineTransform computedSpaceTransform = baseTransform;
241 if (resourceMode & ApplyToTextMode) {
242 // Depending on the font scaling factor, we may need to apply an
243 // additional transform (scale-factor) the paintserver, since text
244 // painting removes the scale factor from the context. (See
245 // SVGInlineTextBox::paintTextWithShadows.)
246 AffineTransform additionalTextTransformation;
247 if (shouldTransformOnTextPainting(object, additionalTextTransformation))
248 computedSpaceTransform = additionalTextTransformation * computedSpaceTransform;
250 if (resourceMode & ApplyToStrokeMode) {
251 // Non-scaling stroke needs to reset the transform back to the host transform.
252 if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
253 computedSpaceTransform = transformOnNonScalingStroke(object, computedSpaceTransform);
255 return computedSpaceTransform;
258 bool RenderSVGResourceContainer::shouldTransformOnTextPainting(RenderObject* object, AffineTransform& resourceTransform)
260 ASSERT_UNUSED(object, object);
262 // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods.
263 ASSERT(object->isSVGText() || object->isSVGTextPath() || object->isSVGInline());
265 // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows.
266 // So, we use that scaling factor here, too, and then push it down to pattern or gradient space
267 // in order to keep the pattern or gradient correctly scaled.
268 float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(object);
269 if (scalingFactor == 1)
271 resourceTransform.scale(scalingFactor);
275 // FIXME: This does not belong here.
276 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
278 if (!object->isSVGShape())
279 return resourceTransform;
281 SVGGraphicsElement* element = toSVGGraphicsElement(object->node());
282 AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
283 transform *= resourceTransform;