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#include "RenderSVGResourceContainer.h"
22
23#include "RenderLayer.h"
24#include "RenderSVGRoot.h"
25#include "RenderView.h"
26#include "SVGRenderingContext.h"
27#include "SVGResourcesCache.h"
28#include <wtf/IsoMallocInlines.h>
29#include <wtf/StackStats.h>
30
31namespace WebCore {
32
33WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGResourceContainer);
34
35static inline SVGDocumentExtensions& svgExtensionsFromElement(SVGElement& element)
36{
37 return element.document().accessSVGExtensions();
38}
39
40RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement& element, RenderStyle&& style)
41 : RenderSVGHiddenContainer(element, WTFMove(style))
42 , m_id(element.getIdAttribute())
43{
44}
45
46RenderSVGResourceContainer::~RenderSVGResourceContainer() = default;
47
48void RenderSVGResourceContainer::layout()
49{
50 StackStats::LayoutCheckPoint layoutCheckPoint;
51 // Invalidate all resources if our layout changed.
52 if (selfNeedsClientInvalidation())
53 RenderSVGRoot::addResourceForClientInvalidation(this);
54
55 RenderSVGHiddenContainer::layout();
56}
57
58void RenderSVGResourceContainer::willBeDestroyed()
59{
60 SVGResourcesCache::resourceDestroyed(*this);
61
62 if (m_registered) {
63 svgExtensionsFromElement(element()).removeResource(m_id);
64 m_registered = false;
65 }
66
67 RenderSVGHiddenContainer::willBeDestroyed();
68}
69
70void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
71{
72 RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
73
74 if (!m_registered) {
75 m_registered = true;
76 registerResource();
77 }
78}
79
80void RenderSVGResourceContainer::idChanged()
81{
82 // Invalidate all our current clients.
83 removeAllClientsFromCache();
84
85 // Remove old id, that is guaranteed to be present in cache.
86 svgExtensionsFromElement(element()).removeResource(m_id);
87 m_id = element().getIdAttribute();
88
89 registerResource();
90}
91
92void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
93{
94 if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating)
95 return;
96
97 m_isInvalidating = true;
98 bool needsLayout = mode == LayoutAndBoundariesInvalidation;
99 bool markForInvalidation = mode != ParentOnlyInvalidation;
100 auto* root = SVGRenderSupport::findTreeRootObject(*this);
101
102 for (auto* client : m_clients) {
103 // We should not mark any client outside the current root for invalidation
104 if (root != SVGRenderSupport::findTreeRootObject(*client))
105 continue;
106
107 if (is<RenderSVGResourceContainer>(*client)) {
108 downcast<RenderSVGResourceContainer>(*client).removeAllClientsFromCache(markForInvalidation);
109 continue;
110 }
111
112 if (markForInvalidation)
113 markClientForInvalidation(*client, mode);
114
115 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*client, needsLayout);
116 }
117
118 markAllClientLayersForInvalidation();
119
120 m_isInvalidating = false;
121}
122
123void RenderSVGResourceContainer::markAllClientLayersForInvalidation()
124{
125 if (m_clientLayers.isEmpty())
126 return;
127 if ((*m_clientLayers.begin())->renderer().renderTreeBeingDestroyed())
128 return;
129 for (auto* clientLayer : m_clientLayers)
130 clientLayer->filterNeedsRepaint();
131}
132
133void RenderSVGResourceContainer::markClientForInvalidation(RenderObject& client, InvalidationMode mode)
134{
135 ASSERT(!m_clients.isEmpty());
136
137 switch (mode) {
138 case LayoutAndBoundariesInvalidation:
139 case BoundariesInvalidation:
140 client.setNeedsBoundariesUpdate();
141 break;
142 case RepaintInvalidation:
143 if (!client.renderTreeBeingDestroyed())
144 client.repaint();
145 break;
146 case ParentOnlyInvalidation:
147 break;
148 }
149}
150
151void RenderSVGResourceContainer::addClient(RenderElement& client)
152{
153 m_clients.add(&client);
154}
155
156void RenderSVGResourceContainer::removeClient(RenderElement& client)
157{
158 removeClientFromCache(client, false);
159 m_clients.remove(&client);
160}
161
162void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client)
163{
164 ASSERT(client);
165 m_clientLayers.add(client);
166}
167
168void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client)
169{
170 ASSERT(client);
171 m_clientLayers.remove(client);
172}
173
174void RenderSVGResourceContainer::registerResource()
175{
176 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element());
177 if (!extensions.isIdOfPendingResource(m_id)) {
178 extensions.addResource(m_id, *this);
179 return;
180 }
181
182 std::unique_ptr<SVGDocumentExtensions::PendingElements> clients = extensions.removePendingResource(m_id);
183
184 // Cache us with the new id.
185 extensions.addResource(m_id, *this);
186
187 // Update cached resources of pending clients.
188 for (auto* client : *clients) {
189 ASSERT(client->hasPendingResources());
190 extensions.clearHasPendingResourcesIfPossible(*client);
191 auto* renderer = client->renderer();
192 if (!renderer)
193 continue;
194 SVGResourcesCache::clientStyleChanged(*renderer, StyleDifference::Layout, renderer->style());
195 renderer->setNeedsLayout();
196 }
197}
198
199bool RenderSVGResourceContainer::shouldTransformOnTextPainting(const RenderElement& renderer, AffineTransform& resourceTransform)
200{
201#if USE(CG)
202 UNUSED_PARAM(renderer);
203 UNUSED_PARAM(resourceTransform);
204 return false;
205#else
206 // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods.
207 ASSERT(renderer.isSVGText() || renderer.isSVGTextPath() || renderer.isSVGInline());
208
209 // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows.
210 // So, we use that scaling factor here, too, and then push it down to pattern or gradient space
211 // in order to keep the pattern or gradient correctly scaled.
212 float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(renderer);
213 if (scalingFactor == 1)
214 return false;
215 resourceTransform.scale(scalingFactor);
216 return true;
217#endif
218}
219
220// FIXME: This does not belong here.
221AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
222{
223 if (!object->isSVGShape())
224 return resourceTransform;
225
226 SVGGraphicsElement* element = downcast<SVGGraphicsElement>(object->node());
227 AffineTransform transform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
228 transform *= resourceTransform;
229 return transform;
230}
231
232}
233