| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/animation/animatable.h" |
| |
| #include "third_party/blink/renderer/bindings/core/v8/unrestricted_double_or_keyframe_animation_options.h" |
| #include "third_party/blink/renderer/bindings/core/v8/unrestricted_double_or_keyframe_effect_options.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_get_animations_options.h" |
| #include "third_party/blink/renderer/core/animation/animation.h" |
| #include "third_party/blink/renderer/core/animation/document_animations.h" |
| #include "third_party/blink/renderer/core/animation/document_timeline.h" |
| #include "third_party/blink/renderer/core/animation/effect_input.h" |
| #include "third_party/blink/renderer/core/animation/effect_model.h" |
| #include "third_party/blink/renderer/core/animation/keyframe_effect.h" |
| #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h" |
| #include "third_party/blink/renderer/core/animation/timing.h" |
| #include "third_party/blink/renderer/core/animation/timing_input.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/element.h" |
| #include "third_party/blink/renderer/core/feature_policy/layout_animations_policy.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/bindings/script_state.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| |
| namespace blink { |
| namespace { |
| |
| // A helper method which is used to trigger a violation report for cases where |
| // the |element.animate| API is used to animate a CSS property which is blocked |
| // by the feature policy 'layout-animations'. |
| void ReportFeaturePolicyViolationsIfNecessary( |
| const ExecutionContext& context, |
| const KeyframeEffectModelBase& effect) { |
| for (const auto& property_handle : effect.Properties()) { |
| if (!property_handle.IsCSSProperty()) |
| continue; |
| const auto& css_property = property_handle.GetCSSProperty(); |
| if (LayoutAnimationsPolicy::AffectedCSSProperties().Contains( |
| &css_property)) { |
| LayoutAnimationsPolicy::ReportViolation(css_property, context); |
| } |
| } |
| } |
| |
| UnrestrictedDoubleOrKeyframeEffectOptions CoerceEffectOptions( |
| UnrestrictedDoubleOrKeyframeAnimationOptions options) { |
| if (options.IsKeyframeAnimationOptions()) { |
| return UnrestrictedDoubleOrKeyframeEffectOptions::FromKeyframeEffectOptions( |
| options.GetAsKeyframeAnimationOptions()); |
| } else { |
| return UnrestrictedDoubleOrKeyframeEffectOptions::FromUnrestrictedDouble( |
| options.GetAsUnrestrictedDouble()); |
| } |
| } |
| |
| } // namespace |
| |
| // https://drafts.csswg.org/web-animations/#dom-animatable-animate |
| Animation* Animatable::animate( |
| ScriptState* script_state, |
| const ScriptValue& keyframes, |
| const UnrestrictedDoubleOrKeyframeAnimationOptions& options, |
| ExceptionState& exception_state) { |
| if (!script_state->ContextIsValid()) |
| return nullptr; |
| Element* element = GetAnimationTarget(); |
| if (!element->GetExecutionContext()) |
| return nullptr; |
| KeyframeEffect* effect = |
| KeyframeEffect::Create(script_state, element, keyframes, |
| CoerceEffectOptions(options), exception_state); |
| if (exception_state.HadException()) |
| return nullptr; |
| |
| // Creation of the keyframe effect parses JavaScript, which could result |
| // in destruction of the execution context. Recheck that it is still valid. |
| if (!element->GetExecutionContext()) |
| return nullptr; |
| |
| ReportFeaturePolicyViolationsIfNecessary(*element->GetExecutionContext(), |
| *effect->Model()); |
| if (!options.IsKeyframeAnimationOptions()) |
| return element->GetDocument().Timeline().Play(effect); |
| |
| Animation* animation; |
| const KeyframeAnimationOptions* options_dict = |
| options.GetAsKeyframeAnimationOptions(); |
| if (!options_dict->hasTimeline()) { |
| animation = element->GetDocument().Timeline().Play(effect); |
| } else if (AnimationTimeline* timeline = options_dict->timeline()) { |
| animation = timeline->Play(effect); |
| } else { |
| animation = Animation::Create(element->GetExecutionContext(), effect, |
| nullptr, exception_state); |
| } |
| |
| if (!animation) |
| return nullptr; |
| |
| animation->setId(options_dict->id()); |
| return animation; |
| } |
| |
| Animation* Animatable::animate(ScriptState* script_state, |
| const ScriptValue& keyframes, |
| ExceptionState& exception_state) { |
| if (!script_state->ContextIsValid()) |
| return nullptr; |
| Element* element = GetAnimationTarget(); |
| if (!element->GetExecutionContext()) |
| return nullptr; |
| KeyframeEffect* effect = |
| KeyframeEffect::Create(script_state, element, keyframes, exception_state); |
| if (exception_state.HadException()) |
| return nullptr; |
| |
| // Creation of the keyframe effect parses JavaScript, which could result |
| // in destruction of the execution context. Recheck that it is still valid. |
| if (!element->GetExecutionContext()) |
| return nullptr; |
| |
| ReportFeaturePolicyViolationsIfNecessary(*element->GetExecutionContext(), |
| *effect->Model()); |
| return element->GetDocument().Timeline().Play(effect); |
| } |
| |
| HeapVector<Member<Animation>> Animatable::getAnimations( |
| GetAnimationsOptions* options) { |
| bool use_subtree = options && options->subtree(); |
| Element* element = GetAnimationTarget(); |
| if (use_subtree) |
| element->GetDocument().UpdateStyleAndLayoutTreeForSubtree(element); |
| else |
| element->GetDocument().UpdateStyleAndLayoutTreeForNode(element); |
| |
| HeapVector<Member<Animation>> animations; |
| if (!use_subtree && !element->HasAnimations()) |
| return animations; |
| |
| for (const auto& animation : |
| element->GetDocument().GetDocumentAnimations().getAnimations( |
| element->GetTreeScope())) { |
| DCHECK(animation->effect()); |
| // TODO(gtsteel) make this use the idl properties |
| Element* target = To<KeyframeEffect>(animation->effect())->EffectTarget(); |
| if (element == target || (use_subtree && element->contains(target))) { |
| // DocumentAnimations::getAnimations should only give us animations that |
| // are either current or in effect. |
| DCHECK(animation->effect()->IsCurrent() || |
| animation->effect()->IsInEffect()); |
| animations.push_back(animation); |
| } |
| } |
| return animations; |
| } |
| |
| } // namespace blink |