1 | /* |
2 | * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> |
3 | * Copyright (C) 2007 Rob Buis <buis@kde.org> |
4 | * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> |
5 | * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public License |
18 | * along with this library; see the file COPYING.LIB. If not, write to |
19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | * Boston, MA 02110-1301, USA. |
21 | */ |
22 | |
23 | #include "config.h" |
24 | #include "RenderSVGResource.h" |
25 | |
26 | #include "Frame.h" |
27 | #include "FrameView.h" |
28 | #include "RenderSVGResourceClipper.h" |
29 | #include "RenderSVGResourceFilter.h" |
30 | #include "RenderSVGResourceMasker.h" |
31 | #include "RenderSVGResourceSolidColor.h" |
32 | #include "RenderView.h" |
33 | #include "SVGResources.h" |
34 | #include "SVGResourcesCache.h" |
35 | #include "SVGURIReference.h" |
36 | |
37 | namespace WebCore { |
38 | |
39 | static inline bool inheritColorFromParentStyleIfNeeded(RenderElement& object, bool applyToFill, Color& color) |
40 | { |
41 | if (color.isValid()) |
42 | return true; |
43 | if (!object.parent()) |
44 | return false; |
45 | const SVGRenderStyle& parentSVGStyle = object.parent()->style().svgStyle(); |
46 | color = applyToFill ? parentSVGStyle.fillPaintColor() : parentSVGStyle.strokePaintColor(); |
47 | return true; |
48 | } |
49 | |
50 | static inline RenderSVGResource* requestPaintingResource(RenderSVGResourceMode mode, RenderElement& renderer, const RenderStyle& style, Color& fallbackColor) |
51 | { |
52 | const SVGRenderStyle& svgStyle = style.svgStyle(); |
53 | |
54 | bool isRenderingMask = renderer.view().frameView().paintBehavior().contains(PaintBehavior::RenderingSVGMask); |
55 | |
56 | // If we have no fill/stroke, return nullptr. |
57 | if (mode == RenderSVGResourceMode::ApplyToFill) { |
58 | // When rendering the mask for a RenderSVGResourceClipper, always use the initial fill paint server, and ignore stroke. |
59 | if (isRenderingMask) { |
60 | RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource(); |
61 | colorResource->setColor(SVGRenderStyle::initialFillPaintColor()); |
62 | return colorResource; |
63 | } |
64 | |
65 | if (!svgStyle.hasFill()) |
66 | return nullptr; |
67 | } else { |
68 | if (!svgStyle.hasStroke() || isRenderingMask) |
69 | return nullptr; |
70 | } |
71 | |
72 | bool applyToFill = mode == RenderSVGResourceMode::ApplyToFill; |
73 | SVGPaintType paintType = applyToFill ? svgStyle.fillPaintType() : svgStyle.strokePaintType(); |
74 | if (paintType == SVGPaintType::None) |
75 | return nullptr; |
76 | |
77 | Color color; |
78 | switch (paintType) { |
79 | case SVGPaintType::CurrentColor: |
80 | case SVGPaintType::RGBColor: |
81 | case SVGPaintType::URICurrentColor: |
82 | case SVGPaintType::URIRGBColor: |
83 | color = applyToFill ? svgStyle.fillPaintColor() : svgStyle.strokePaintColor(); |
84 | break; |
85 | default: |
86 | break; |
87 | } |
88 | |
89 | if (style.insideLink() == InsideLink::InsideVisited) { |
90 | // FIXME: This code doesn't support the uri component of the visited link paint, https://bugs.webkit.org/show_bug.cgi?id=70006 |
91 | SVGPaintType visitedPaintType = applyToFill ? svgStyle.visitedLinkFillPaintType() : svgStyle.visitedLinkStrokePaintType(); |
92 | |
93 | // For SVGPaintType::CurrentColor, 'color' already contains the 'visitedColor'. |
94 | if (visitedPaintType < SVGPaintType::URINone && visitedPaintType != SVGPaintType::CurrentColor) { |
95 | const Color& visitedColor = applyToFill ? svgStyle.visitedLinkFillPaintColor() : svgStyle.visitedLinkStrokePaintColor(); |
96 | if (visitedColor.isValid()) |
97 | color = visitedColor.colorWithAlpha(color.alphaAsFloat()); |
98 | } |
99 | } |
100 | |
101 | // If the primary resource is just a color, return immediately. |
102 | RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource(); |
103 | if (paintType < SVGPaintType::URINone) { |
104 | if (!inheritColorFromParentStyleIfNeeded(renderer, applyToFill, color)) |
105 | return nullptr; |
106 | |
107 | colorResource->setColor(color); |
108 | return colorResource; |
109 | } |
110 | |
111 | // If no resources are associated with the given renderer, return the color resource. |
112 | auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer); |
113 | if (!resources) { |
114 | if (paintType == SVGPaintType::URINone || !inheritColorFromParentStyleIfNeeded(renderer, applyToFill, color)) |
115 | return nullptr; |
116 | |
117 | colorResource->setColor(color); |
118 | return colorResource; |
119 | } |
120 | |
121 | // If the requested resource is not available, return the color resource. |
122 | RenderSVGResource* uriResource = mode == RenderSVGResourceMode::ApplyToFill ? resources->fill() : resources->stroke(); |
123 | if (!uriResource) { |
124 | if (!inheritColorFromParentStyleIfNeeded(renderer, applyToFill, color)) |
125 | return nullptr; |
126 | |
127 | colorResource->setColor(color); |
128 | return colorResource; |
129 | } |
130 | |
131 | // The paint server resource exists, though it may be invalid (pattern with width/height=0). Pass the fallback color to our caller |
132 | // so it can use the solid color painting resource, if applyResource() on the URI resource failed. |
133 | fallbackColor = color; |
134 | return uriResource; |
135 | } |
136 | |
137 | RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderElement& renderer, const RenderStyle& style, Color& fallbackColor) |
138 | { |
139 | return requestPaintingResource(RenderSVGResourceMode::ApplyToFill, renderer, style, fallbackColor); |
140 | } |
141 | |
142 | RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderElement& renderer, const RenderStyle& style, Color& fallbackColor) |
143 | { |
144 | return requestPaintingResource(RenderSVGResourceMode::ApplyToStroke, renderer, style, fallbackColor); |
145 | } |
146 | |
147 | RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource() |
148 | { |
149 | static RenderSVGResourceSolidColor* s_sharedSolidPaintingResource = 0; |
150 | if (!s_sharedSolidPaintingResource) |
151 | s_sharedSolidPaintingResource = new RenderSVGResourceSolidColor; |
152 | return s_sharedSolidPaintingResource; |
153 | } |
154 | |
155 | static inline void removeFromCacheAndInvalidateDependencies(RenderElement& renderer, bool needsLayout) |
156 | { |
157 | if (auto* resources = SVGResourcesCache::cachedResourcesForRenderer(renderer)) { |
158 | if (RenderSVGResourceFilter* filter = resources->filter()) |
159 | filter->removeClientFromCache(renderer); |
160 | |
161 | if (RenderSVGResourceMasker* masker = resources->masker()) |
162 | masker->removeClientFromCache(renderer); |
163 | |
164 | if (RenderSVGResourceClipper* clipper = resources->clipper()) |
165 | clipper->removeClientFromCache(renderer); |
166 | } |
167 | |
168 | if (!is<SVGElement>(renderer.element())) |
169 | return; |
170 | auto* dependencies = renderer.document().accessSVGExtensions().setOfElementsReferencingTarget(downcast<SVGElement>(*renderer.element())); |
171 | if (!dependencies) |
172 | return; |
173 | |
174 | for (auto* element : *dependencies) { |
175 | if (auto* renderer = element->renderer()) { |
176 | // We allow cycles in SVGDocumentExtensions reference sets in order to avoid expensive |
177 | // reference graph adjustments on changes, so we need to break possible cycles here. |
178 | static NeverDestroyed<HashSet<SVGElement*>> invalidatingDependencies; |
179 | if (UNLIKELY(!invalidatingDependencies.get().add(element).isNewEntry)) { |
180 | // Reference cycle: we are in process of invalidating this dependant. |
181 | continue; |
182 | } |
183 | RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer, needsLayout); |
184 | invalidatingDependencies.get().remove(element); |
185 | } |
186 | } |
187 | } |
188 | |
189 | void RenderSVGResource::markForLayoutAndParentResourceInvalidation(RenderObject& object, bool needsLayout) |
190 | { |
191 | ASSERT(object.node()); |
192 | |
193 | if (needsLayout && !object.renderTreeBeingDestroyed()) |
194 | object.setNeedsLayout(); |
195 | |
196 | if (is<RenderElement>(object)) |
197 | removeFromCacheAndInvalidateDependencies(downcast<RenderElement>(object), needsLayout); |
198 | |
199 | // Invalidate resources in ancestor chain, if needed. |
200 | auto current = object.parent(); |
201 | while (current) { |
202 | removeFromCacheAndInvalidateDependencies(*current, needsLayout); |
203 | |
204 | if (is<RenderSVGResourceContainer>(*current)) { |
205 | // This will process the rest of the ancestors. |
206 | downcast<RenderSVGResourceContainer>(*current).removeAllClientsFromCache(); |
207 | break; |
208 | } |
209 | |
210 | current = current->parent(); |
211 | } |
212 | } |
213 | |
214 | } |
215 | |