1 /*
2 * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "third_party/blink/renderer/core/svg/svg_gradient_element.h"
23
24 #include "third_party/blink/renderer/core/css/style_change_reason.h"
25 #include "third_party/blink/renderer/core/dom/element_traversal.h"
26 #include "third_party/blink/renderer/core/dom/id_target_observer.h"
27 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
28 #include "third_party/blink/renderer/core/svg/gradient_attributes.h"
29 #include "third_party/blink/renderer/core/svg/svg_enumeration_map.h"
30 #include "third_party/blink/renderer/core/svg/svg_stop_element.h"
31 #include "third_party/blink/renderer/core/svg/svg_transform_list.h"
32 #include "third_party/blink/renderer/platform/heap/heap.h"
33
34 namespace blink {
35
36 template <>
GetEnumerationMap()37 const SVGEnumerationMap& GetEnumerationMap<SVGSpreadMethodType>() {
38 static const SVGEnumerationMap::Entry enum_items[] = {
39 {kSVGSpreadMethodPad, "pad"},
40 {kSVGSpreadMethodReflect, "reflect"},
41 {kSVGSpreadMethodRepeat, "repeat"},
42 };
43 static const SVGEnumerationMap entries(enum_items);
44 return entries;
45 }
46
SVGGradientElement(const QualifiedName & tag_name,Document & document)47 SVGGradientElement::SVGGradientElement(const QualifiedName& tag_name,
48 Document& document)
49 : SVGElement(tag_name, document),
50 SVGURIReference(this),
51 gradient_transform_(MakeGarbageCollected<SVGAnimatedTransformList>(
52 this,
53 svg_names::kGradientTransformAttr,
54 CSSPropertyID::kTransform)),
55 spread_method_(
56 MakeGarbageCollected<SVGAnimatedEnumeration<SVGSpreadMethodType>>(
57 this,
58 svg_names::kSpreadMethodAttr,
59 kSVGSpreadMethodPad)),
60 gradient_units_(MakeGarbageCollected<
61 SVGAnimatedEnumeration<SVGUnitTypes::SVGUnitType>>(
62 this,
63 svg_names::kGradientUnitsAttr,
64 SVGUnitTypes::kSvgUnitTypeObjectboundingbox)) {
65 AddToPropertyMap(gradient_transform_);
66 AddToPropertyMap(spread_method_);
67 AddToPropertyMap(gradient_units_);
68 }
69
Trace(Visitor * visitor)70 void SVGGradientElement::Trace(Visitor* visitor) {
71 visitor->Trace(gradient_transform_);
72 visitor->Trace(spread_method_);
73 visitor->Trace(gradient_units_);
74 visitor->Trace(target_id_observer_);
75 SVGElement::Trace(visitor);
76 SVGURIReference::Trace(visitor);
77 }
78
BuildPendingResource()79 void SVGGradientElement::BuildPendingResource() {
80 ClearResourceReferences();
81 if (!isConnected())
82 return;
83 Element* target = ObserveTarget(target_id_observer_, *this);
84 if (auto* gradient = DynamicTo<SVGGradientElement>(target))
85 AddReferenceTo(gradient);
86
87 InvalidateGradient(layout_invalidation_reason::kSvgResourceInvalidated);
88 }
89
ClearResourceReferences()90 void SVGGradientElement::ClearResourceReferences() {
91 UnobserveTarget(target_id_observer_);
92 RemoveAllOutgoingReferences();
93 }
94
CollectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableCSSPropertyValueSet * style)95 void SVGGradientElement::CollectStyleForPresentationAttribute(
96 const QualifiedName& name,
97 const AtomicString& value,
98 MutableCSSPropertyValueSet* style) {
99 if (name == svg_names::kGradientTransformAttr) {
100 AddPropertyToPresentationAttributeStyle(
101 style, CSSPropertyID::kTransform,
102 *gradient_transform_->CurrentValue()->CssValue());
103 return;
104 }
105 SVGElement::CollectStyleForPresentationAttribute(name, value, style);
106 }
107
SvgAttributeChanged(const QualifiedName & attr_name)108 void SVGGradientElement::SvgAttributeChanged(const QualifiedName& attr_name) {
109 if (attr_name == svg_names::kGradientTransformAttr) {
110 InvalidateSVGPresentationAttributeStyle();
111 SetNeedsStyleRecalc(kLocalStyleChange,
112 StyleChangeReasonForTracing::FromAttribute(attr_name));
113 }
114
115 if (attr_name == svg_names::kGradientUnitsAttr ||
116 attr_name == svg_names::kGradientTransformAttr ||
117 attr_name == svg_names::kSpreadMethodAttr) {
118 SVGElement::InvalidationGuard invalidation_guard(this);
119 InvalidateGradient(layout_invalidation_reason::kAttributeChanged);
120 return;
121 }
122
123 if (SVGURIReference::IsKnownAttribute(attr_name)) {
124 SVGElement::InvalidationGuard invalidation_guard(this);
125 BuildPendingResource();
126 return;
127 }
128
129 SVGElement::SvgAttributeChanged(attr_name);
130 }
131
InsertedInto(ContainerNode & root_parent)132 Node::InsertionNotificationRequest SVGGradientElement::InsertedInto(
133 ContainerNode& root_parent) {
134 SVGElement::InsertedInto(root_parent);
135 if (root_parent.isConnected())
136 BuildPendingResource();
137 return kInsertionDone;
138 }
139
RemovedFrom(ContainerNode & root_parent)140 void SVGGradientElement::RemovedFrom(ContainerNode& root_parent) {
141 SVGElement::RemovedFrom(root_parent);
142 if (root_parent.isConnected())
143 ClearResourceReferences();
144 }
145
ChildrenChanged(const ChildrenChange & change)146 void SVGGradientElement::ChildrenChanged(const ChildrenChange& change) {
147 SVGElement::ChildrenChanged(change);
148
149 if (!change.ByParser())
150 InvalidateGradient(layout_invalidation_reason::kChildChanged);
151 }
152
InvalidateGradient(LayoutInvalidationReasonForTracing reason)153 void SVGGradientElement::InvalidateGradient(
154 LayoutInvalidationReasonForTracing reason) {
155 if (auto* layout_object = ToLayoutSVGResourceContainer(GetLayoutObject()))
156 layout_object->InvalidateCacheAndMarkForLayout(reason);
157 }
158
InvalidateDependentGradients()159 void SVGGradientElement::InvalidateDependentGradients() {
160 NotifyIncomingReferences([](SVGElement& element) {
161 if (auto* gradient = DynamicTo<SVGGradientElement>(element)) {
162 gradient->InvalidateGradient(
163 layout_invalidation_reason::kSvgResourceInvalidated);
164 }
165 });
166 }
167
CollectCommonAttributes(GradientAttributes & attributes) const168 void SVGGradientElement::CollectCommonAttributes(
169 GradientAttributes& attributes) const {
170 if (!attributes.HasSpreadMethod() && spreadMethod()->IsSpecified())
171 attributes.SetSpreadMethod(spreadMethod()->CurrentValue()->EnumValue());
172
173 if (!attributes.HasGradientUnits() && gradientUnits()->IsSpecified())
174 attributes.SetGradientUnits(gradientUnits()->CurrentValue()->EnumValue());
175
176 if (!attributes.HasGradientTransform() &&
177 HasTransform(SVGElement::kExcludeMotionTransform)) {
178 attributes.SetGradientTransform(
179 CalculateTransform(SVGElement::kExcludeMotionTransform));
180 }
181
182 if (!attributes.HasStops()) {
183 const Vector<Gradient::ColorStop>& stops(BuildStops());
184 if (!stops.IsEmpty())
185 attributes.SetStops(stops);
186 }
187 }
188
ReferencedElement() const189 const SVGGradientElement* SVGGradientElement::ReferencedElement() const {
190 // Respect xlink:href, take attributes from referenced element.
191 return DynamicTo<SVGGradientElement>(
192 TargetElementFromIRIString(HrefString(), GetTreeScope()));
193 }
194
BuildStops() const195 Vector<Gradient::ColorStop> SVGGradientElement::BuildStops() const {
196 Vector<Gradient::ColorStop> stops;
197
198 float previous_offset = 0.0f;
199 for (const SVGStopElement& stop :
200 Traversal<SVGStopElement>::ChildrenOf(*this)) {
201 // Figure out right monotonic offset.
202 float offset = stop.offset()->CurrentValue()->Value();
203 offset = std::min(std::max(previous_offset, offset), 1.0f);
204 previous_offset = offset;
205
206 stops.push_back(
207 Gradient::ColorStop(offset, stop.StopColorIncludingOpacity()));
208 }
209 return stops;
210 }
211
212 } // namespace blink
213