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 "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
21
22 #include <memory>
23 #include "third_party/blink/renderer/core/html_names.h"
24 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
25 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h"
26 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
27 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h"
28 #include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
29
30 namespace blink {
31
32 SVGResourcesCache::SVGResourcesCache() = default;
33
34 SVGResourcesCache::~SVGResourcesCache() = default;
35
AddResourcesFromLayoutObject(LayoutObject & object,const ComputedStyle & style)36 SVGResources* SVGResourcesCache::AddResourcesFromLayoutObject(
37 LayoutObject& object,
38 const ComputedStyle& style) {
39 DCHECK(!cache_.Contains(&object));
40
41 // Build a list of all resources associated with the passed LayoutObject.
42 std::unique_ptr<SVGResources> new_resources =
43 SVGResources::BuildResources(object, style);
44 if (!new_resources)
45 return nullptr;
46
47 // Put object in cache.
48 SVGResources* resources =
49 cache_.Set(&object, std::move(new_resources)).stored_value->value.get();
50
51 // Run cycle-detection _afterwards_, so self-references can be caught as well.
52 HashSet<LayoutSVGResourceContainer*> resource_set;
53 resources->BuildSetOfResources(resource_set);
54
55 SVGResourcesCycleSolver solver;
56 for (auto* resource_container : resource_set) {
57 if (resource_container->FindCycle(solver))
58 resources->ClearReferencesTo(resource_container);
59 }
60 return resources;
61 }
62
RemoveResourcesFromLayoutObject(LayoutObject & object)63 bool SVGResourcesCache::RemoveResourcesFromLayoutObject(LayoutObject& object) {
64 std::unique_ptr<SVGResources> resources = cache_.Take(&object);
65 return !!resources;
66 }
67
68 SVGResourcesCache::ResourceUpdateInfo
UpdateResourcesFromLayoutObject(LayoutObject & object,const ComputedStyle & new_style)69 SVGResourcesCache::UpdateResourcesFromLayoutObject(
70 LayoutObject& object,
71 const ComputedStyle& new_style) {
72 std::unique_ptr<SVGResources> old_resources = cache_.Take(&object);
73 SVGResources* new_resources = AddResourcesFromLayoutObject(object, new_style);
74 return {
75 old_resources || new_resources,
76 SVGResources::DifferenceNeedsLayout(old_resources.get(), new_resources)};
77 }
78
ResourcesCache(Document & document)79 static inline SVGResourcesCache& ResourcesCache(Document& document) {
80 return document.AccessSVGExtensions().ResourcesCache();
81 }
82
CachedResourcesForLayoutObject(const LayoutObject & layout_object)83 SVGResources* SVGResourcesCache::CachedResourcesForLayoutObject(
84 const LayoutObject& layout_object) {
85 return ResourcesCache(layout_object.GetDocument()).cache_.at(&layout_object);
86 }
87
ClientLayoutChanged(LayoutObject & object)88 void SVGResourcesCache::ClientLayoutChanged(LayoutObject& object) {
89 SVGResources* resources = CachedResourcesForLayoutObject(object);
90 if (!resources)
91 return;
92 // Invalidate the resources if either the LayoutObject itself changed,
93 // or we have filter resources, which could depend on the layout of children.
94 if (!object.SelfNeedsLayout() && !resources->Filter())
95 return;
96 SVGElementResourceClient* client = SVGResources::GetClient(object);
97 InvalidationModeMask invalidation_flags =
98 resources->RemoveClientFromCacheAffectingObjectBounds(*client);
99 if (LayoutSVGResourcePaintServer* fill = resources->Fill()) {
100 fill->RemoveClientFromCache(*client);
101 invalidation_flags |= SVGResourceClient::kPaintInvalidation;
102 }
103 if (LayoutSVGResourcePaintServer* stroke = resources->Stroke()) {
104 stroke->RemoveClientFromCache(*client);
105 invalidation_flags |= SVGResourceClient::kPaintInvalidation;
106 }
107 if (invalidation_flags) {
108 LayoutSVGResourceContainer::MarkClientForInvalidation(object,
109 invalidation_flags);
110 }
111 }
112
LayoutObjectCanHaveResources(const LayoutObject & layout_object)113 static inline bool LayoutObjectCanHaveResources(
114 const LayoutObject& layout_object) {
115 return layout_object.GetNode() && layout_object.GetNode()->IsSVGElement() &&
116 !layout_object.IsSVGInlineText();
117 }
118
IsLayoutObjectOfResourceContainer(const LayoutObject & layout_object)119 static inline bool IsLayoutObjectOfResourceContainer(
120 const LayoutObject& layout_object) {
121 const LayoutObject* current = &layout_object;
122 while (current) {
123 if (current->IsSVGResourceContainer())
124 return true;
125 current = current->Parent();
126 }
127 return false;
128 }
129
ClientStyleChanged(LayoutObject & layout_object,StyleDifference diff,const ComputedStyle & new_style)130 void SVGResourcesCache::ClientStyleChanged(LayoutObject& layout_object,
131 StyleDifference diff,
132 const ComputedStyle& new_style) {
133 DCHECK(layout_object.GetNode());
134 DCHECK(layout_object.GetNode()->IsSVGElement());
135
136 if (!diff.HasDifference() || !layout_object.Parent())
137 return;
138
139 // LayoutObjects for SVGFE*Element should not be calling this function.
140 DCHECK(!layout_object.IsSVGFilterPrimitive());
141
142 // Dynamic changes of CSS properties like 'clip-path' may require us to
143 // recompute the associated resources for a LayoutObject.
144 // TODO(fs): Avoid passing in a useless StyleDifference, but instead compare
145 // oldStyle/newStyle to see which resources changed to be able to selectively
146 // rebuild individual resources, instead of all of them.
147 bool needs_layout = false;
148 if (LayoutObjectCanHaveResources(layout_object)) {
149 SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument());
150 auto update_info =
151 cache.UpdateResourcesFromLayoutObject(layout_object, new_style);
152 if (update_info) {
153 layout_object.SetNeedsPaintPropertyUpdate();
154 // Since the visual rect has the bounds of the clip-path, mask and filter
155 // baked in, and the visual rect is updated during layout, we need to
156 // trigger layout if the style change could somehow have affected the
157 // bounds that form the visual rect.
158 needs_layout = update_info.needs_layout;
159 }
160 }
161
162 // If this layoutObject is the child of ResourceContainer and it require
163 // repainting that changes of CSS properties such as 'visibility',
164 // request repainting.
165 needs_layout |= diff.NeedsPaintInvalidation() &&
166 IsLayoutObjectOfResourceContainer(layout_object);
167
168 LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
169 layout_object, needs_layout);
170 }
171
ResourceReferenceChanged(LayoutObject & layout_object)172 void SVGResourcesCache::ResourceReferenceChanged(LayoutObject& layout_object) {
173 DCHECK(layout_object.IsSVG());
174 DCHECK(layout_object.GetNode());
175 DCHECK(layout_object.GetNode()->IsSVGElement());
176
177 if (!layout_object.Parent())
178 return;
179
180 // Only LayoutObjects that can actually have resources should be pending and
181 // hence be able to call this method.
182 DCHECK(LayoutObjectCanHaveResources(layout_object));
183
184 SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument());
185 if (cache.UpdateResourcesFromLayoutObject(layout_object,
186 layout_object.StyleRef())) {
187 layout_object.SetNeedsPaintPropertyUpdate();
188 }
189
190 LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
191 layout_object, true);
192 }
193
ClientWasAddedToTree(LayoutObject & layout_object)194 void SVGResourcesCache::ClientWasAddedToTree(LayoutObject& layout_object) {
195 if (!layout_object.GetNode())
196 return;
197 LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
198 layout_object, false);
199
200 if (!LayoutObjectCanHaveResources(layout_object))
201 return;
202 SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument());
203 if (cache.AddResourcesFromLayoutObject(layout_object,
204 layout_object.StyleRef()))
205 layout_object.SetNeedsPaintPropertyUpdate();
206 }
207
ClientWillBeRemovedFromTree(LayoutObject & layout_object)208 void SVGResourcesCache::ClientWillBeRemovedFromTree(
209 LayoutObject& layout_object) {
210 if (!layout_object.GetNode())
211 return;
212 LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(
213 layout_object, false);
214
215 if (!LayoutObjectCanHaveResources(layout_object))
216 return;
217 SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument());
218 if (cache.RemoveResourcesFromLayoutObject(layout_object))
219 layout_object.SetNeedsPaintPropertyUpdate();
220 }
221
ClientDestroyed(LayoutObject & layout_object)222 void SVGResourcesCache::ClientDestroyed(LayoutObject& layout_object) {
223 SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument());
224 cache.RemoveResourcesFromLayoutObject(layout_object);
225 }
226
TemporaryStyleScope(LayoutObject & layout_object,const ComputedStyle & style,const ComputedStyle & temporary_style)227 SVGResourcesCache::TemporaryStyleScope::TemporaryStyleScope(
228 LayoutObject& layout_object,
229 const ComputedStyle& style,
230 const ComputedStyle& temporary_style)
231 : layout_object_(layout_object),
232 original_style_(style),
233 temporary_style_(temporary_style),
234 styles_are_equal_(style == temporary_style) {
235 if (styles_are_equal_)
236 return;
237 DCHECK(LayoutObjectCanHaveResources(layout_object_));
238 auto& element = To<SVGElement>(*layout_object_.GetNode());
239 SVGResources::UpdatePaints(element, nullptr, temporary_style_);
240 SwitchTo(temporary_style);
241 }
242
~TemporaryStyleScope()243 SVGResourcesCache::TemporaryStyleScope::~TemporaryStyleScope() {
244 if (styles_are_equal_)
245 return;
246 auto& element = To<SVGElement>(*layout_object_.GetNode());
247 SVGResources::ClearPaints(element, &temporary_style_);
248 SwitchTo(original_style_);
249 }
250
SwitchTo(const ComputedStyle & style)251 void SVGResourcesCache::TemporaryStyleScope::SwitchTo(
252 const ComputedStyle& style) {
253 DCHECK(!styles_are_equal_);
254 SVGResourcesCache& cache = ResourcesCache(layout_object_.GetDocument());
255 cache.UpdateResourcesFromLayoutObject(layout_object_, style);
256 }
257
258 } // namespace blink
259