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