Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / RenderSVGResourceContainer.cpp
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/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"
29
30 #include "wtf/TemporaryChange.h"
31
32 namespace WebCore {
33
34 static inline SVGDocumentExtensions& svgExtensionsFromElement(SVGElement* element)
35 {
36     ASSERT(element);
37     return element->document().accessSVGExtensions();
38 }
39
40 RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement* node)
41     : RenderSVGHiddenContainer(node)
42     , m_isInLayout(false)
43     , m_id(node->getIdAttribute())
44     , m_invalidationMask(0)
45     , m_registered(false)
46     , m_isInvalidating(false)
47 {
48 }
49
50 RenderSVGResourceContainer::~RenderSVGResourceContainer()
51 {
52     if (m_registered)
53         svgExtensionsFromElement(element()).removeResource(m_id);
54 }
55
56 void RenderSVGResourceContainer::layout()
57 {
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());
61     if (m_isInLayout)
62         return;
63
64     TemporaryChange<bool> inLayoutChange(m_isInLayout, true);
65
66     RenderSVGHiddenContainer::layout();
67
68     clearInvalidationMask();
69 }
70
71 void RenderSVGResourceContainer::willBeDestroyed()
72 {
73     SVGResourcesCache::resourceDestroyed(this);
74     RenderSVGHiddenContainer::willBeDestroyed();
75 }
76
77 void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
78 {
79     RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
80
81     if (!m_registered) {
82         m_registered = true;
83         registerResource();
84     }
85 }
86
87 void RenderSVGResourceContainer::idChanged()
88 {
89     // Invalidate all our current clients.
90     removeAllClientsFromCache();
91
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();
96
97     registerResource();
98 }
99
100 void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
101 {
102     if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating)
103         return;
104
105     if (m_invalidationMask & mode)
106         return;
107
108     m_invalidationMask |= mode;
109     m_isInvalidating = true;
110     bool needsLayout = mode == LayoutAndBoundariesInvalidation;
111     bool markForInvalidation = mode != ParentOnlyInvalidation;
112
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);
118             continue;
119         }
120
121         if (markForInvalidation)
122             markClientForInvalidation(client, mode);
123
124         RenderSVGResource::markForLayoutAndParentResourceInvalidation(client, needsLayout);
125     }
126
127     markAllClientLayersForInvalidation();
128
129     m_isInvalidating = false;
130 }
131
132 void RenderSVGResourceContainer::markAllClientLayersForInvalidation()
133 {
134     HashSet<RenderLayer*>::iterator layerEnd = m_clientLayers.end();
135     for (HashSet<RenderLayer*>::iterator it = m_clientLayers.begin(); it != layerEnd; ++it)
136         (*it)->filterNeedsRepaint();
137 }
138
139 void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
140 {
141     ASSERT(client);
142     ASSERT(!m_clients.isEmpty());
143
144     switch (mode) {
145     case LayoutAndBoundariesInvalidation:
146     case BoundariesInvalidation:
147         client->setNeedsBoundariesUpdate();
148         break;
149     case RepaintInvalidation:
150         if (client->view()) {
151             if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isInPerformLayout())
152                 client->setShouldDoFullRepaintAfterLayout(true);
153             else
154                 client->repaint();
155         }
156         break;
157     case ParentOnlyInvalidation:
158         break;
159     }
160 }
161
162 void RenderSVGResourceContainer::addClient(RenderObject* client)
163 {
164     ASSERT(client);
165     m_clients.add(client);
166     clearInvalidationMask();
167 }
168
169 void RenderSVGResourceContainer::removeClient(RenderObject* client)
170 {
171     ASSERT(client);
172     removeClientFromCache(client, false);
173     m_clients.remove(client);
174 }
175
176 void RenderSVGResourceContainer::addClientRenderLayer(Node* node)
177 {
178     ASSERT(node);
179     if (!node->renderer() || !node->renderer()->hasLayer())
180         return;
181     m_clientLayers.add(toRenderLayerModelObject(node->renderer())->layer());
182     clearInvalidationMask();
183 }
184
185 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client)
186 {
187     ASSERT(client);
188     m_clientLayers.add(client);
189     clearInvalidationMask();
190 }
191
192 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client)
193 {
194     ASSERT(client);
195     m_clientLayers.remove(client);
196 }
197
198 void RenderSVGResourceContainer::invalidateCacheAndMarkForLayout(SubtreeLayoutScope* layoutScope)
199 {
200     if (selfNeedsLayout())
201         return;
202
203     setNeedsLayout(MarkContainingBlockChain, layoutScope);
204
205     if (everHadLayout())
206         removeAllClientsFromCache();
207 }
208
209 void RenderSVGResourceContainer::registerResource()
210 {
211     SVGDocumentExtensions& extensions = svgExtensionsFromElement(element());
212     if (!extensions.hasPendingResource(m_id)) {
213         extensions.addResource(m_id, this);
214         return;
215     }
216
217     OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions.removePendingResource(m_id));
218
219     // Cache us with the new id.
220     extensions.addResource(m_id, this);
221
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();
228         if (!renderer)
229             continue;
230
231         StyleDifference diff;
232         diff.setNeedsFullLayout();
233         SVGResourcesCache::clientStyleChanged(renderer, diff, renderer->style());
234         renderer->setNeedsLayout();
235     }
236 }
237
238 AffineTransform RenderSVGResourceContainer::computeResourceSpaceTransform(RenderObject* object, const AffineTransform& baseTransform, const SVGRenderStyle* svgStyle, unsigned short resourceMode)
239 {
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;
249     }
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);
254     }
255     return computedSpaceTransform;
256 }
257
258 bool RenderSVGResourceContainer::shouldTransformOnTextPainting(RenderObject* object, AffineTransform& resourceTransform)
259 {
260     ASSERT_UNUSED(object, object);
261
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());
264
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)
270         return false;
271     resourceTransform.scale(scalingFactor);
272     return true;
273 }
274
275 // FIXME: This does not belong here.
276 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
277 {
278     if (!object->isSVGShape())
279         return resourceTransform;
280
281     SVGGraphicsElement* element = toSVGGraphicsElement(object->node());
282     AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate);
283     transform *= resourceTransform;
284     return transform;
285 }
286
287 }