blob: 1058b458bfaa243b2c052485090c5a8f3a68c004 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_ANIMATION_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_ANIMATION_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_property.h"
#include "third_party/blink/renderer/core/animation/animation_effect.h"
#include "third_party/blink/renderer/core/animation/animation_effect_owner.h"
#include "third_party/blink/renderer/core/animation/compositor_animations.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/events/event_target.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_client.h"
#include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
class CompositorAnimation;
class Element;
class ExceptionState;
class PaintArtifactCompositor;
class TreeScope;
class AnimationTimeline;
class CORE_EXPORT Animation : public EventTargetWithInlineData,
public ActiveScriptWrappable<Animation>,
public ExecutionContextLifecycleObserver,
public CompositorAnimationDelegate,
public CompositorAnimationClient,
public AnimationEffectOwner {
DEFINE_WRAPPERTYPEINFO();
USING_PRE_FINALIZER(Animation, Dispose);
public:
enum AnimationPlayState {
kUnset,
kIdle,
kPending, // TODO(crbug.com/958433) remove non-spec compliant state.
kRunning,
kPaused,
kFinished
};
// https://drafts.csswg.org/web-animations/#animation-replace-state
enum ReplaceState { kActive, kRemoved, kPersisted };
// Priority for sorting getAnimation by Animation class, arranged from lowest
// priority to highest priority as per spec:
// https://drafts.csswg.org/web-animations/#dom-document-getanimations
enum AnimationClassPriority {
kCssTransitionPriority,
kCssAnimationPriority,
kDefaultPriority
};
// kTreeOrder uses the order in the DOM to determine animations' relative
// position.
// kPointerOrder simply compares Element pointers and determine animations'
// relative position.
enum CompareAnimationsOrdering { kTreeOrder, kPointerOrder };
static Animation* Create(AnimationEffect*,
AnimationTimeline*,
ExceptionState& = ASSERT_NO_EXCEPTION);
// Web Animations API IDL constructors.
static Animation* Create(ExecutionContext*,
AnimationEffect*,
ExceptionState&);
static Animation* Create(ExecutionContext*,
AnimationEffect*,
AnimationTimeline*,
ExceptionState&);
Animation(ExecutionContext*, AnimationTimeline*, AnimationEffect*);
~Animation() override;
void Dispose();
virtual bool IsCSSAnimation() const { return false; }
virtual bool IsCSSTransition() const { return false; }
virtual Element* OwningElement() const { return nullptr; }
virtual void ClearOwningElement() {}
bool IsOwned() const { return OwningElement(); }
// Returns whether the animation is finished.
bool Update(TimingUpdateReason);
// AnimationEffectOwner:
void UpdateIfNecessary() override;
void EffectInvalidated() override;
bool IsEventDispatchAllowed() const override;
Animation* GetAnimation() override { return this; }
// timeToEffectChange returns:
// nullopt - if this animation is no longer in effect
// AnimationTimeDelta() - if this animation requires an update on the
// next frame
// AnimationTimeDelta() > 0 - if this animation requires an update
// after 'n' units of time
base::Optional<AnimationTimeDelta> TimeToEffectChange();
void cancel();
base::Optional<double> currentTime() const;
void setCurrentTime(base::Optional<double> new_current_time,
ExceptionState& exception_state);
void setCurrentTime(base::Optional<double> new_current_time);
base::Optional<double> UnlimitedCurrentTime() const;
// https://drafts.csswg.org/web-animations/#play-states
String PlayStateString() const;
static const char* PlayStateString(AnimationPlayState);
AnimationPlayState CalculateAnimationPlayState() const;
// As a web exposed API, playState must update style and layout if the play
// state may be affected by it (see CSSAnimation::playState), whereas
// PlayStateString can be used to query the current play state.
virtual String playState() const;
bool PendingInternal() const;
// As a web exposed API, pending must update style and layout if the pending
// status may be affected by it (see CSSAnimation::pending), whereas
// PendingInternal can be used to query the current pending status.
virtual bool pending() const;
virtual void pause(ExceptionState& = ASSERT_NO_EXCEPTION);
virtual void play(ExceptionState& = ASSERT_NO_EXCEPTION);
virtual void reverse(ExceptionState& = ASSERT_NO_EXCEPTION);
void finish(ExceptionState& = ASSERT_NO_EXCEPTION);
void updatePlaybackRate(double playback_rate,
ExceptionState& = ASSERT_NO_EXCEPTION);
ScriptPromise finished(ScriptState*);
ScriptPromise ready(ScriptState*);
bool Paused() const {
return CalculateAnimationPlayState() == kPaused && !is_paused_for_testing_;
}
bool Playing() const override {
return CalculateAnimationPlayState() == kRunning && !Limited() &&
!is_paused_for_testing_;
}
bool Limited() const { return Limited(CurrentTimeInternal()); }
bool FinishedInternal() const { return finished_; }
DEFINE_ATTRIBUTE_EVENT_LISTENER(finish, kFinish)
DEFINE_ATTRIBUTE_EVENT_LISTENER(cancel, kCancel)
DEFINE_ATTRIBUTE_EVENT_LISTENER(remove, kRemove)
const AtomicString& InterfaceName() const override;
ExecutionContext* GetExecutionContext() const override;
bool HasPendingActivity() const final;
void ContextDestroyed() override;
double playbackRate() const;
void setPlaybackRate(double, ExceptionState& = ASSERT_NO_EXCEPTION);
AnimationTimeline* timeline() { return timeline_; }
Document* GetDocument() const;
base::Optional<double> startTime() const;
base::Optional<double> StartTimeInternal() const { return start_time_; }
virtual void setStartTime(base::Optional<double>, ExceptionState&);
void setStartTime(base::Optional<double>);
const AnimationEffect* effect() const { return content_.Get(); }
AnimationEffect* effect() { return content_.Get(); }
void setEffect(AnimationEffect*);
void setId(const String& id) { id_ = id; }
const String& id() const { return id_; }
// Pausing via this method is not reflected in the value returned by
// paused() and must never overlap with pausing via pause().
void PauseForTesting(double pause_time);
void DisableCompositedAnimationForTesting();
// This should only be used for CSS
void Unpause();
void SetOutdated();
bool Outdated() { return outdated_; }
CompositorAnimations::FailureReasons CheckCanStartAnimationOnCompositor(
const PaintArtifactCompositor* paint_artifact_compositor,
PropertyHandleSet* unsupported_properties = nullptr) const;
void StartAnimationOnCompositor(
const PaintArtifactCompositor* paint_artifact_compositor);
void CancelAnimationOnCompositor();
void RestartAnimationOnCompositor();
void CancelIncompatibleAnimationsOnCompositor();
bool HasActiveAnimationsOnCompositor();
void SetCompositorPending(bool effect_changed = false);
void NotifyReady(double ready_time);
void CommitPendingPlay(double ready_time);
void CommitPendingPause(double ready_time);
// CompositorAnimationClient implementation.
CompositorAnimation* GetCompositorAnimation() const override {
return compositor_animation_ ? compositor_animation_->GetAnimation()
: nullptr;
}
bool Affects(const Element&, const CSSProperty&) const;
// Returns whether we should continue with the commit for this animation or
// wait until next commit.
bool PreCommit(int compositor_group,
const PaintArtifactCompositor*,
bool start_on_compositor);
void PostCommit();
unsigned SequenceNumber() const override { return sequence_number_; }
int CompositorGroup() const { return compositor_group_; }
static bool HasLowerCompositeOrdering(
const Animation* animation1,
const Animation* animation2,
CompareAnimationsOrdering compare_animation_type);
bool EffectSuppressed() const override { return effect_suppressed_; }
void SetEffectSuppressed(bool);
void InvalidateKeyframeEffect(const TreeScope&);
void Trace(Visitor*) const override;
bool CompositorPendingForTesting() const { return compositor_pending_; }
// Methods for handling removal and persistence of animations.
bool IsReplaceable();
void RemoveReplacedAnimation();
void persist();
String replaceState();
void commitStyles(ExceptionState& = ASSERT_NO_EXCEPTION);
bool ReplaceStateRemoved() const override {
return replace_state_ == kRemoved;
}
bool ReplaceStateActive() const { return replace_state_ == kActive; }
// Overridden for CSS animations to force pending animation properties to be
// applied. This step is required before any web animation API calls that
// depends on computed values.
virtual void FlushPendingUpdates() const {}
protected:
DispatchEventResult DispatchEventInternal(Event&) override;
void AddedEventListener(const AtomicString& event_type,
RegisteredEventListener&) override;
base::Optional<double> CurrentTimeInternal() const;
TimelinePhase CurrentPhaseInternal() const;
virtual AnimationEffect::EventDelegate* CreateEventDelegate(
Element* target,
const AnimationEffect::EventDelegate* old_event_delegate) {
return nullptr;
}
private:
void SetCurrentTimeInternal(double new_current_time);
void SetHoldTimeAndPhase(
base::Optional<double> new_hold_time /* in seconds */,
TimelinePhase new_hold_phase);
void ResetHoldTimeAndPhase();
bool ValidateHoldTimeAndPhase() const;
void ClearOutdated();
void ForceServiceOnNextFrame();
double EffectEnd() const;
bool Limited(base::Optional<double> current_time) const;
// Playback rate that will take effect once any pending tasks are resolved.
// If there are no pending tasks, then the effective playback rate equals the
// active playback rate.
double EffectivePlaybackRate() const;
void ApplyPendingPlaybackRate();
base::Optional<double> CalculateStartTime(double current_time) const;
base::Optional<double> CalculateCurrentTime() const;
TimelinePhase CalculateCurrentPhase() const;
void BeginUpdatingState();
void EndUpdatingState();
CompositorAnimations::FailureReasons
CheckCanStartAnimationOnCompositorInternal() const;
void CreateCompositorAnimation();
void DestroyCompositorAnimation();
void AttachCompositorTimeline();
void DetachCompositorTimeline();
void AttachCompositedLayers();
void DetachCompositedLayers();
// CompositorAnimationDelegate implementation.
void NotifyAnimationStarted(double monotonic_time, int group) override;
void NotifyAnimationFinished(double monotonic_time, int group) override {}
void NotifyAnimationAborted(double monotonic_time, int group) override {}
using AnimationPromise = ScriptPromiseProperty<Member<Animation>,
Member<DOMException>>;
void ResolvePromiseMaybeAsync(AnimationPromise*);
void RejectAndResetPromise(AnimationPromise*);
void RejectAndResetPromiseMaybeAsync(AnimationPromise*);
// Updates the finished state of the animation. If the update is the result of
// a discontinuous time change then the value for current time is not bound by
// the limits of the animation. The finished notification may be synchronous
// or asynchronous. A synchronous notification is used in the case of
// explicitly calling finish on an animation.
enum class UpdateType { kContinuous, kDiscontinuous };
enum class NotificationType { kAsync, kSync };
void UpdateFinishedState(UpdateType update_context,
NotificationType notification_type);
void QueueFinishedEvent();
// Plays an animation. When auto_rewind is enabled, the current time can be
// adjusted to accommodate reversal of an animation or snapping to an
// endpoint.
enum class AutoRewind { kDisabled, kEnabled };
void PlayInternal(AutoRewind auto_rewind, ExceptionState& exception_state);
void ResetPendingTasks();
base::Optional<double> TimelineTime() const;
void ScheduleAsyncFinish();
void AsyncFinishMicrotask();
void CommitFinishNotification();
// Tracking the state of animations in dev tools.
void NotifyProbe();
String id_;
// Extended play state reported to dev tools. This play state has an
// additional pending state that is not part of the spec by expected by dev
// tools.
AnimationPlayState reported_play_state_;
double playback_rate_;
// The pending playback rate is not currently in effect. It typically takes
// effect when running a scheduled task in response to the animation being
// ready.
base::Optional<double> pending_playback_rate_;
base::Optional<double> start_time_;
base::Optional<double> hold_time_;
base::Optional<TimelinePhase> hold_phase_;
base::Optional<double> previous_current_time_;
unsigned sequence_number_;
Member<AnimationPromise> finished_promise_;
Member<AnimationPromise> ready_promise_;
Member<AnimationEffect> content_;
// Document refers to the timeline's document if there is a timeline.
// Otherwise it refers to the document for the execution context.
Member<Document> document_;
Member<AnimationTimeline> timeline_;
ReplaceState replace_state_;
// Testing flags.
bool is_paused_for_testing_;
bool is_composited_animation_disabled_for_testing_;
// Pending micro-tasks. These flags are used for tracking purposes only for
// the Animation.pending attribute, and do not otherwise affect internal flow
// control.
bool pending_pause_;
bool pending_play_;
// Indicates finish notification queued but not processed.
bool pending_finish_notification_;
bool has_queued_microtask_;
// This indicates timing information relevant to the animation's effect
// has changed by means other than the ordinary progression of time
bool outdated_;
// Indicates the animation is no longer active. Cancelled animation is marked
// as finished_.
bool finished_;
// Indicates finish notification has been handled.
bool committed_finish_notification_;
// Holds a 'finished' event queued for asynchronous dispatch via the
// ScriptedAnimationController. This object remains active until the
// event is actually dispatched.
Member<Event> pending_finished_event_;
Member<Event> pending_cancelled_event_;
Member<Event> pending_remove_event_;
// TODO(crbug.com/960944): Consider reintroducing kPause and cleanup use of
// mutually exclusive pending_play_ and pending_pause_ flags.
enum CompositorAction { kNone, kStart };
class CompositorState {
USING_FAST_MALLOC(CompositorState);
public:
explicit CompositorState(Animation& animation)
: start_time(animation.start_time_),
hold_time(animation.hold_time_),
playback_rate(animation.EffectivePlaybackRate()),
effect_changed(false),
pending_action(animation.start_time_ ? kNone : kStart) {}
base::Optional<double> start_time;
base::Optional<double> hold_time;
double playback_rate;
bool effect_changed;
CompositorAction pending_action;
DISALLOW_COPY_AND_ASSIGN(CompositorState);
};
enum CompositorPendingChange {
kSetCompositorPending,
kSetCompositorPendingWithEffectChanged,
kDoNotSetCompositorPending,
};
// CompositorAnimation objects need to eagerly sever their connection to their
// Animation delegate; use a separate 'holder' on-heap object to accomplish
// that.
class CompositorAnimationHolder final
: public GarbageCollected<CompositorAnimationHolder> {
USING_PRE_FINALIZER(CompositorAnimationHolder, Dispose);
public:
static CompositorAnimationHolder* Create(Animation*);
explicit CompositorAnimationHolder(Animation*);
void Detach();
void Trace(Visitor* visitor) const { visitor->Trace(animation_); }
CompositorAnimation* GetAnimation() const {
return compositor_animation_.get();
}
private:
void Dispose();
std::unique_ptr<CompositorAnimation> compositor_animation_;
Member<Animation> animation_;
};
// This mirrors the known compositor state. It is created when a compositor
// animation is started. Updated once the start time is known and each time
// modifications are pushed to the compositor.
std::unique_ptr<CompositorState> compositor_state_;
bool compositor_pending_;
int compositor_group_;
Member<CompositorAnimationHolder> compositor_animation_;
bool effect_suppressed_;
FRIEND_TEST_ALL_PREFIXES(AnimationAnimationTestCompositeAfterPaint,
NoCompositeWithoutCompositedElementId);
FRIEND_TEST_ALL_PREFIXES(AnimationAnimationTestNoCompositing,
PendingActivityWithFinishedEventListener);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_ANIMATION_H_