1 /*
2  * Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4  * Copyright (C) 2008 Apple Inc. All rights reserved.
5  * Copyright (C) Research In Motion Limited 2011. 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 "third_party/blink/renderer/core/svg/svg_animate_element.h"
24 
25 #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h"
26 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
27 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
28 #include "third_party/blink/renderer/core/css/style_change_reason.h"
29 #include "third_party/blink/renderer/core/dom/document.h"
30 #include "third_party/blink/renderer/core/dom/qualified_name.h"
31 #include "third_party/blink/renderer/core/svg/properties/svg_animated_property.h"
32 #include "third_party/blink/renderer/core/svg/properties/svg_property.h"
33 #include "third_party/blink/renderer/core/svg/svg_animated_color.h"
34 #include "third_party/blink/renderer/core/svg/svg_length.h"
35 #include "third_party/blink/renderer/core/svg/svg_length_list.h"
36 #include "third_party/blink/renderer/core/svg/svg_number.h"
37 #include "third_party/blink/renderer/core/svg/svg_string.h"
38 #include "third_party/blink/renderer/core/xlink_names.h"
39 #include "third_party/blink/renderer/platform/heap/heap.h"
40 
41 namespace blink {
42 
43 namespace {
44 
IsTargetAttributeCSSProperty(const SVGElement & target_element,const QualifiedName & attribute_name)45 bool IsTargetAttributeCSSProperty(const SVGElement& target_element,
46                                   const QualifiedName& attribute_name) {
47   return SVGElement::IsAnimatableCSSProperty(attribute_name) ||
48          target_element.IsPresentationAttribute(attribute_name);
49 }
50 
ComputeCSSPropertyValue(SVGElement * element,CSSPropertyID id)51 String ComputeCSSPropertyValue(SVGElement* element, CSSPropertyID id) {
52   DCHECK(element);
53   // TODO(fs): StyleEngine doesn't support document without a frame.
54   // Refer to comment in Element::computedStyle.
55   DCHECK(element->InActiveDocument());
56 
57   // Don't include any properties resulting from CSS Transitions/Animations or
58   // SMIL animations, as we want to retrieve the "base value".
59   element->SetUseOverrideComputedStyle(true);
60   String value = MakeGarbageCollected<CSSComputedStyleDeclaration>(element)
61                      ->GetPropertyValue(id);
62   element->SetUseOverrideComputedStyle(false);
63   return value;
64 }
65 
PropertyValueType(const QualifiedName & attribute_name,const String & value)66 AnimatedPropertyValueType PropertyValueType(const QualifiedName& attribute_name,
67                                             const String& value) {
68   DEFINE_STATIC_LOCAL(const AtomicString, inherit, ("inherit"));
69   if (value.IsEmpty() || value != inherit ||
70       !SVGElement::IsAnimatableCSSProperty(attribute_name))
71     return kRegularPropertyValue;
72   return kInheritValue;
73 }
74 
ConstructQualifiedName(const SVGElement & svg_element,const AtomicString & attribute_name)75 QualifiedName ConstructQualifiedName(const SVGElement& svg_element,
76                                      const AtomicString& attribute_name) {
77   if (attribute_name.IsEmpty())
78     return AnyQName();
79   if (!attribute_name.Contains(':'))
80     return QualifiedName(g_null_atom, attribute_name, g_null_atom);
81 
82   AtomicString prefix;
83   AtomicString local_name;
84   if (!Document::ParseQualifiedName(attribute_name, prefix, local_name,
85                                     IGNORE_EXCEPTION_FOR_TESTING))
86     return AnyQName();
87 
88   const AtomicString& namespace_uri = svg_element.lookupNamespaceURI(prefix);
89   if (namespace_uri.IsEmpty())
90     return AnyQName();
91 
92   QualifiedName resolved_attr_name(g_null_atom, local_name, namespace_uri);
93   // "Animation elements treat attributeName='xlink:href' as being an alias
94   // for targetting the 'href' attribute."
95   // https://svgwg.org/svg2-draft/types.html#__svg__SVGURIReference__href
96   if (resolved_attr_name == xlink_names::kHrefAttr)
97     return svg_names::kHrefAttr;
98   return resolved_attr_name;
99 }
100 
101 }  // unnamed namespace
102 
SVGAnimateElement(Document & document)103 SVGAnimateElement::SVGAnimateElement(Document& document)
104     : SVGAnimateElement(svg_names::kAnimateTag, document) {}
105 
SVGAnimateElement(const QualifiedName & tag_name,Document & document)106 SVGAnimateElement::SVGAnimateElement(const QualifiedName& tag_name,
107                                      Document& document)
108     : SVGAnimationElement(tag_name, document),
109       attribute_name_(AnyQName()),
110       type_(kAnimatedUnknown),
111       css_property_id_(CSSPropertyID::kInvalid),
112       from_property_value_type_(kRegularPropertyValue),
113       to_property_value_type_(kRegularPropertyValue),
114       attribute_type_(kAttributeTypeAuto) {}
115 
116 SVGAnimateElement::~SVGAnimateElement() = default;
117 
IsSVGAnimationAttributeSettingJavaScriptURL(const Attribute & attribute) const118 bool SVGAnimateElement::IsSVGAnimationAttributeSettingJavaScriptURL(
119     const Attribute& attribute) const {
120   if ((attribute.GetName() == svg_names::kFromAttr ||
121        attribute.GetName() == svg_names::kToAttr) &&
122       AttributeValueIsJavaScriptURL(attribute))
123     return true;
124 
125   if (attribute.GetName() == svg_names::kValuesAttr) {
126     Vector<String> parts;
127     if (!ParseValues(attribute.Value(), parts)) {
128       // Assume the worst.
129       return true;
130     }
131     for (const auto& part : parts) {
132       if (ProtocolIsJavaScript(part))
133         return true;
134     }
135   }
136 
137   return SVGSMILElement::IsSVGAnimationAttributeSettingJavaScriptURL(attribute);
138 }
139 
InsertedInto(ContainerNode & root_parent)140 Node::InsertionNotificationRequest SVGAnimateElement::InsertedInto(
141     ContainerNode& root_parent) {
142   SVGAnimationElement::InsertedInto(root_parent);
143   if (root_parent.isConnected()) {
144     SetAttributeName(ConstructQualifiedName(
145         *this, FastGetAttribute(svg_names::kAttributeNameAttr)));
146   }
147   return kInsertionDone;
148 }
149 
RemovedFrom(ContainerNode & root_parent)150 void SVGAnimateElement::RemovedFrom(ContainerNode& root_parent) {
151   if (root_parent.isConnected())
152     SetAttributeName(AnyQName());
153   SVGAnimationElement::RemovedFrom(root_parent);
154 }
155 
ParseAttribute(const AttributeModificationParams & params)156 void SVGAnimateElement::ParseAttribute(
157     const AttributeModificationParams& params) {
158   if (params.name == svg_names::kAttributeTypeAttr) {
159     SetAttributeType(params.new_value);
160     return;
161   }
162   if (params.name == svg_names::kAttributeNameAttr) {
163     SetAttributeName(ConstructQualifiedName(*this, params.new_value));
164     return;
165   }
166   SVGAnimationElement::ParseAttribute(params);
167 }
168 
ResolveTargetProperty()169 void SVGAnimateElement::ResolveTargetProperty() {
170   DCHECK(targetElement());
171   target_property_ = targetElement()->PropertyFromAttribute(AttributeName());
172   if (target_property_) {
173     type_ = target_property_->GetType();
174     css_property_id_ = target_property_->CssPropertyId();
175 
176     // Only <animateTransform> is allowed to animate AnimatedTransformList.
177     // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties
178     if (type_ == kAnimatedTransformList) {
179       type_ = kAnimatedUnknown;
180       css_property_id_ = CSSPropertyID::kInvalid;
181     }
182   } else {
183     type_ = SVGElement::AnimatedPropertyTypeForCSSAttribute(AttributeName());
184     css_property_id_ =
185         type_ != kAnimatedUnknown
186             ? cssPropertyID(targetElement()->GetDocument().ToExecutionContext(),
187                             AttributeName().LocalName())
188             : CSSPropertyID::kInvalid;
189   }
190   // Blacklist <script> targets here for now to prevent unpleasantries. This
191   // also disallows the perfectly "valid" animation of 'className' on said
192   // element. If SVGScriptElement.href is transitioned off of SVGAnimatedHref,
193   // this can be removed.
194   if (IsA<SVGScriptElement>(*targetElement())) {
195     type_ = kAnimatedUnknown;
196     css_property_id_ = CSSPropertyID::kInvalid;
197   }
198   DCHECK(type_ != kAnimatedPoint && type_ != kAnimatedStringList &&
199          type_ != kAnimatedTransform && type_ != kAnimatedTransformList);
200 }
201 
ClearTargetProperty()202 void SVGAnimateElement::ClearTargetProperty() {
203   target_property_ = nullptr;
204   type_ = kAnimatedUnknown;
205   css_property_id_ = CSSPropertyID::kInvalid;
206 }
207 
UpdateTargetProperty()208 void SVGAnimateElement::UpdateTargetProperty() {
209   if (SVGElement* target = targetElement())
210     ResolveTargetProperty();
211   else
212     ClearTargetProperty();
213 }
214 
GetAnimatedPropertyType() const215 AnimatedPropertyType SVGAnimateElement::GetAnimatedPropertyType() const {
216   // TODO(fs): Should be possible to DCHECK targetElement() here instead.
217   return !targetElement() ? kAnimatedUnknown : type_;
218 }
219 
HasValidAnimation() const220 bool SVGAnimateElement::HasValidAnimation() const {
221   if (AttributeName() == AnyQName())
222     return false;
223   if (type_ == kAnimatedUnknown)
224     return false;
225   // Always animate CSS properties using the ApplyCSSAnimation code path,
226   // regardless of the attributeType value.
227   // If attributeType="CSS" and attributeName doesn't point to a CSS property,
228   // ignore the animation.
229   return IsTargetAttributeCSSProperty(*targetElement(), AttributeName()) ||
230          GetAttributeType() != kAttributeTypeCSS;
231 }
232 
CreatePropertyForAttributeAnimation(const String & value) const233 SVGPropertyBase* SVGAnimateElement::CreatePropertyForAttributeAnimation(
234     const String& value) const {
235   // SVG DOM animVal animation code-path.
236   // TransformList must be animated via <animateTransform>, and its
237   // {from,by,to} attribute values needs to be parsed w.r.t. its "type"
238   // attribute. Spec:
239   // http://www.w3.org/TR/SVG/single-page.html#animate-AnimateTransformElement
240   DCHECK_NE(type_, kAnimatedTransformList);
241   DCHECK(target_property_);
242   return target_property_->CurrentValueBase()->CloneForAnimation(value);
243 }
244 
CreatePropertyForCSSAnimation(const String & value) const245 SVGPropertyBase* SVGAnimateElement::CreatePropertyForCSSAnimation(
246     const String& value) const {
247   // CSS properties animation code-path.
248   // Create a basic instance of the corresponding SVG property.
249   // The instance will not have full context info. (e.g. SVGLengthMode)
250   switch (type_) {
251     case kAnimatedColor:
252       return MakeGarbageCollected<SVGColorProperty>(value);
253     case kAnimatedNumber: {
254       auto* property = MakeGarbageCollected<SVGNumber>();
255       property->SetValueAsString(value);
256       return property;
257     }
258     case kAnimatedLength: {
259       auto* property = MakeGarbageCollected<SVGLength>();
260       property->SetValueAsString(value);
261       return property;
262     }
263     case kAnimatedLengthList: {
264       auto* property = MakeGarbageCollected<SVGLengthList>();
265       property->SetValueAsString(value);
266       return property;
267     }
268     case kAnimatedString: {
269       auto* property = MakeGarbageCollected<SVGString>();
270       property->SetValueAsString(value);
271       return property;
272     }
273     // These types don't appear in the table in
274     // SVGElement::animatedPropertyTypeForCSSAttribute() and thus don't need
275     // support.
276     case kAnimatedAngle:
277     case kAnimatedBoolean:
278     case kAnimatedEnumeration:
279     case kAnimatedInteger:
280     case kAnimatedIntegerOptionalInteger:
281     case kAnimatedNumberList:
282     case kAnimatedNumberOptionalNumber:
283     case kAnimatedPath:
284     case kAnimatedPoint:
285     case kAnimatedPoints:
286     case kAnimatedPreserveAspectRatio:
287     case kAnimatedRect:
288     case kAnimatedStringList:
289     case kAnimatedTransform:
290     case kAnimatedTransformList:
291     case kAnimatedUnknown:
292       break;
293     default:
294       break;
295   }
296   NOTREACHED();
297   return nullptr;
298 }
299 
CreatePropertyForAnimation(const String & value) const300 SVGPropertyBase* SVGAnimateElement::CreatePropertyForAnimation(
301     const String& value) const {
302   if (IsAnimatingSVGDom())
303     return CreatePropertyForAttributeAnimation(value);
304   DCHECK(IsAnimatingCSSProperty());
305   return CreatePropertyForCSSAnimation(value);
306 }
307 
AdjustForInheritance(SVGPropertyBase * property_value,AnimatedPropertyValueType value_type) const308 SVGPropertyBase* SVGAnimateElement::AdjustForInheritance(
309     SVGPropertyBase* property_value,
310     AnimatedPropertyValueType value_type) const {
311   if (value_type != kInheritValue)
312     return property_value;
313   // TODO(fs): At the moment the computed style gets returned as a String and
314   // needs to get parsed again. In the future we might want to work with the
315   // value type directly to avoid the String parsing.
316   DCHECK(targetElement());
317   Element* parent = targetElement()->parentElement();
318   auto* svg_parent = DynamicTo<SVGElement>(parent);
319   if (!svg_parent)
320     return property_value;
321   // Replace 'inherit' by its computed property value.
322   String value = ComputeCSSPropertyValue(svg_parent, css_property_id_);
323   return CreatePropertyForAnimation(value);
324 }
325 
DiscreteSelectValue(AnimationMode animation_mode,float percentage,SVGPropertyBase * from,SVGPropertyBase * to)326 static SVGPropertyBase* DiscreteSelectValue(AnimationMode animation_mode,
327                                             float percentage,
328                                             SVGPropertyBase* from,
329                                             SVGPropertyBase* to) {
330   if ((animation_mode == kFromToAnimation && percentage > 0.5) ||
331       animation_mode == kToAnimation || percentage == 1) {
332     return to;
333   }
334   return from;
335 }
336 
CalculateAnimatedValue(float percentage,unsigned repeat_count,SVGSMILElement * result_element) const337 void SVGAnimateElement::CalculateAnimatedValue(
338     float percentage,
339     unsigned repeat_count,
340     SVGSMILElement* result_element) const {
341   DCHECK(result_element);
342   DCHECK(targetElement());
343   if (!IsSVGAnimateElement(*result_element))
344     return;
345 
346   DCHECK(percentage >= 0 && percentage <= 1);
347   DCHECK_NE(GetAnimatedPropertyType(), kAnimatedUnknown);
348   DCHECK(from_property_);
349   DCHECK_EQ(from_property_->GetType(), GetAnimatedPropertyType());
350   DCHECK(to_property_);
351 
352   auto* result_animation_element = To<SVGAnimateElement>(result_element);
353   DCHECK(result_animation_element->animated_value_);
354   DCHECK_EQ(result_animation_element->GetAnimatedPropertyType(),
355             GetAnimatedPropertyType());
356 
357   if (IsA<SVGSetElement>(*this))
358     percentage = 1;
359 
360   if (GetCalcMode() == kCalcModeDiscrete)
361     percentage = percentage < 0.5 ? 0 : 1;
362 
363   // Target element might have changed.
364   SVGElement* target_element = targetElement();
365 
366   // Values-animation accumulates using the last values entry corresponding to
367   // the end of duration time.
368   SVGPropertyBase* animated_value = result_animation_element->animated_value_;
369   SVGPropertyBase* to_at_end_of_duration_value =
370       to_at_end_of_duration_property_ ? to_at_end_of_duration_property_
371                                       : to_property_;
372   SVGPropertyBase* from_value = GetAnimationMode() == kToAnimation
373                                     ? animated_value
374                                     : from_property_.Get();
375   SVGPropertyBase* to_value = to_property_;
376 
377   // Apply CSS inheritance rules.
378   from_value = AdjustForInheritance(from_value, from_property_value_type_);
379   to_value = AdjustForInheritance(to_value, to_property_value_type_);
380 
381   // If the animated type can only be animated discretely, then do that here,
382   // replacing |result_element|s animated value.
383   if (!AnimatedPropertyTypeSupportsAddition()) {
384     result_animation_element->animated_value_ = DiscreteSelectValue(
385         GetAnimationMode(), percentage, from_value, to_value);
386     return;
387   }
388 
389   animated_value->CalculateAnimatedValue(
390       *this, percentage, repeat_count, from_value, to_value,
391       to_at_end_of_duration_value, target_element);
392 }
393 
CalculateToAtEndOfDurationValue(const String & to_at_end_of_duration_string)394 bool SVGAnimateElement::CalculateToAtEndOfDurationValue(
395     const String& to_at_end_of_duration_string) {
396   if (to_at_end_of_duration_string.IsEmpty())
397     return false;
398   to_at_end_of_duration_property_ =
399       CreatePropertyForAnimation(to_at_end_of_duration_string);
400   return true;
401 }
402 
CalculateFromAndToValues(const String & from_string,const String & to_string)403 bool SVGAnimateElement::CalculateFromAndToValues(const String& from_string,
404                                                  const String& to_string) {
405   DCHECK(targetElement());
406   from_property_ = CreatePropertyForAnimation(from_string);
407   from_property_value_type_ = PropertyValueType(AttributeName(), from_string);
408   to_property_ = CreatePropertyForAnimation(to_string);
409   to_property_value_type_ = PropertyValueType(AttributeName(), to_string);
410   return true;
411 }
412 
CalculateFromAndByValues(const String & from_string,const String & by_string)413 bool SVGAnimateElement::CalculateFromAndByValues(const String& from_string,
414                                                  const String& by_string) {
415   DCHECK(targetElement());
416 
417   if (GetAnimationMode() == kByAnimation && !IsAdditive())
418     return false;
419 
420   // from-by animation may only be used with attributes that support addition
421   // (e.g. most numeric attributes).
422   if (GetAnimationMode() == kFromByAnimation &&
423       !AnimatedPropertyTypeSupportsAddition())
424     return false;
425 
426   DCHECK(!IsA<SVGSetElement>(*this));
427 
428   from_property_ = CreatePropertyForAnimation(from_string);
429   from_property_value_type_ = PropertyValueType(AttributeName(), from_string);
430   to_property_ = CreatePropertyForAnimation(by_string);
431   to_property_value_type_ = PropertyValueType(AttributeName(), by_string);
432   to_property_->Add(from_property_, targetElement());
433   return true;
434 }
435 
ResetAnimatedType()436 void SVGAnimateElement::ResetAnimatedType() {
437   DCHECK(targetElement());
438   if (IsAnimatingSVGDom()) {
439     // SVG DOM animVal animation code-path.
440     animated_value_ = target_property_->CreateAnimatedValue();
441     DCHECK_EQ(animated_value_->GetType(), type_);
442     return;
443   }
444   DCHECK(IsAnimatingCSSProperty());
445   // Presentation attributes which has an SVG DOM representation should use the
446   // "SVG DOM" code-path (above.)
447   DCHECK(SVGElement::IsAnimatableCSSProperty(AttributeName()));
448 
449   // CSS properties animation code-path.
450   String base_value =
451       ComputeCSSPropertyValue(targetElement(), css_property_id_);
452   animated_value_ = CreatePropertyForAnimation(base_value);
453 }
454 
ClearAnimatedType()455 void SVGAnimateElement::ClearAnimatedType() {
456   SVGElement* target_element = targetElement();
457   DCHECK(target_element);
458 
459   // CSS properties animation code-path.
460   if (IsAnimatingCSSProperty()) {
461     MutableCSSPropertyValueSet* property_set =
462         target_element->EnsureAnimatedSMILStyleProperties();
463     if (property_set->RemoveProperty(css_property_id_)) {
464       target_element->SetNeedsStyleRecalc(
465           kLocalStyleChange,
466           StyleChangeReasonForTracing::Create(style_change_reason::kAnimation));
467     }
468   }
469   // SVG DOM animVal animation code-path.
470   if (IsAnimatingSVGDom())
471     target_element->ClearAnimatedAttribute(AttributeName());
472 
473   animated_value_.Clear();
474 }
475 
ApplyResultsToTarget()476 void SVGAnimateElement::ApplyResultsToTarget() {
477   DCHECK_NE(GetAnimatedPropertyType(), kAnimatedUnknown);
478   DCHECK(animated_value_);
479   DCHECK(targetElement());
480 
481   // We do update the style and the animation property independent of each
482   // other.
483   SVGElement* target_element = targetElement();
484 
485   // CSS properties animation code-path.
486   if (IsAnimatingCSSProperty()) {
487     // Convert the result of the animation to a String and apply it as CSS
488     // property on the target_element.
489     MutableCSSPropertyValueSet* properties =
490         target_element->EnsureAnimatedSMILStyleProperties();
491     auto animated_value_string = animated_value_->ValueAsString();
492     auto& document = target_element->GetDocument();
493     auto set_result = properties->SetProperty(
494         css_property_id_, animated_value_string, false,
495         document.GetSecureContextMode(), document.ElementSheet().Contents());
496     if (set_result.did_change) {
497       target_element->SetNeedsStyleRecalc(
498           kLocalStyleChange,
499           StyleChangeReasonForTracing::Create(style_change_reason::kAnimation));
500     }
501   }
502   // SVG DOM animVal animation code-path.
503   if (IsAnimatingSVGDom())
504     target_element->SetAnimatedAttribute(AttributeName(), animated_value_);
505 }
506 
AnimatedPropertyTypeSupportsAddition() const507 bool SVGAnimateElement::AnimatedPropertyTypeSupportsAddition() const {
508   // http://www.w3.org/TR/SVG/animate.html#AnimationAttributesAndProperties.
509   switch (GetAnimatedPropertyType()) {
510     case kAnimatedBoolean:
511     case kAnimatedEnumeration:
512     case kAnimatedPreserveAspectRatio:
513     case kAnimatedString:
514     case kAnimatedUnknown:
515       return false;
516     default:
517       return true;
518   }
519 }
520 
IsAdditive() const521 bool SVGAnimateElement::IsAdditive() const {
522   if (GetAnimationMode() == kByAnimation ||
523       GetAnimationMode() == kFromByAnimation) {
524     if (!AnimatedPropertyTypeSupportsAddition())
525       return false;
526   }
527 
528   return SVGAnimationElement::IsAdditive();
529 }
530 
CalculateDistance(const String & from_string,const String & to_string)531 float SVGAnimateElement::CalculateDistance(const String& from_string,
532                                            const String& to_string) {
533   DCHECK(targetElement());
534   // FIXME: A return value of float is not enough to support paced animations on
535   // lists.
536   SVGPropertyBase* from_value = CreatePropertyForAnimation(from_string);
537   SVGPropertyBase* to_value = CreatePropertyForAnimation(to_string);
538   return from_value->CalculateDistance(to_value, targetElement());
539 }
540 
WillChangeAnimatedType()541 void SVGAnimateElement::WillChangeAnimatedType() {
542   UnregisterAnimation(attribute_name_);
543   // Should've been cleared by the above if needed.
544   DCHECK(!animated_value_);
545   from_property_.Clear();
546   to_property_.Clear();
547   to_at_end_of_duration_property_.Clear();
548 }
549 
DidChangeAnimatedType()550 void SVGAnimateElement::DidChangeAnimatedType() {
551   UpdateTargetProperty();
552   RegisterAnimation(attribute_name_);
553 }
554 
WillChangeAnimationTarget()555 void SVGAnimateElement::WillChangeAnimationTarget() {
556   SVGAnimationElement::WillChangeAnimationTarget();
557   WillChangeAnimatedType();
558 }
559 
DidChangeAnimationTarget()560 void SVGAnimateElement::DidChangeAnimationTarget() {
561   DidChangeAnimatedType();
562   SVGAnimationElement::DidChangeAnimationTarget();
563 }
564 
SetAttributeName(const QualifiedName & attribute_name)565 void SVGAnimateElement::SetAttributeName(const QualifiedName& attribute_name) {
566   if (attribute_name == attribute_name_)
567     return;
568   WillChangeAnimatedType();
569   attribute_name_ = attribute_name;
570   DidChangeAnimatedType();
571   AnimationAttributeChanged();
572 }
573 
SetAttributeType(const AtomicString & attribute_type_string)574 void SVGAnimateElement::SetAttributeType(
575     const AtomicString& attribute_type_string) {
576   AttributeType attribute_type = kAttributeTypeAuto;
577   if (attribute_type_string == "CSS")
578     attribute_type = kAttributeTypeCSS;
579   else if (attribute_type_string == "XML")
580     attribute_type = kAttributeTypeXML;
581   if (attribute_type == attribute_type_)
582     return;
583   WillChangeAnimatedType();
584   attribute_type_ = attribute_type;
585   DidChangeAnimatedType();
586   AnimationAttributeChanged();
587 }
588 
Trace(Visitor * visitor)589 void SVGAnimateElement::Trace(Visitor* visitor) {
590   visitor->Trace(from_property_);
591   visitor->Trace(to_property_);
592   visitor->Trace(to_at_end_of_duration_property_);
593   visitor->Trace(animated_value_);
594   visitor->Trace(target_property_);
595   SVGAnimationElement::Trace(visitor);
596 }
597 
598 }  // namespace blink
599