diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/animation')
88 files changed, 2016 insertions, 734 deletions
diff --git a/chromium/third_party/blink/renderer/core/animation/BUILD.gn b/chromium/third_party/blink/renderer/core/animation/BUILD.gn index a690dc32877..efc1cfc7c81 100644 --- a/chromium/third_party/blink/renderer/core/animation/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/animation/BUILD.gn @@ -72,6 +72,8 @@ blink_core_sources("animation") { "css/css_transition_data.h", "css_angle_interpolation_type.cc", "css_angle_interpolation_type.h", + "css_aspect_ratio_interpolation_type.cc", + "css_aspect_ratio_interpolation_type.h", "css_basic_shape_interpolation_type.cc", "css_basic_shape_interpolation_type.h", "css_border_image_length_box_interpolation_type.cc", @@ -168,6 +170,8 @@ blink_core_sources("animation") { "image_slice_property_functions.h", "inert_effect.cc", "inert_effect.h", + "interpolable_aspect_ratio.cc", + "interpolable_aspect_ratio.h", "interpolable_filter.cc", "interpolable_filter.h", "interpolable_length.cc", diff --git a/chromium/third_party/blink/renderer/core/animation/DIR_METADATA b/chromium/third_party/blink/renderer/core/animation/DIR_METADATA new file mode 100644 index 00000000000..072e8c98258 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/DIR_METADATA @@ -0,0 +1,5 @@ +monorail { + component: "Blink>Animation" +} + +team_email: "animations-dev@chromium.org" diff --git a/chromium/third_party/blink/renderer/core/animation/OWNERS b/chromium/third_party/blink/renderer/core/animation/OWNERS index ef3593179c5..e7571208635 100644 --- a/chromium/third_party/blink/renderer/core/animation/OWNERS +++ b/chromium/third_party/blink/renderer/core/animation/OWNERS @@ -8,6 +8,3 @@ alancutter@chromium.org ericwilligers@chromium.org per-file compositor_*=loyso@chromium.org - -# TEAM: animations-dev@chromium.org -# COMPONENT: Blink>Animation diff --git a/chromium/third_party/blink/renderer/core/animation/animatable.cc b/chromium/third_party/blink/renderer/core/animation/animatable.cc index 01fee49eac1..bb64f4e2334 100644 --- a/chromium/third_party/blink/renderer/core/animation/animatable.cc +++ b/chromium/third_party/blink/renderer/core/animation/animatable.cc @@ -73,6 +73,11 @@ Animation* Animatable::animate( 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()) @@ -90,6 +95,9 @@ Animation* Animatable::animate( nullptr, exception_state); } + if (!animation) + return nullptr; + animation->setId(options_dict->id()); return animation; } @@ -107,6 +115,11 @@ Animation* Animatable::animate(ScriptState* script_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); diff --git a/chromium/third_party/blink/renderer/core/animation/animation.cc b/chromium/third_party/blink/renderer/core/animation/animation.cc index 3cd250acb32..5bededa4316 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation.cc @@ -53,6 +53,8 @@ #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" #include "third_party/blink/renderer/core/css/style_change_reason.h" +#include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h" +#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/events/animation_playback_event.h" @@ -83,26 +85,6 @@ unsigned NextSequenceNumber() { return ++next; } -double SecondsToMilliseconds(double seconds) { - return seconds * 1000; -} - -double MillisecondsToSeconds(double milliseconds) { - return milliseconds / 1000; -} - -double Max(base::Optional<double> a, double b) { - if (a.has_value()) - return std::max(a.value(), b); - return b; -} - -double Min(base::Optional<double> a, double b) { - if (a.has_value()) - return std::min(a.value(), b); - return b; -} - PseudoPriority ConvertPseudoIdtoPriority(const PseudoId& pseudo) { if (pseudo == kPseudoIdNone) return PseudoPriority::kNone; @@ -180,6 +162,16 @@ AtomicString GetCSSTransitionCSSPropertyName(const Animation* animation) { ->TransitionCSSPropertyName() .ToAtomicString(); } + +bool GreaterThanOrEqualWithinTimeTolerance(const AnimationTimeDelta& a, + const AnimationTimeDelta& b) { + double a_ms = a.InMillisecondsF(); + double b_ms = b.InMillisecondsF(); + if (std::abs(a_ms - b_ms) < Animation::kTimeToleranceMs) + return true; + + return a_ms > b_ms; +} } // namespace Animation* Animation::Create(AnimationEffect* effect, @@ -294,13 +286,21 @@ void Animation::Dispose() { DCHECK(!compositor_animation_); } -double Animation::EffectEnd() const { - return content_ ? content_->SpecifiedTiming().EndTimeInternal() : 0; +AnimationTimeDelta Animation::EffectEnd() const { + return content_ ? AnimationTimeDelta::FromSecondsD( + content_->SpecifiedTiming().EndTimeInternal()) + : AnimationTimeDelta(); } -bool Animation::Limited(base::Optional<double> current_time) const { - return (EffectivePlaybackRate() < 0 && current_time <= 0) || - (EffectivePlaybackRate() > 0 && current_time >= EffectEnd()); +bool Animation::Limited(base::Optional<AnimationTimeDelta> current_time) const { + if (!current_time) + return false; + + return (EffectivePlaybackRate() < 0 && + current_time <= AnimationTimeDelta()) || + (EffectivePlaybackRate() > 0 && + GreaterThanOrEqualWithinTimeTolerance(current_time.value(), + EffectEnd())); } Document* Animation::GetDocument() const { @@ -312,9 +312,9 @@ base::Optional<double> Animation::TimelineTime() const { } // https://drafts.csswg.org/web-animations/#setting-the-current-time-of-an-animation. -void Animation::setCurrentTime(base::Optional<double> new_current_time, +void Animation::setCurrentTime(CSSNumberish current_time, ExceptionState& exception_state) { - if (!new_current_time) { + if (current_time.IsNull()) { // If the current time is resolved, then throw a TypeError. if (CurrentTimeInternal()) { exception_state.ThrowTypeError( @@ -323,12 +323,25 @@ void Animation::setCurrentTime(base::Optional<double> new_current_time, return; } - SetCurrentTimeInternal(MillisecondsToSeconds(new_current_time.value())); + if (current_time.IsCSSNumericValue()) { + // Throw exception for CSSNumberish that is a CSSNumericValue + exception_state.ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + "Invalid startTime. CSSNumericValue not yet supported."); + return; + } + + DCHECK(current_time.IsDouble()); + // Convert from double to AnimationTimeDelta for internal use. + base::Optional<AnimationTimeDelta> new_current_time = + AnimationTimeDelta::FromMillisecondsD(current_time.GetAsDouble()); + + DCHECK(new_current_time); + SetCurrentTimeInternal(new_current_time.value()); // Synchronously resolve pending pause task. if (pending_pause_) { - SetHoldTimeAndPhase(MillisecondsToSeconds(new_current_time.value()), - TimelinePhase::kActive); + SetHoldTimeAndPhase(new_current_time, TimelinePhase::kActive); ApplyPendingPlaybackRate(); start_time_ = base::nullopt; pending_pause_ = false; @@ -345,19 +358,17 @@ void Animation::setCurrentTime(base::Optional<double> new_current_time, NotifyProbe(); } -void Animation::setCurrentTime(base::Optional<double> new_current_time) { +void Animation::setCurrentTime(CSSNumberish current_time) { NonThrowableExceptionState exception_state; - setCurrentTime(new_current_time, exception_state); + setCurrentTime(current_time, exception_state); } // https://drafts.csswg.org/web-animations/#setting-the-current-time-of-an-animation // See steps for silently setting the current time. The preliminary step of // handling an unresolved time are to be handled by the caller. -void Animation::SetCurrentTimeInternal(double new_current_time) { - DCHECK(std::isfinite(new_current_time)); - - base::Optional<double> previous_start_time = start_time_; - base::Optional<double> previous_hold_time = hold_time_; +void Animation::SetCurrentTimeInternal(AnimationTimeDelta new_current_time) { + base::Optional<AnimationTimeDelta> previous_start_time = start_time_; + base::Optional<AnimationTimeDelta> previous_hold_time = hold_time_; base::Optional<TimelinePhase> previous_hold_phase = hold_phase_; // Update either the hold time or the start time. @@ -383,11 +394,8 @@ void Animation::SetCurrentTimeInternal(double new_current_time) { } void Animation::SetHoldTimeAndPhase( - base::Optional<double> new_hold_time /* in seconds */, + base::Optional<AnimationTimeDelta> new_hold_time, TimelinePhase new_hold_phase) { - // new_hold_time must be valid, unless new_hold_phase is inactive. - DCHECK(new_hold_time || - (!new_hold_time && new_hold_phase == TimelinePhase::kInactive)); hold_time_ = new_hold_time; hold_phase_ = new_hold_phase; } @@ -397,18 +405,22 @@ void Animation::ResetHoldTimeAndPhase() { hold_phase_ = base::nullopt; } -base::Optional<double> Animation::startTime() const { - return start_time_ - ? base::make_optional(SecondsToMilliseconds(start_time_.value())) - : base::nullopt; +void Animation::startTime(CSSNumberish& startTime) const { + startTime = + start_time_ + ? CSSNumberish::FromDouble(start_time_.value().InMillisecondsF()) + : CSSNumberish(); } // https://drafts.csswg.org/web-animations/#the-current-time-of-an-animation -base::Optional<double> Animation::currentTime() const { +void Animation::currentTime(CSSNumberish& currentTime) const { // 1. If the animation’s hold time is resolved, // The current time is the animation’s hold time. - if (hold_time_.has_value()) - return SecondsToMilliseconds(hold_time_.value()); + if (hold_time_.has_value()) { + currentTime = + CSSNumberish::FromDouble(hold_time_.value().InMillisecondsF()); + return; + } // 2. If any of the following are true: // * the animation has no associated timeline, or @@ -416,28 +428,30 @@ base::Optional<double> Animation::currentTime() const { // * the animation’s start time is unresolved. // The current time is an unresolved time value. if (!timeline_ || !timeline_->IsActive() || !start_time_) - return base::nullopt; + return; // 3. Otherwise, // current time = (timeline time - start time) × playback rate - base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds(); + base::Optional<AnimationTimeDelta> timeline_time = timeline_->CurrentTime(); // An active timeline should always have a value, and since inactive timeline // is handled in step 2 above, make sure that timeline_time has a value. DCHECK(timeline_time.has_value()); - double current_time = + AnimationTimeDelta calculated_current_time = (timeline_time.value() - start_time_.value()) * playback_rate_; - return SecondsToMilliseconds(current_time); + + currentTime = + CSSNumberish::FromDouble(calculated_current_time.InMillisecondsF()); } bool Animation::ValidateHoldTimeAndPhase() const { - return (hold_phase_ && hold_time_) || + return hold_phase_ || ((!hold_phase_ || hold_phase_ == TimelinePhase::kInactive) && !hold_time_); } -base::Optional<double> Animation::CurrentTimeInternal() const { +base::Optional<AnimationTimeDelta> Animation::CurrentTimeInternal() const { DCHECK(ValidateHoldTimeAndPhase()); return hold_time_ ? hold_time_ : CalculateCurrentTime(); } @@ -447,7 +461,7 @@ TimelinePhase Animation::CurrentPhaseInternal() const { return hold_phase_ ? hold_phase_.value() : CalculateCurrentPhase(); } -base::Optional<double> Animation::UnlimitedCurrentTime() const { +base::Optional<AnimationTimeDelta> Animation::UnlimitedCurrentTime() const { return CalculateAnimationPlayState() == kPaused || !start_time_ ? CurrentTimeInternal() : CalculateCurrentTime(); @@ -466,9 +480,11 @@ bool Animation::PreCommit( compositor_state_ && (Paused() || compositor_state_->playback_rate != EffectivePlaybackRate()); bool hard_change = - compositor_state_ && (compositor_state_->effect_changed || - compositor_state_->start_time != start_time_ || - !compositor_state_->start_time || !start_time_); + compositor_state_ && + (compositor_state_->effect_changed || !compositor_state_->start_time || + !start_time_ || + !IsWithinAnimationTimeEpsilon(compositor_state_->start_time.value(), + start_time_.value().InSecondsF())); // FIXME: softChange && !hardChange should generate a Pause/ThenStart, // not a Cancel, but we can't communicate these to the compositor yet. @@ -526,7 +542,8 @@ void Animation::PostCommit() { DCHECK_EQ(kStart, compositor_state_->pending_action); if (compositor_state_->start_time) { - DCHECK_EQ(start_time_.value(), compositor_state_->start_time.value()); + DCHECK(IsWithinAnimationTimeEpsilon(start_time_.value().InSecondsF(), + compositor_state_->start_time.value())); compositor_state_->pending_action = kNone; } } @@ -621,7 +638,7 @@ bool Animation::HasLowerCompositeOrdering( return animation1->SequenceNumber() < animation2->SequenceNumber(); } -void Animation::NotifyReady(double ready_time) { +void Animation::NotifyReady(AnimationTimeDelta ready_time) { // Complete the pending updates prior to updating the compositor state in // order to ensure a correct start time for the compositor state without the // need to duplicate the calculations. @@ -633,7 +650,9 @@ void Animation::NotifyReady(double ready_time) { if (compositor_state_ && compositor_state_->pending_action == kStart) { DCHECK(!compositor_state_->start_time); compositor_state_->pending_action = kNone; - compositor_state_->start_time = start_time_; + compositor_state_->start_time = + start_time_ ? base::make_optional(start_time_.value().InSecondsF()) + : base::nullopt; } // Notify of change to play state. @@ -643,8 +662,7 @@ void Animation::NotifyReady(double ready_time) { // Microtask for playing an animation. // Refer to Step 8.3 'pending play task' in // https://drafts.csswg.org/web-animations/#playing-an-animation-section. -void Animation::CommitPendingPlay(double ready_time) { - DCHECK(std::isfinite(ready_time)); +void Animation::CommitPendingPlay(AnimationTimeDelta ready_time) { DCHECK(start_time_ || hold_time_); DCHECK(pending_play_); pending_play_ = false; @@ -680,7 +698,7 @@ void Animation::CommitPendingPlay(double ready_time) { // If the playback rate is zero, let new start time be simply ready // time. // B.5 Set the start time of animation to new start time. - double current_time_to_match = + AnimationTimeDelta current_time_to_match = (ready_time - start_time_.value()) * playback_rate_; ApplyPendingPlaybackRate(); if (playback_rate_ == 0) { @@ -705,7 +723,7 @@ void Animation::CommitPendingPlay(double ready_time) { // Microtask for pausing an animation. // Refer to step 7 'pending pause task' in // https://drafts.csswg.org/web-animations-1/#pausing-an-animation-section -void Animation::CommitPendingPause(double ready_time) { +void Animation::CommitPendingPause(AnimationTimeDelta ready_time) { DCHECK(pending_pause_); pending_pause_ = false; @@ -769,7 +787,7 @@ void Animation::setTimeline(AnimationTimeline* timeline) { UpdateIfNecessary(); AnimationPlayState old_play_state = CalculateAnimationPlayState(); - base::Optional<double> old_current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> old_current_time = CurrentTimeInternal(); CancelAnimationOnCompositor(); @@ -795,7 +813,8 @@ void Animation::setTimeline(AnimationTimeline* timeline) { if (timeline) { if (!timeline->IsMonotonicallyIncreasing()) { ApplyPendingPlaybackRate(); - double boundary_time = (playback_rate_ > 0) ? 0 : EffectEnd(); + AnimationTimeDelta boundary_time = + (playback_rate_ > 0) ? AnimationTimeDelta() : EffectEnd(); switch (old_play_state) { case kIdle: break; @@ -851,11 +870,11 @@ void Animation::setTimeline(AnimationTimeline* timeline) { NotifyProbe(); } -base::Optional<double> Animation::CalculateStartTime( - double current_time) const { - base::Optional<double> start_time; +base::Optional<AnimationTimeDelta> Animation::CalculateStartTime( + AnimationTimeDelta current_time) const { + base::Optional<AnimationTimeDelta> start_time; if (timeline_) { - base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds(); + base::Optional<AnimationTimeDelta> timeline_time = timeline_->CurrentTime(); if (timeline_time) start_time = timeline_time.value() - current_time / playback_rate_; // TODO(crbug.com/916117): Handle NaN time for scroll-linked animations. @@ -864,16 +883,13 @@ base::Optional<double> Animation::CalculateStartTime( return start_time; } -base::Optional<double> Animation::CalculateCurrentTime() const { +base::Optional<AnimationTimeDelta> Animation::CalculateCurrentTime() const { if (!start_time_ || !timeline_ || !timeline_->IsActive()) return base::nullopt; - base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds(); - if (!timeline_time) { - // timeline_time can be null only when the timeline is inactive - DCHECK(!timeline_->IsActive()); - return base::nullopt; - } + base::Optional<AnimationTimeDelta> timeline_time = timeline_->CurrentTime(); + // timeline_ must be active here, make sure it is returning a current_time. + DCHECK(timeline_time); return (timeline_time.value() - start_time_.value()) * playback_rate_; } @@ -885,42 +901,56 @@ TimelinePhase Animation::CalculateCurrentPhase() const { } // https://drafts.csswg.org/web-animations/#setting-the-start-time-of-an-animation -void Animation::setStartTime(base::Optional<double> start_time_ms, +void Animation::setStartTime(CSSNumberish start_time, ExceptionState& exception_state) { + if (!start_time.IsNull() && start_time.IsCSSNumericValue()) { + // Throw exception for CSSNumberish that is a CSSNumericValue + exception_state.ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + "Invalid startTime. CSSNumericValue not yet supported."); + return; + } + + base::Optional<AnimationTimeDelta> new_start_time = + !start_time.IsNull() + ? base::make_optional( + AnimationTimeDelta::FromMillisecondsD(start_time.GetAsDouble())) + : base::nullopt; + bool had_start_time = start_time_.has_value(); // 1. Let timeline time be the current time value of the timeline that // animation is associated with. If there is no timeline associated with // animation or the associated timeline is inactive, let the timeline time // be unresolved. - base::Optional<double> timeline_time = timeline_ && timeline_->IsActive() - ? timeline_->CurrentTimeSeconds() - : base::nullopt; + base::Optional<AnimationTimeDelta> timeline_time = + timeline_ && timeline_->IsActive() ? timeline_->CurrentTime() + : base::nullopt; // 2. If timeline time is unresolved and new start time is resolved, make // animation’s hold time unresolved. // This preserves the invariant that when we don’t have an active timeline it // is only possible to set either the start time or the animation’s current // time. - if (!timeline_time && start_time_ms) { + if (!timeline_time && new_start_time) { ResetHoldTimeAndPhase(); } // 3. Let previous current time be animation’s current time. - base::Optional<double> previous_current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> previous_current_time = + CurrentTimeInternal(); TimelinePhase previous_current_phase = CurrentPhaseInternal(); // 4. Apply any pending playback rate on animation. ApplyPendingPlaybackRate(); // 5. Set animation’s start time to new start time. - base::Optional<double> new_start_time; - if (start_time_ms) { - new_start_time = MillisecondsToSeconds(start_time_ms.value()); + if (new_start_time) { // Snap to timeline time if within floating point tolerance to ensure // deterministic behavior in phase transitions. - if (timeline_time && IsWithinAnimationTimeEpsilon(timeline_time.value(), - new_start_time.value())) { + if (timeline_time && + IsWithinAnimationTimeEpsilon(timeline_time.value().InSecondsF(), + new_start_time.value().InSecondsF())) { new_start_time = timeline_time.value(); } } @@ -959,7 +989,7 @@ void Animation::setStartTime(base::Optional<double> start_time_ms, UpdateFinishedState(UpdateType::kDiscontinuous, NotificationType::kAsync); // Update user agent. - base::Optional<double> new_current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> new_current_time = CurrentTimeInternal(); // Even when the animation is not outdated,call SetOutdated to ensure // the animation is tracked by its timeline for future timing // updates. @@ -972,9 +1002,9 @@ void Animation::setStartTime(base::Optional<double> start_time_ms, NotifyProbe(); } -void Animation::setStartTime(base::Optional<double> start_time_ms) { +void Animation::setStartTime(CSSNumberish start_time) { NonThrowableExceptionState exception_state; - setStartTime(start_time_ms, exception_state); + setStartTime(start_time, exception_state); } // https://drafts.csswg.org/web-animations-1/#setting-the-associated-effect @@ -1049,11 +1079,11 @@ void Animation::setEffect(AnimationEffect* new_effect) { // The timing phase is ‘before’. // Otherwise, // The timing phase is ‘after’. - base::Optional<double> current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> current_time = CurrentTimeInternal(); Timing::Phase phase; if (!current_time) phase = Timing::kPhaseNone; - else if (current_time < 0) + else if (current_time < AnimationTimeDelta()) phase = Timing::kPhaseBefore; else phase = Timing::kPhaseAfter; @@ -1177,7 +1207,7 @@ void Animation::pause(ExceptionState& exception_state) { return; // 3. Let seek time be a time value that is initially unresolved. - base::Optional<double> seek_time; + base::Optional<AnimationTimeDelta> seek_time; // 4. Let has finite timeline be true if animation has an associated timeline // that is not monotonically increasing. @@ -1194,12 +1224,11 @@ void Animation::pause(ExceptionState& exception_state) { // steps. // Otherwise, // Set seek time to animation's associated effect end. - base::Optional<double> current_time = CurrentTimeInternal(); - if (!current_time) { + if (!CurrentTimeInternal()) { if (playback_rate_ >= 0) { - seek_time = 0; + seek_time = AnimationTimeDelta(); } else { - if (EffectEnd() == std::numeric_limits<double>::infinity()) { + if (EffectEnd().is_inf()) { exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, "Cannot play reversed Animation with infinite target effect end."); @@ -1283,7 +1312,7 @@ void Animation::PlayInternal(AutoRewind auto_rewind, bool enable_seek = auto_rewind == AutoRewind::kEnabled || reset_current_time_on_resume_; bool has_pending_ready_promise = false; - base::Optional<double> seek_time; + base::Optional<AnimationTimeDelta> seek_time; bool has_finite_timeline = timeline_ && !timeline_->IsMonotonicallyIncreasing(); @@ -1311,21 +1340,23 @@ void Animation::PlayInternal(AutoRewind auto_rewind, // is unresolved, // 5c1. Set seek time to zero. double effective_playback_rate = EffectivePlaybackRate(); - base::Optional<double> current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> current_time = CurrentTimeInternal(); if (reset_current_time_on_resume_) { current_time = base::nullopt; reset_current_time_on_resume_ = false; } + base::Optional<AnimationTimeDelta> effect_end = EffectEnd(); if (effective_playback_rate > 0 && enable_seek && - (!current_time || current_time < 0 || current_time >= EffectEnd())) { - seek_time = 0; + (!current_time || current_time < AnimationTimeDelta() || + current_time >= effect_end)) { + seek_time = AnimationTimeDelta(); } else if (effective_playback_rate < 0 && enable_seek && - (!current_time || current_time <= 0 || + (!current_time || current_time <= AnimationTimeDelta() || current_time > EffectEnd())) { - if (EffectEnd() == std::numeric_limits<double>::infinity()) { + if (EffectEnd().is_inf()) { exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, "Cannot play reversed Animation with infinite target effect end."); @@ -1333,7 +1364,7 @@ void Animation::PlayInternal(AutoRewind auto_rewind, } seek_time = EffectEnd(); } else if (effective_playback_rate == 0 && !current_time) { - seek_time = 0; + seek_time = AnimationTimeDelta(); } // 6. If seek time is resolved, @@ -1441,8 +1472,7 @@ void Animation::finish(ExceptionState& exception_state) { "Cannot finish Animation with a playbackRate of 0."); return; } - if (EffectivePlaybackRate() > 0 && - EffectEnd() == std::numeric_limits<double>::infinity()) { + if (EffectivePlaybackRate() > 0 && EffectEnd().is_inf()) { exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, "Cannot finish Animation with an infinite target effect end."); @@ -1451,7 +1481,8 @@ void Animation::finish(ExceptionState& exception_state) { ApplyPendingPlaybackRate(); - double new_current_time = playback_rate_ < 0 ? 0 : EffectEnd(); + AnimationTimeDelta new_current_time = + playback_rate_ < 0 ? AnimationTimeDelta() : EffectEnd(); SetCurrentTimeInternal(new_current_time); if (!start_time_ && timeline_ && timeline_->IsActive()) @@ -1483,7 +1514,7 @@ void Animation::UpdateFinishedState(UpdateType update_type, // required to accommodate timelines that may change direction. Without this // distinction, a once-finished animation would remain finished even when its // timeline progresses in the opposite direction. - base::Optional<double> unconstrained_current_time = + base::Optional<AnimationTimeDelta> unconstrained_current_time = did_seek ? CurrentTimeInternal() : CalculateCurrentTime(); // 2. Conditionally update the hold time. @@ -1497,22 +1528,42 @@ void Animation::UpdateFinishedState(UpdateType update_type, // boundary. The value of previous current time is used to retain this // value. double playback_rate = EffectivePlaybackRate(); - base::Optional<double> hold_time; + base::Optional<AnimationTimeDelta> hold_time; TimelinePhase hold_phase; - if (playback_rate > 0 && unconstrained_current_time >= EffectEnd()) { - hold_time = did_seek ? unconstrained_current_time - : Max(previous_current_time_, EffectEnd()); + + if (playback_rate > 0 && + GreaterThanOrEqualWithinTimeTolerance( + unconstrained_current_time.value(), EffectEnd())) { + if (did_seek) { + hold_time = unconstrained_current_time; + } else { + if (previous_current_time_ > EffectEnd()) { + hold_time = previous_current_time_; + } else { + hold_time = EffectEnd(); + } + } hold_phase = did_seek ? TimelinePhase::kActive : CalculateCurrentPhase(); SetHoldTimeAndPhase(hold_time, hold_phase); - } else if (playback_rate < 0 && unconstrained_current_time <= 0) { - hold_time = did_seek ? unconstrained_current_time - : Min(previous_current_time_, 0); + } else if (playback_rate < 0 && + unconstrained_current_time.value() <= AnimationTimeDelta()) { + if (did_seek) { + hold_time = unconstrained_current_time; + } else { + if (previous_current_time_ <= AnimationTimeDelta()) { + hold_time = previous_current_time_; + } else { + hold_time = AnimationTimeDelta(); + } + } hold_phase = did_seek ? TimelinePhase::kActive : CalculateCurrentPhase(); // Hack for resolving precision issue at zero. - if (hold_time.value() == -0) - hold_time = 0; + if (hold_time.has_value() && + IsWithinAnimationTimeEpsilon(hold_time.value().InSecondsF(), -0)) { + hold_time = AnimationTimeDelta(); + } SetHoldTimeAndPhase(hold_time, hold_phase); } else if (playback_rate != 0) { @@ -1573,7 +1624,7 @@ void Animation::AsyncFinishMicrotask() { if (pending_finish_notification_) { // A pending play or pause must resolve before the finish promise. if (PendingInternal() && timeline_) - NotifyReady(timeline_->CurrentTimeSeconds().value_or(0)); + NotifyReady(timeline_->CurrentTime().value_or(AnimationTimeDelta())); CommitFinishNotification(); } @@ -1645,14 +1696,14 @@ void Animation::updatePlaybackRate(double playback_rate, // animation with the did seek flag set to false, and the // synchronously notify flag set to false. case kFinished: { - base::Optional<double> unconstrained_current_time = + base::Optional<AnimationTimeDelta> unconstrained_current_time = CalculateCurrentTime(); - base::Optional<double> timeline_time = - timeline_ ? timeline_->CurrentTimeSeconds() : base::nullopt; + base::Optional<AnimationTimeDelta> timeline_time = + timeline_ ? timeline_->CurrentTime() : base::nullopt; if (playback_rate) { if (timeline_time) { start_time_ = (timeline_time && unconstrained_current_time) - ? base::make_optional<double>( + ? base::make_optional<AnimationTimeDelta>( (timeline_time.value() - unconstrained_current_time.value()) / playback_rate) @@ -1768,7 +1819,7 @@ void Animation::ApplyPendingPlaybackRate() { void Animation::setPlaybackRate(double playback_rate, ExceptionState& exception_state) { - base::Optional<double> start_time_before = start_time_; + base::Optional<AnimationTimeDelta> start_time_before = start_time_; // 1. Clear any pending playback rate on animation. // 2. Let previous time be the value of the current time of animation before @@ -1777,9 +1828,10 @@ void Animation::setPlaybackRate(double playback_rate, // 4. If previous time is resolved, set the current time of animation to // previous time pending_playback_rate_ = base::nullopt; - base::Optional<double> previous_current_time = currentTime(); + CSSNumberish previous_current_time; + currentTime(previous_current_time); playback_rate_ = playback_rate; - if (previous_current_time.has_value()) { + if (!previous_current_time.IsNull()) { setCurrentTime(previous_current_time, exception_state); } @@ -1855,7 +1907,7 @@ Animation::CheckCanStartAnimationOnCompositorInternal() const { // enable compositing provided the iteration duration is finite. Having an // infinite number of iterations in the animation should not impede the // ability to composite the animation. - if (std::isinf(EffectEnd()) && EffectivePlaybackRate() < 0) + if (EffectEnd().is_inf() && EffectivePlaybackRate() < 0) reasons |= CompositorAnimations::kInvalidAnimationOrEffect; // An Animation without a timeline effectively isn't playing, so there is no @@ -1893,10 +1945,13 @@ void Animation::StartAnimationOnCompositor( CheckCanStartAnimationOnCompositor(paint_artifact_compositor, nullptr), CompositorAnimations::kNoFailure); + // If PlaybackRate is 0, then we will run into divide by 0 issues. + DCHECK(!IsWithinAnimationTimeEpsilon(0, EffectivePlaybackRate())); + bool reversed = EffectivePlaybackRate() < 0; - base::Optional<double> start_time = base::nullopt; - double time_offset = 0; + base::Optional<AnimationTimeDelta> start_time; + AnimationTimeDelta time_offset = AnimationTimeDelta(); // Start the animation on the compositor with either a start time or time // offset. The start time is used for synchronous updates where the // compositor start time must be in precise alignment with the specified time @@ -1905,27 +1960,30 @@ void Animation::StartAnimationOnCompositor( // Asynchronous updates have an associated pending play or pending pause // task associated with them. if (start_time_ && !PendingInternal()) { - start_time = timeline_->ZeroTimeInSeconds() + start_time_.value(); + start_time = timeline_->ZeroTime() + start_time_.value(); if (reversed) { start_time = start_time.value() - (EffectEnd() / fabs(EffectivePlaybackRate())); } - DCHECK(std::isfinite(start_time.value())); } else { - base::Optional<double> current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> current_time = CurrentTimeInternal(); DCHECK(current_time); time_offset = reversed ? EffectEnd() - current_time.value() : current_time.value(); time_offset = time_offset / fabs(EffectivePlaybackRate()); - DCHECK(std::isfinite(time_offset)); } DCHECK_NE(compositor_group_, 0); DCHECK(To<KeyframeEffect>(content_.Get())); + base::Optional<double> start_time_s; + if (start_time) { + start_time_s = start_time.value().InSecondsF(); + } To<KeyframeEffect>(content_.Get()) - ->StartAnimationOnCompositor(compositor_group_, start_time, - base::TimeDelta::FromSecondsD(time_offset), - EffectivePlaybackRate()); + ->StartAnimationOnCompositor( + compositor_group_, start_time_s, + base::TimeDelta::FromSecondsD(time_offset.InSecondsF()), + EffectivePlaybackRate()); } // TODO(crbug.com/960944): Rename to SetPendingCommit. This method handles both @@ -1952,7 +2010,10 @@ void Animation::SetCompositorPending(bool effect_changed) { if (PendingInternal() || !compositor_state_ || compositor_state_->effect_changed || compositor_state_->playback_rate != EffectivePlaybackRate() || - compositor_state_->start_time != start_time_ || + compositor_state_->start_time.has_value() != start_time_.has_value() || + (compositor_state_->start_time && start_time_ && + !IsWithinAnimationTimeEpsilon(compositor_state_->start_time.value(), + start_time_.value().InSecondsF())) || !compositor_state_->start_time || !start_time_) { compositor_pending_ = true; document_->GetPendingAnimations().Add(this); @@ -2002,14 +2063,14 @@ bool Animation::Update(TimingUpdateReason reason) { UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync); if (content_) { - base::Optional<double> inherited_time; + base::Optional<AnimationTimeDelta> inherited_time; TimelinePhase inherited_phase = TimelinePhase::kInactive; if (!idle) { inherited_time = CurrentTimeInternal(); // Special case for end-exclusivity when playing backwards. - if (inherited_time == 0 && EffectivePlaybackRate() < 0) - inherited_time = -1; + if (inherited_time == AnimationTimeDelta() && EffectivePlaybackRate() < 0) + inherited_time = AnimationTimeDelta::FromSecondsD(-1); inherited_phase = CurrentPhaseInternal(); } @@ -2049,12 +2110,14 @@ bool Animation::Update(TimingUpdateReason reason) { void Animation::QueueFinishedEvent() { const AtomicString& event_type = event_type_names::kFinish; if (GetExecutionContext() && HasEventListeners(event_type)) { - base::Optional<double> event_current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> event_current_time = + CurrentTimeInternal(); + base::Optional<double> event_current_time_ms; if (event_current_time) - event_current_time = SecondsToMilliseconds(event_current_time.value()); + event_current_time_ms = event_current_time.value().InMillisecondsF(); // TODO(crbug.com/916117): Handle NaN values for scroll-linked animations. pending_finished_event_ = MakeGarbageCollected<AnimationPlaybackEvent>( - event_type, event_current_time, TimelineTime()); + event_type, event_current_time_ms, TimelineTime()); pending_finished_event_->SetTarget(this); pending_finished_event_->SetCurrentTarget(this); document_->EnqueueAnimationFrameEvent(pending_finished_event_); @@ -2084,10 +2147,9 @@ base::Optional<AnimationTimeDelta> Animation::TimeToEffectChange() { return base::nullopt; if (!content_) { - base::Optional<double> current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> current_time = CurrentTimeInternal(); DCHECK(current_time); - return AnimationTimeDelta::FromSecondsD(-current_time.value() / - playback_rate_); + return -current_time.value() / playback_rate_; } if (!HasActiveAnimationsOnCompositor() && @@ -2100,7 +2162,8 @@ base::Optional<AnimationTimeDelta> Animation::TimeToEffectChange() { } void Animation::cancel() { - double current_time_before_cancel = CurrentTimeInternal().value_or(0); + AnimationTimeDelta current_time_before_cancel = + CurrentTimeInternal().value_or(AnimationTimeDelta()); AnimationPlayState initial_play_state = CalculateAnimationPlayState(); if (initial_play_state != kIdle) { ResetPendingTasks(); @@ -2223,7 +2286,7 @@ void Animation::AddedEventListener( UseCounter::Count(GetExecutionContext(), WebFeature::kAnimationFinishEvent); } -void Animation::PauseForTesting(double pause_time) { +void Animation::PauseForTesting(AnimationTimeDelta pause_time) { // Do not restart a canceled animation. if (CalculateAnimationPlayState() == kIdle) return; @@ -2232,11 +2295,11 @@ void Animation::PauseForTesting(double pause_time) { // animation. SetCurrentTimeInternal(pause_time); if (HasActiveAnimationsOnCompositor()) { - base::Optional<double> current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> current_time = CurrentTimeInternal(); DCHECK(current_time); To<KeyframeEffect>(content_.Get()) ->PauseAnimationForTestingOnCompositor( - base::TimeDelta::FromSecondsD(current_time.value())); + base::TimeDelta::FromSecondsD(current_time.value().InSecondsF())); } // Do not wait for animation ready to lock in the hold time. Otherwise, @@ -2420,11 +2483,13 @@ void Animation::RemoveReplacedAnimation() { replace_state_ = kRemoved; const AtomicString& event_type = event_type_names::kRemove; if (GetExecutionContext() && HasEventListeners(event_type)) { - base::Optional<double> event_current_time = CurrentTimeInternal(); + base::Optional<AnimationTimeDelta> event_current_time = + CurrentTimeInternal(); + base::Optional<double> event_current_time_ms; if (event_current_time) - event_current_time = SecondsToMilliseconds(event_current_time.value()); + event_current_time_ms = event_current_time.value().InMillisecondsF(); pending_remove_event_ = MakeGarbageCollected<AnimationPlaybackEvent>( - event_type, event_current_time, TimelineTime()); + event_type, event_current_time_ms, TimelineTime()); pending_remove_event_->SetTarget(this); pending_remove_event_->SetCurrentTarget(this); document_->EnqueueAnimationFrameEvent(pending_remove_event_); @@ -2533,6 +2598,24 @@ void Animation::commitStyles(ExceptionState& exception_state) { WrapWeakPersistent(inline_style), WrapWeakPersistent(target))); } +bool Animation::IsInDisplayLockedSubtree() { + Element* owning_element = OwningElement(); + if (!owning_element || !GetDocument()) + return false; + + base::TimeTicks display_lock_update_timestamp = + GetDocument()->GetDisplayLockDocumentState().GetLockUpdateTimestamp(); + + if (last_display_lock_update_time_ < display_lock_update_timestamp) { + const Element* element = + DisplayLockUtilities::NearestLockedExclusiveAncestor(*owning_element); + is_in_display_locked_subtree_ = !!element; + last_display_lock_update_time_ = display_lock_update_timestamp; + } + + return is_in_display_locked_subtree_; +} + void Animation::Trace(Visitor* visitor) const { visitor->Trace(content_); visitor->Trace(document_); diff --git a/chromium/third_party/blink/renderer/core/animation/animation.h b/chromium/third_party/blink/renderer/core/animation/animation.h index caeacc7cf95..1a512b861d3 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation.h +++ b/chromium/third_party/blink/renderer/core/animation/animation.h @@ -36,6 +36,7 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/optional.h" +#include "base/time/time.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" @@ -44,6 +45,7 @@ #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/css/cssom/css_numeric_value.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" @@ -99,6 +101,10 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, // relative position. enum CompareAnimationsOrdering { kTreeOrder, kPointerOrder }; + // Only expect timing accuracy to within 1 microsecond. + // drafts.csswg.org/web-animations/#precision-of-time-values. + static constexpr double kTimeToleranceMs = 0.001; + static Animation* Create(AnimationEffect*, AnimationTimeline*, ExceptionState& = ASSERT_NO_EXCEPTION); @@ -141,12 +147,13 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, 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); + void currentTime(CSSNumberish&) const; + base::Optional<AnimationTimeDelta> CurrentTimeInternal() const; + void setCurrentTime(CSSNumberish, ExceptionState& exception_state); + void setCurrentTime(CSSNumberish); + void SetCurrentTimeInternal(AnimationTimeDelta); - base::Optional<double> UnlimitedCurrentTime() const; + base::Optional<AnimationTimeDelta> UnlimitedCurrentTime() const; // https://drafts.csswg.org/web-animations/#play-states String PlayStateString() const; @@ -202,10 +209,12 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, virtual void setTimeline(AnimationTimeline* 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>); + void startTime(CSSNumberish&) const; + base::Optional<AnimationTimeDelta> StartTimeInternal() const { + return start_time_; + } + virtual void setStartTime(CSSNumberish, ExceptionState&); + void setStartTime(CSSNumberish); const AnimationEffect* effect() const { return content_.Get(); } AnimationEffect* effect() { return content_.Get(); } @@ -216,7 +225,7 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, // 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 PauseForTesting(AnimationTimeDelta pause_time); void DisableCompositedAnimationForTesting(); // This should only be used for CSS @@ -238,9 +247,9 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, 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); + void NotifyReady(AnimationTimeDelta ready_time); + void CommitPendingPlay(AnimationTimeDelta ready_time); + void CommitPendingPause(AnimationTimeDelta ready_time); // CompositorAnimationClient implementation. CompositorAnimation* GetCompositorAnimation() const override { return compositor_animation_ ? compositor_animation_->GetAnimation() @@ -291,11 +300,16 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, // depends on computed values. virtual void FlushPendingUpdates() const {} + bool IsInDisplayLockedSubtree(); + + void SetCanCompositeBGColorAnim() { can_composite_bgcolor_anim_ = true; } + void ResetCanCompositeBGColorAnim() { can_composite_bgcolor_anim_ = false; } + bool CanCompositeBGColorAnim() const { return can_composite_bgcolor_anim_; } + 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, @@ -304,18 +318,16 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, } private: - void SetCurrentTimeInternal(double new_current_time); - void SetHoldTimeAndPhase( - base::Optional<double> new_hold_time /* in seconds */, - TimelinePhase new_hold_phase); + void SetHoldTimeAndPhase(base::Optional<AnimationTimeDelta> new_hold_time, + TimelinePhase new_hold_phase); void ResetHoldTimeAndPhase(); bool ValidateHoldTimeAndPhase() const; void ClearOutdated(); void ForceServiceOnNextFrame(); - double EffectEnd() const; - bool Limited(base::Optional<double> current_time) const; + AnimationTimeDelta EffectEnd() const; + bool Limited(base::Optional<AnimationTimeDelta> 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 @@ -323,8 +335,9 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, double EffectivePlaybackRate() const; void ApplyPendingPlaybackRate(); - base::Optional<double> CalculateStartTime(double current_time) const; - base::Optional<double> CalculateCurrentTime() const; + base::Optional<AnimationTimeDelta> CalculateStartTime( + AnimationTimeDelta current_time) const; + base::Optional<AnimationTimeDelta> CalculateCurrentTime() const; TimelinePhase CalculateCurrentPhase() const; void BeginUpdatingState(); @@ -387,10 +400,10 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, // 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<AnimationTimeDelta> start_time_; + base::Optional<AnimationTimeDelta> hold_time_; base::Optional<TimelinePhase> hold_phase_; - base::Optional<double> previous_current_time_; + base::Optional<AnimationTimeDelta> previous_current_time_; bool reset_current_time_on_resume_ = false; unsigned sequence_number_; @@ -445,9 +458,17 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, USING_FAST_MALLOC(CompositorState); public: + // TODO(https://crbug.com/1166397): Convert composited animations to use + // AnimationTimeDelta for start_time_ and hold_time_. explicit CompositorState(Animation& animation) - : start_time(animation.start_time_), - hold_time(animation.hold_time_), + : start_time(animation.start_time_ + ? base::make_optional( + animation.start_time_.value().InSecondsF()) + : base::nullopt), + hold_time(animation.hold_time_ + ? base::make_optional( + animation.hold_time_.value().InSecondsF()) + : base::nullopt), playback_rate(animation.EffectivePlaybackRate()), effect_changed(false), pending_action(animation.start_time_ ? kNone : kStart) {} @@ -503,6 +524,20 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, bool effect_suppressed_; + // True if the background color animation can be composited. Set by the + // BackgroundColorPaintWorklet::GetBGColorPaintWorkletParams. We keep it here + // instead of ElementAnimations such that when we create a compositor + // animation, we know which animation should fall back. Note that when we + // extend the native paint worklet to composite other types of animations in + // the future, we might need to extend this to be a fall back reasons. + bool can_composite_bgcolor_anim_ = false; + + // Animations with an owning element stop ticking if there is an active + // display lock on an ancestor element. Cache the status to minimize the + // number of tree walks. + base::TimeTicks last_display_lock_update_time_ = base::TimeTicks(); + bool is_in_display_locked_subtree_ = false; + FRIEND_TEST_ALL_PREFIXES(AnimationAnimationTestCompositeAfterPaint, NoCompositeWithoutCompositedElementId); FRIEND_TEST_ALL_PREFIXES(AnimationAnimationTestNoCompositing, diff --git a/chromium/third_party/blink/renderer/core/animation/animation.idl b/chromium/third_party/blink/renderer/core/animation/animation.idl index ee01f455ec1..0bdb34f11c0 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation.idl +++ b/chromium/third_party/blink/renderer/core/animation/animation.idl @@ -41,8 +41,8 @@ enum ReplaceState { "active", "removed", "persisted" }; [CallWith=ExecutionContext, RaisesException] constructor(optional AnimationEffect? effect = null, optional AnimationTimeline? timeline); [Measure] attribute AnimationEffect? effect; [RuntimeEnabled=WebAnimationsAPI] attribute AnimationTimeline? timeline; - [Measure, RaisesException=Setter] attribute double? startTime; - [Measure, RaisesException=Setter] attribute double? currentTime; + [Measure, RaisesException=Setter] attribute CSSNumberish? startTime; + [Measure, RaisesException=Setter] attribute CSSNumberish? currentTime; [Measure, RaisesException=Setter] attribute double playbackRate; [Measure] readonly attribute AnimationPlayState playState; [RuntimeEnabled=WebAnimationsAPI, Measure] readonly attribute ReplaceState replaceState; diff --git a/chromium/third_party/blink/renderer/core/animation/animation_effect.cc b/chromium/third_party/blink/renderer/core/animation/animation_effect.cc index 286f2d33216..6cdacfffe51 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_effect.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation_effect.cc @@ -47,7 +47,7 @@ AnimationEffect::AnimationEffect(const Timing& timing, timing_(timing), event_delegate_(event_delegate), needs_update_(true), - cancel_time_(0) { + cancel_time_(AnimationTimeDelta()) { timing_.AssertValid(); } @@ -131,7 +131,7 @@ base::Optional<Timing::Phase> TimelinePhaseToTimingPhase( } void AnimationEffect::UpdateInheritedTime( - base::Optional<double> inherited_time, + base::Optional<AnimationTimeDelta> inherited_time, base::Optional<TimelinePhase> inherited_timeline_phase, TimingUpdateReason reason) const { base::Optional<double> playback_rate = base::nullopt; @@ -152,7 +152,9 @@ void AnimationEffect::UpdateInheritedTime( last_update_time_ = inherited_time; last_update_phase_ = timeline_phase; - const base::Optional<double> local_time = inherited_time; + const base::Optional<double> local_time = + inherited_time ? base::make_optional(inherited_time.value().InSecondsF()) + : base::nullopt; if (needs_update) { Timing::CalculatedTiming calculated = SpecifiedTiming().CalculateTimings( local_time, timeline_phase, direction, IsA<KeyframeEffect>(this), diff --git a/chromium/third_party/blink/renderer/core/animation/animation_effect.h b/chromium/third_party/blink/renderer/core/animation/animation_effect.h index a2373d4f7de..c876426d7e9 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_effect.h +++ b/chromium/third_party/blink/renderer/core/animation/animation_effect.h @@ -114,8 +114,10 @@ class CORE_EXPORT AnimationEffect : public ScriptWrappable { ComputedEffectTiming* getComputedTiming() const; void updateTiming(OptionalEffectTiming*, ExceptionState& = ASSERT_NO_EXCEPTION); - double GetCancelTime() const { return cancel_time_; } - void SetCancelTime(double cancel_time) { cancel_time_ = cancel_time; } + AnimationTimeDelta GetCancelTime() const { return cancel_time_; } + void SetCancelTime(AnimationTimeDelta cancel_time) { + cancel_time_ = cancel_time; + } // Attach/Detach the AnimationEffect from its owning animation. virtual void Attach(AnimationEffectOwner* owner) { owner_ = owner; } @@ -134,7 +136,7 @@ class CORE_EXPORT AnimationEffect : public ScriptWrappable { // When AnimationEffect receives a new inherited time via updateInheritedTime // it will (if necessary) recalculate timings and (if necessary) call // updateChildrenAndEffects. - void UpdateInheritedTime(base::Optional<double> inherited_time, + void UpdateInheritedTime(base::Optional<AnimationTimeDelta> inherited_time, base::Optional<TimelinePhase> inherited_phase, TimingUpdateReason) const; void Invalidate() const { needs_update_ = true; } @@ -168,9 +170,9 @@ class CORE_EXPORT AnimationEffect : public ScriptWrappable { mutable Timing::CalculatedTiming calculated_; mutable bool needs_update_; - mutable base::Optional<double> last_update_time_; + mutable base::Optional<AnimationTimeDelta> last_update_time_; mutable base::Optional<Timing::Phase> last_update_phase_; - double cancel_time_; + AnimationTimeDelta cancel_time_; const Timing::CalculatedTiming& EnsureCalculated() const; }; diff --git a/chromium/third_party/blink/renderer/core/animation/animation_effect_test.cc b/chromium/third_party/blink/renderer/core/animation/animation_effect_test.cc index bacd7956bf9..9a810175445 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_effect_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation_effect_test.cc @@ -84,8 +84,9 @@ class TestAnimationEffect : public AnimationEffect { void UpdateInheritedTime(double time, TimingUpdateReason reason) { event_delegate_->Reset(); - AnimationEffect::UpdateInheritedTime( - time, /*inherited_phase*/ base::nullopt, reason); + AnimationEffect::UpdateInheritedTime(AnimationTimeDelta::FromSecondsD(time), + /*inherited_phase*/ base::nullopt, + reason); } void UpdateChildrenAndEffects() const override {} diff --git a/chromium/third_party/blink/renderer/core/animation/animation_input_helpers.cc b/chromium/third_party/blink/renderer/core/animation/animation_input_helpers.cc index fd95d13cd49..86a7a8b13d0 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_input_helpers.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation_input_helpers.cc @@ -79,7 +79,7 @@ CSSPropertyID AnimationInputHelpers::KeyframeAttributeToCSSProperty( builder.Append('-'); builder.Append(property[i]); } - return cssPropertyID(document.GetExecutionContext(), builder.ToString()); + return CssPropertyID(document.GetExecutionContext(), builder.ToString()); } CSSPropertyID AnimationInputHelpers::KeyframeAttributeToPresentationAttribute( @@ -92,7 +92,7 @@ CSSPropertyID AnimationInputHelpers::KeyframeAttributeToPresentationAttribute( String unprefixed_property = RemoveSVGPrefix(property); if (SVGElement::IsAnimatableCSSProperty(QualifiedName( g_null_atom, AtomicString(unprefixed_property), g_null_atom))) { - return cssPropertyID(element->GetExecutionContext(), unprefixed_property); + return CssPropertyID(element->GetExecutionContext(), unprefixed_property); } return CSSPropertyID::kInvalid; } diff --git a/chromium/third_party/blink/renderer/core/animation/animation_test.cc b/chromium/third_party/blink/renderer/core/animation/animation_test.cc index 151f3417187..5916636297d 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation_test.cc @@ -68,14 +68,6 @@ namespace blink { -namespace { - -double MillisecondsToSeconds(double milliseconds) { - return milliseconds / 1000; -} - -} // namespace - void ExpectRelativeErrorWithinEpsilon(double expected, double observed) { EXPECT_NEAR(1.0, observed / expected, std::numeric_limits<double>::epsilon()); } @@ -97,7 +89,7 @@ class AnimationAnimationTestNoCompositing : public RenderingTest { timeline = GetDocument().Timeline(); timeline->ResetForTesting(); animation = timeline->Play(nullptr); - animation->setStartTime(0); + animation->setStartTime(CSSNumberish::FromDouble(0)); animation->setEffect(MakeAnimation()); } @@ -193,8 +185,10 @@ class AnimationAnimationTestNoCompositing : public RenderingTest { } bool SimulateFrame(double time_ms) { - if (animation->pending()) - animation->NotifyReady(MillisecondsToSeconds(last_frame_time)); + if (animation->pending()) { + animation->NotifyReady( + AnimationTimeDelta::FromMillisecondsD(last_frame_time)); + } SimulateMicrotask(); last_frame_time = time_ms; @@ -222,6 +216,33 @@ class AnimationAnimationTestNoCompositing : public RenderingTest { GetPage().Animator().ServiceScriptedAnimations(new_time); } + bool StartTimeIsSet(Persistent<blink::Animation> animation) { + CSSNumberish start_time; + animation->startTime(start_time); + return !start_time.IsNull(); + } + + bool CurrentTimeIsSet(Persistent<blink::Animation> animation) { + CSSNumberish current_time; + animation->currentTime(current_time); + return !current_time.IsNull(); + } + + double GetStartTimeMs(Persistent<blink::Animation> animation) { + CSSNumberish start_time; + animation->startTime(start_time); + return start_time.GetAsDouble(); + } + + double GetCurrentTimeMs(Persistent<blink::Animation> animation) { + CSSNumberish current_time; + animation->currentTime(current_time); + return current_time.GetAsDouble(); + } + +#define EXPECT_TIME(expected, observed) \ + EXPECT_NEAR(expected, observed, Animation::kTimeToleranceMs) + Persistent<DocumentTimeline> timeline; Persistent<Animation> animation; @@ -231,6 +252,38 @@ class AnimationAnimationTestNoCompositing : public RenderingTest { class AnimationAnimationTestCompositing : public AnimationAnimationTestNoCompositing { + public: + Animation* CreateAnimation(CSSPropertyID property_id, + String from, + String to) { + Timing timing; + timing.iteration_duration = AnimationTimeDelta::FromSecondsD(30); + + Persistent<StringKeyframe> start_keyframe = + MakeGarbageCollected<StringKeyframe>(); + start_keyframe->SetCSSPropertyValue( + property_id, from, SecureContextMode::kInsecureContext, nullptr); + Persistent<StringKeyframe> end_keyframe = + MakeGarbageCollected<StringKeyframe>(); + end_keyframe->SetCSSPropertyValue( + property_id, to, SecureContextMode::kInsecureContext, nullptr); + + StringKeyframeVector keyframes; + keyframes.push_back(start_keyframe); + keyframes.push_back(end_keyframe); + + Element* element = GetElementById("target"); + auto* model = MakeGarbageCollected<StringKeyframeEffectModel>(keyframes); + + NonThrowableExceptionState exception_state; + DocumentTimeline* timeline = + MakeGarbageCollected<DocumentTimeline>(&GetDocument()); + return Animation::Create( + MakeGarbageCollected<KeyframeEffect>(element, model, timing), timeline, + exception_state); + } + + private: void SetUp() override { EnableCompositing(); AnimationAnimationTestNoCompositing::SetUp(); @@ -250,155 +303,158 @@ class AnimationAnimationTestCompositeAfterPaint TEST_F(AnimationAnimationTestNoCompositing, InitialState) { SetUpWithoutStartingTimeline(); animation = timeline->Play(nullptr); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->pending()); EXPECT_FALSE(animation->Paused()); EXPECT_EQ(1, animation->playbackRate()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(StartTimeIsSet(animation)); StartTimeline(); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(0, timeline->CurrentTimeMilliseconds()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, timeline->CurrentTimeMilliseconds().value()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_FALSE(animation->Paused()); EXPECT_FALSE(animation->pending()); EXPECT_EQ(1, animation->playbackRate()); - EXPECT_EQ(0, animation->startTime()); + EXPECT_TIME(0, GetStartTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, CurrentTimeDoesNotSetOutdated) { EXPECT_FALSE(animation->Outdated()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_FALSE(animation->Outdated()); // FIXME: We should split simulateFrame into a version that doesn't update // the animation and one that does, as most of the tests don't require // update() to be called. GetDocument().GetAnimationClock().UpdateTime( base::TimeTicks() + base::TimeDelta::FromMilliseconds(10000)); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); EXPECT_FALSE(animation->Outdated()); } TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTime) { EXPECT_EQ("running", animation->playState()); - animation->setCurrentTime(10000); + animation->setCurrentTime(CSSNumberish::FromDouble(10000)); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateFrame(10000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimeNegative) { - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(-10000, animation->currentTime()); + EXPECT_TIME(-10000, GetCurrentTimeMs(animation)); SimulateFrame(20000); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); animation->setPlaybackRate(-2); - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); EXPECT_EQ("finished", animation->playState()); // A seek can set current time outside the range [0, EffectEnd()]. - EXPECT_EQ(-10000, animation->currentTime()); + EXPECT_TIME(-10000, GetCurrentTimeMs(animation)); SimulateFrame(40000); // Hold current time even though outside normal range for the animation. EXPECT_FALSE(animation->pending()); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(-10000, animation->currentTime()); + EXPECT_TIME(-10000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimeNegativeWithoutSimultaneousPlaybackRateChange) { SimulateFrame(20000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); EXPECT_EQ("running", animation->playState()); // Reversing the direction preserves current time. animation->setPlaybackRate(-1); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); SimulateAwaitReady(); SimulateFrame(30000); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); EXPECT_EQ("running", animation->playState()); - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); EXPECT_EQ("finished", animation->playState()); } TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimePastContentEnd) { - animation->setCurrentTime(50000); + animation->setCurrentTime(CSSNumberish::FromDouble(50000)); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(50000, animation->currentTime()); + EXPECT_TIME(50000, GetCurrentTimeMs(animation)); SimulateFrame(20000); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(50000, animation->currentTime()); + EXPECT_TIME(50000, GetCurrentTimeMs(animation)); // Reversing the play direction changes the play state from finished to // running. animation->setPlaybackRate(-2); - animation->setCurrentTime(50000); + animation->setCurrentTime(CSSNumberish::FromDouble(50000)); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(50000, animation->currentTime()); + EXPECT_TIME(50000, GetCurrentTimeMs(animation)); SimulateAwaitReady(); SimulateFrame(40000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimeMax) { double limit = std::numeric_limits<double>::max(); - animation->setCurrentTime(limit); - ExpectRelativeErrorWithinEpsilon(limit, animation->currentTime().value()); + animation->setCurrentTime(CSSNumberish::FromDouble(limit)); + CSSNumberish current_time; + animation->currentTime(current_time); + ExpectRelativeErrorWithinEpsilon(limit, current_time.GetAsDouble()); SimulateFrame(100000); - ExpectRelativeErrorWithinEpsilon(limit, animation->currentTime().value()); + animation->currentTime(current_time); + ExpectRelativeErrorWithinEpsilon(limit, current_time.GetAsDouble()); } TEST_F(AnimationAnimationTestNoCompositing, SetCurrentTimeSetsStartTime) { - EXPECT_EQ(0, animation->startTime()); - animation->setCurrentTime(1000); - EXPECT_EQ(-1000, animation->startTime()); + EXPECT_TIME(0, GetStartTimeMs(animation)); + animation->setCurrentTime(CSSNumberish::FromDouble(1000)); + EXPECT_TIME(-1000, GetStartTimeMs(animation)); SimulateFrame(1000); - EXPECT_EQ(-1000, animation->startTime()); - EXPECT_EQ(2000, animation->currentTime()); + EXPECT_TIME(-1000, GetStartTimeMs(animation)); + EXPECT_TIME(2000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetStartTime) { SimulateFrame(20000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(0, animation->startTime()); - EXPECT_EQ(20000, animation->currentTime()); - animation->setStartTime(10000); + EXPECT_TIME(0, GetStartTimeMs(animation)); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); + animation->setStartTime(CSSNumberish::FromDouble(10000)); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(10000, animation->startTime()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetStartTimeMs(animation)); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateFrame(30000); - EXPECT_EQ(10000, animation->startTime()); - EXPECT_EQ(20000, animation->currentTime()); - animation->setStartTime(-20000); + EXPECT_TIME(10000, GetStartTimeMs(animation)); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); + animation->setStartTime(CSSNumberish::FromDouble(-20000)); EXPECT_EQ("finished", animation->playState()); } TEST_F(AnimationAnimationTestNoCompositing, SetStartTimeLimitsAnimation) { // Setting the start time is a seek operation, which is not constrained by the // normal limits on the animation. - animation->setStartTime(-50000); + animation->setStartTime(CSSNumberish::FromDouble(-50000)); EXPECT_EQ("finished", animation->playState()); EXPECT_TRUE(animation->Limited()); - EXPECT_EQ(50000, animation->currentTime()); + EXPECT_TIME(50000, GetCurrentTimeMs(animation)); animation->setPlaybackRate(-1); EXPECT_EQ("running", animation->playState()); - animation->setStartTime(-100000); + animation->setStartTime(CSSNumberish::FromDouble(-100000)); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(-100000, animation->currentTime()); + EXPECT_TIME(-100000, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->Limited()); } @@ -406,15 +462,15 @@ TEST_F(AnimationAnimationTestNoCompositing, SetStartTimeOnLimitedAnimation) { // The setStartTime method is a seek and thus not constrained by the normal // limits on the animation. SimulateFrame(30000); - animation->setStartTime(-10000); + animation->setStartTime(CSSNumberish::FromDouble(-10000)); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(40000, animation->currentTime()); + EXPECT_TIME(40000, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->Limited()); - animation->setCurrentTime(50000); - EXPECT_EQ(50000, animation->currentTime()); - animation->setStartTime(-40000); - EXPECT_EQ(70000, animation->currentTime()); + animation->setCurrentTime(CSSNumberish::FromDouble(50000)); + EXPECT_TIME(50000, GetCurrentTimeMs(animation)); + animation->setStartTime(CSSNumberish::FromDouble(-40000)); + EXPECT_TIME(70000, GetCurrentTimeMs(animation)); EXPECT_EQ("finished", animation->playState()); EXPECT_TRUE(animation->Limited()); } @@ -426,11 +482,11 @@ TEST_F(AnimationAnimationTestNoCompositing, StartTimePauseFinish) { EXPECT_TRUE(animation->pending()); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(StartTimeIsSet(animation)); animation->finish(exception_state); EXPECT_EQ("finished", animation->playState()); EXPECT_FALSE(animation->pending()); - EXPECT_EQ(-30000, animation->startTime()); + EXPECT_TIME(-30000, GetStartTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, FinishWhenPaused) { @@ -449,24 +505,24 @@ TEST_F(AnimationAnimationTestNoCompositing, FinishWhenPaused) { TEST_F(AnimationAnimationTestNoCompositing, StartTimeFinishPause) { NonThrowableExceptionState exception_state; animation->finish(exception_state); - EXPECT_EQ(-30000, animation->startTime()); + EXPECT_TIME(-30000, GetStartTimeMs(animation)); animation->pause(); EXPECT_EQ("paused", animation->playState()); EXPECT_TRUE(animation->pending()); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(StartTimeIsSet(animation)); } TEST_F(AnimationAnimationTestNoCompositing, StartTimeWithZeroPlaybackRate) { animation->setPlaybackRate(0); EXPECT_EQ("running", animation->playState()); SimulateAwaitReady(); - EXPECT_TRUE(animation->startTime().has_value()); + EXPECT_TRUE(StartTimeIsSet(animation)); SimulateFrame(10000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, PausePlay) { @@ -475,13 +531,13 @@ TEST_F(AnimationAnimationTestNoCompositing, PausePlay) { animation->pause(); EXPECT_EQ("paused", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); // Resume playing the animation at the 20s mark. SimulateFrame(20000); EXPECT_EQ("paused", animation->playState()); EXPECT_FALSE(animation->pending()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); animation->play(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); @@ -490,19 +546,19 @@ TEST_F(AnimationAnimationTestNoCompositing, PausePlay) { // Advance another 10s. SimulateFrame(30000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, PlayRewindsToStart) { // Auto-replay when starting from limit. - animation->setCurrentTime(30000); + animation->setCurrentTime(CSSNumberish::FromDouble(30000)); animation->play(); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); // Auto-replay when starting past the upper bound. - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); animation->play(); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); @@ -510,11 +566,11 @@ TEST_F(AnimationAnimationTestNoCompositing, PlayRewindsToStart) { // from a negative value of current time. SimulateFrame(10000); EXPECT_FALSE(animation->pending()); - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); EXPECT_EQ("running", animation->playState()); EXPECT_FALSE(animation->pending()); animation->play(); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); SimulateAwaitReady(); @@ -526,14 +582,14 @@ TEST_F(AnimationAnimationTestNoCompositing, PlayRewindsToEnd) { // Snap to end when playing a reversed animation from the start. animation->setPlaybackRate(-1); animation->play(); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); // Snap to end if playing a reversed animation starting past the upper limit. - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); animation->play(); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->pending()); SimulateFrame(10000); @@ -542,9 +598,9 @@ TEST_F(AnimationAnimationTestNoCompositing, PlayRewindsToEnd) { // Snap to the end if playing a reversed animation starting with a negative // value for current time. - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); animation->play(); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); @@ -559,15 +615,15 @@ TEST_F(AnimationAnimationTestNoCompositing, // becomes the hold time. animation->setPlaybackRate(0); animation->play(); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); animation->play(); - EXPECT_EQ(40000, animation->currentTime()); + EXPECT_TIME(40000, GetCurrentTimeMs(animation)); - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); animation->play(); - EXPECT_EQ(-10000, animation->currentTime()); + EXPECT_TIME(-10000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, @@ -583,76 +639,76 @@ TEST_F(AnimationAnimationTestNoCompositing, } TEST_F(AnimationAnimationTestNoCompositing, Reverse) { - animation->setCurrentTime(10000); + animation->setCurrentTime(CSSNumberish::FromDouble(10000)); animation->pause(); animation->reverse(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); // Effective playback rate does not kick in until the animation is ready. EXPECT_EQ(1, animation->playbackRate()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); EXPECT_EQ(-1, animation->playbackRate()); // Updating the playback rate does not change current time. - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, ReverseHoldsCurrentTimeWithPlaybackRateZero) { - animation->setCurrentTime(10000); + animation->setCurrentTime(CSSNumberish::FromDouble(10000)); animation->setPlaybackRate(0); animation->pause(); animation->reverse(); SimulateAwaitReady(); EXPECT_EQ("running", animation->playState()); EXPECT_EQ(0, animation->playbackRate()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateFrame(20000); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, ReverseSeeksToStart) { - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); animation->setPlaybackRate(-1); animation->reverse(); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, ReverseSeeksToEnd) { - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); animation->reverse(); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, ReverseBeyondLimit) { - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); animation->setPlaybackRate(-1); animation->reverse(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); animation->reverse(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, Finish) { NonThrowableExceptionState exception_state; animation->finish(exception_state); // Finished snaps to the end of the animation. - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); EXPECT_EQ("finished", animation->playState()); // Finished is a synchronous operation. EXPECT_FALSE(animation->pending()); animation->setPlaybackRate(-1); animation->finish(exception_state); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_EQ("finished", animation->playState()); EXPECT_FALSE(animation->pending()); } @@ -660,28 +716,28 @@ TEST_F(AnimationAnimationTestNoCompositing, Finish) { TEST_F(AnimationAnimationTestNoCompositing, FinishAfterEffectEnd) { NonThrowableExceptionState exception_state; // OK to set current time out of bounds. - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); animation->finish(exception_state); // The finish method triggers a snap to the upper boundary. - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, FinishBeforeStart) { NonThrowableExceptionState exception_state; - animation->setCurrentTime(-10000); + animation->setCurrentTime(CSSNumberish::FromDouble(-10000)); animation->setPlaybackRate(-1); animation->finish(exception_state); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, FinishDoesNothingWithPlaybackRateZero) { // Cannot finish an animation that has a playback rate of zero. DummyExceptionStateForTesting exception_state; - animation->setCurrentTime(10000); + animation->setCurrentTime(CSSNumberish::FromDouble(10000)); animation->setPlaybackRate(0); animation->finish(exception_state); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); EXPECT_TRUE(exception_state.HadException()); } @@ -693,11 +749,11 @@ TEST_F(AnimationAnimationTestNoCompositing, FinishRaisesException) { timing.iteration_count = std::numeric_limits<double>::infinity(); animation->setEffect(MakeGarbageCollected<KeyframeEffect>( nullptr, MakeEmptyEffectModel(), timing)); - animation->setCurrentTime(10000); + animation->setCurrentTime(CSSNumberish::FromDouble(10000)); DummyExceptionStateForTesting exception_state; animation->finish(exception_state); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); EXPECT_TRUE(exception_state.HadException()); EXPECT_EQ(DOMExceptionCode::kInvalidStateError, exception_state.CodeAs<DOMExceptionCode>()); @@ -705,12 +761,12 @@ TEST_F(AnimationAnimationTestNoCompositing, FinishRaisesException) { TEST_F(AnimationAnimationTestNoCompositing, LimitingAtEffectEnd) { SimulateFrame(30000); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->Limited()); // Cannot run past the end of the animation without a seek. SimulateFrame(40000); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); EXPECT_FALSE(animation->Paused()); } @@ -720,11 +776,11 @@ TEST_F(AnimationAnimationTestNoCompositing, LimitingAtStart) { SimulateAwaitReady(); SimulateFrame(45000); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->Limited()); SimulateFrame(60000); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); EXPECT_FALSE(animation->Paused()); } @@ -732,75 +788,75 @@ TEST_F(AnimationAnimationTestNoCompositing, LimitingWithNoEffect) { animation->setEffect(nullptr); EXPECT_TRUE(animation->Limited()); SimulateFrame(30000); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetPlaybackRate) { animation->setPlaybackRate(2); SimulateAwaitReady(); EXPECT_EQ(2, animation->playbackRate()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); SimulateFrame(10000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetPlaybackRateWhilePaused) { SimulateFrame(10000); animation->pause(); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); animation->setPlaybackRate(2); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateAwaitReady(); SimulateFrame(20000); animation->play(); // Change to playback rate does not alter current time. - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateAwaitReady(); SimulateFrame(25000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetPlaybackRateWhileLimited) { // Animation plays until it hits the upper bound. SimulateFrame(40000); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->Limited()); animation->setPlaybackRate(2); SimulateAwaitReady(); // Already at the end of the animation. SimulateFrame(50000); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); animation->setPlaybackRate(-2); SimulateAwaitReady(); SimulateFrame(60000); EXPECT_FALSE(animation->Limited()); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetPlaybackRateZero) { SimulateFrame(10000); animation->setPlaybackRate(0); - EXPECT_EQ(10000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); SimulateFrame(20000); - EXPECT_EQ(10000, animation->currentTime()); - animation->setCurrentTime(20000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); + animation->setCurrentTime(CSSNumberish::FromDouble(20000)); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetPlaybackRateMax) { animation->setPlaybackRate(std::numeric_limits<double>::max()); EXPECT_EQ(std::numeric_limits<double>::max(), animation->playbackRate()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); SimulateAwaitReady(); SimulateFrame(1); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRate) { @@ -808,10 +864,10 @@ TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRate) { EXPECT_EQ(1, animation->playbackRate()); SimulateAwaitReady(); EXPECT_EQ(2, animation->playbackRate()); - EXPECT_EQ(0, animation->currentTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); SimulateFrame(10000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRateWhilePaused) { @@ -836,11 +892,11 @@ TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRateWhilePaused) { TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRateWhileLimited) { NonThrowableExceptionState exception_state; animation->finish(exception_state); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); // Updating playback rate does not affect current time. animation->updatePlaybackRate(2); - EXPECT_EQ(30000, animation->currentTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); // Updating payback rate is resolved immediately for an animation in the // finished state. @@ -863,36 +919,36 @@ TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRateWhileRunning) { TEST_F(AnimationAnimationTestNoCompositing, SetEffect) { animation = timeline->Play(nullptr); - animation->setStartTime(0); + animation->setStartTime(CSSNumberish::FromDouble(0)); AnimationEffect* effect1 = MakeAnimation(); AnimationEffect* effect2 = MakeAnimation(); animation->setEffect(effect1); EXPECT_EQ(effect1, animation->effect()); - EXPECT_EQ(0, animation->currentTime()); - animation->setCurrentTime(15000); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); + animation->setCurrentTime(CSSNumberish::FromDouble(15000)); animation->setEffect(effect2); - EXPECT_EQ(15000, animation->currentTime()); + EXPECT_TIME(15000, GetCurrentTimeMs(animation)); EXPECT_EQ(nullptr, effect1->GetAnimationForTesting()); EXPECT_EQ(animation, effect2->GetAnimationForTesting()); EXPECT_EQ(effect2, animation->effect()); } TEST_F(AnimationAnimationTestNoCompositing, SetEffectLimitsAnimation) { - animation->setCurrentTime(20000); + animation->setCurrentTime(CSSNumberish::FromDouble(20000)); animation->setEffect(MakeAnimation(10)); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); EXPECT_TRUE(animation->Limited()); SimulateFrame(10000); - EXPECT_EQ(20000, animation->currentTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, SetEffectUnlimitsAnimation) { - animation->setCurrentTime(40000); + animation->setCurrentTime(CSSNumberish::FromDouble(40000)); animation->setEffect(MakeAnimation(60)); EXPECT_FALSE(animation->Limited()); - EXPECT_EQ(40000, animation->currentTime()); + EXPECT_TIME(40000, GetCurrentTimeMs(animation)); SimulateFrame(10000); - EXPECT_EQ(50000, animation->currentTime()); + EXPECT_TIME(50000, GetCurrentTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, EmptyAnimationsDontUpdateEffects) { @@ -912,6 +968,10 @@ TEST_F(AnimationAnimationTestNoCompositing, AnimationsDisassociateFromEffect) { EXPECT_EQ(nullptr, animation2->effect()); } +#define EXPECT_TIMEDELTA(expected, observed) \ + EXPECT_NEAR(expected.InMillisecondsF(), observed.InMillisecondsF(), \ + Animation::kTimeToleranceMs) + TEST_F(AnimationAnimationTestNoCompositing, AnimationsReturnTimeToNextEffect) { Timing timing; timing.start_delay = 1; @@ -920,46 +980,48 @@ TEST_F(AnimationAnimationTestNoCompositing, AnimationsReturnTimeToNextEffect) { auto* keyframe_effect = MakeGarbageCollected<KeyframeEffect>( nullptr, MakeEmptyEffectModel(), timing); animation = timeline->Play(keyframe_effect); - animation->setStartTime(0); + animation->setStartTime(CSSNumberish::FromDouble(0)); // Next effect change at end of start delay. SimulateFrame(0); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(1), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(1), + animation->TimeToEffectChange().value()); // Next effect change at end of start delay. SimulateFrame(500); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(0.5), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(0.5), + animation->TimeToEffectChange().value()); // Start of active phase. SimulateFrame(1000); - EXPECT_EQ(AnimationTimeDelta(), animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); // Still in active phase. SimulateFrame(1500); - EXPECT_EQ(AnimationTimeDelta(), animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); // Start of the after phase. Next effect change at end of after phase. SimulateFrame(2000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(1), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(1), + animation->TimeToEffectChange().value()); // Still in effect if fillmode = forward|both. SimulateFrame(3000); EXPECT_EQ(base::nullopt, animation->TimeToEffectChange()); // Reset to start of animation. Next effect at the end of the start delay. - animation->setCurrentTime(0); + animation->setCurrentTime(CSSNumberish::FromDouble(0)); SimulateFrame(3000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(1), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(1), + animation->TimeToEffectChange().value()); // Start delay is scaled by playback rate. animation->setPlaybackRate(2); SimulateFrame(3000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(0.5), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(0.5), + animation->TimeToEffectChange().value()); // Effectively a paused animation. animation->setPlaybackRate(0); @@ -967,23 +1029,24 @@ TEST_F(AnimationAnimationTestNoCompositing, AnimationsReturnTimeToNextEffect) { EXPECT_EQ(base::nullopt, animation->TimeToEffectChange()); // Reversed animation from end time. Next effect after end delay. - animation->setCurrentTime(3000); + animation->setCurrentTime(CSSNumberish::FromDouble(3000)); animation->setPlaybackRate(-1); animation->Update(kTimingUpdateOnDemand); SimulateFrame(3000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(1), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(1), + animation->TimeToEffectChange().value()); // End delay is scaled by playback rate. animation->setPlaybackRate(-2); animation->Update(kTimingUpdateOnDemand); SimulateFrame(3000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(0.5), - animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(0.5), + animation->TimeToEffectChange().value()); } TEST_F(AnimationAnimationTestNoCompositing, TimeToNextEffectWhenPaused) { - EXPECT_EQ(AnimationTimeDelta(), animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); animation->pause(); EXPECT_TRUE(animation->pending()); EXPECT_EQ("paused", animation->playState()); @@ -995,8 +1058,9 @@ TEST_F(AnimationAnimationTestNoCompositing, TimeToNextEffectWhenPaused) { TEST_F(AnimationAnimationTestNoCompositing, TimeToNextEffectWhenCancelledBeforeStart) { - EXPECT_EQ(AnimationTimeDelta(), animation->TimeToEffectChange()); - animation->setCurrentTime(-8000); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); + animation->setCurrentTime(CSSNumberish::FromDouble(-8000)); animation->setPlaybackRate(2); EXPECT_EQ("running", animation->playState()); animation->cancel(); @@ -1010,8 +1074,9 @@ TEST_F(AnimationAnimationTestNoCompositing, TEST_F(AnimationAnimationTestNoCompositing, TimeToNextEffectWhenCancelledBeforeStartReverse) { - EXPECT_EQ(AnimationTimeDelta(), animation->TimeToEffectChange()); - animation->setCurrentTime(9000); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); + animation->setCurrentTime(CSSNumberish::FromDouble(9000)); animation->setPlaybackRate(-3); EXPECT_EQ("running", animation->playState()); animation->cancel(); @@ -1023,7 +1088,8 @@ TEST_F(AnimationAnimationTestNoCompositing, TEST_F(AnimationAnimationTestNoCompositing, TimeToNextEffectSimpleCancelledBeforeStart) { - EXPECT_EQ(AnimationTimeDelta(), animation->TimeToEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); EXPECT_EQ("running", animation->playState()); animation->cancel(); EXPECT_EQ("idle", animation->playState()); @@ -1059,99 +1125,99 @@ TEST_F(AnimationAnimationTestNoCompositing, HasLowerCompositeOrdering) { TEST_F(AnimationAnimationTestNoCompositing, PlayAfterCancel) { animation->cancel(); EXPECT_EQ("idle", animation->playState()); - EXPECT_FALSE(animation->currentTime().has_value()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(CurrentTimeIsSet(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); animation->play(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(0, animation->currentTime()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); - EXPECT_EQ(0, animation->currentTime()); - EXPECT_EQ(0, animation->startTime()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); + EXPECT_TIME(0, GetStartTimeMs(animation)); SimulateFrame(10000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(10000, animation->currentTime()); - EXPECT_EQ(0, animation->startTime()); + EXPECT_TIME(10000, GetCurrentTimeMs(animation)); + EXPECT_TIME(0, GetStartTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, PlayBackwardsAfterCancel) { animation->setPlaybackRate(-1); - animation->setCurrentTime(15000); + animation->setCurrentTime(CSSNumberish::FromDouble(15000)); animation->cancel(); EXPECT_EQ("idle", animation->playState()); EXPECT_FALSE(animation->pending()); - EXPECT_FALSE(animation->currentTime().has_value()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(CurrentTimeIsSet(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); // Snap to the end of the animation. animation->play(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(30000, animation->currentTime()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); - EXPECT_EQ(30000, animation->startTime()); + EXPECT_TIME(30000, GetStartTimeMs(animation)); SimulateFrame(10000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(20000, animation->currentTime()); - EXPECT_EQ(30000, animation->startTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); + EXPECT_TIME(30000, GetStartTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, ReverseAfterCancel) { animation->cancel(); EXPECT_EQ("idle", animation->playState()); EXPECT_FALSE(animation->pending()); - EXPECT_FALSE(animation->currentTime().has_value()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(CurrentTimeIsSet(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); // Reverse snaps to the end of the animation. animation->reverse(); EXPECT_EQ("running", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(30000, animation->currentTime()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); - EXPECT_EQ(30000, animation->startTime()); + EXPECT_TIME(30000, GetStartTimeMs(animation)); SimulateFrame(10000); EXPECT_EQ("running", animation->playState()); - EXPECT_EQ(20000, animation->currentTime()); - EXPECT_EQ(30000, animation->startTime()); + EXPECT_TIME(20000, GetCurrentTimeMs(animation)); + EXPECT_TIME(30000, GetStartTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, FinishAfterCancel) { NonThrowableExceptionState exception_state; animation->cancel(); EXPECT_EQ("idle", animation->playState()); - EXPECT_FALSE(animation->currentTime().has_value()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(CurrentTimeIsSet(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); animation->finish(exception_state); EXPECT_EQ("finished", animation->playState()); - EXPECT_EQ(30000, animation->currentTime()); - EXPECT_EQ(-30000, animation->startTime()); + EXPECT_TIME(30000, GetCurrentTimeMs(animation)); + EXPECT_TIME(-30000, GetStartTimeMs(animation)); } TEST_F(AnimationAnimationTestNoCompositing, PauseAfterCancel) { animation->cancel(); EXPECT_EQ("idle", animation->playState()); - EXPECT_FALSE(animation->currentTime().has_value()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_FALSE(CurrentTimeIsSet(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); animation->pause(); EXPECT_EQ("paused", animation->playState()); EXPECT_TRUE(animation->pending()); - EXPECT_EQ(0, animation->currentTime()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); SimulateAwaitReady(); EXPECT_FALSE(animation->pending()); - EXPECT_EQ(0, animation->currentTime()); - EXPECT_FALSE(animation->startTime().has_value()); + EXPECT_TIME(0, GetCurrentTimeMs(animation)); + EXPECT_FALSE(StartTimeIsSet(animation)); } // crbug.com/1052217 @@ -1169,7 +1235,8 @@ TEST_F(AnimationAnimationTestNoCompositing, SetPlaybackRateAfterFinish) { EXPECT_EQ(animation->playbackRate(), -1); EXPECT_TRUE(animation->Outdated()); animation->Update(kTimingUpdateOnDemand); - EXPECT_EQ(0, animation->TimeToEffectChange()->InSecondsF()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); EXPECT_FALSE(animation->Outdated()); } @@ -1189,7 +1256,8 @@ TEST_F(AnimationAnimationTestNoCompositing, UpdatePlaybackRateAfterFinish) { EXPECT_EQ(animation->playbackRate(), -1); EXPECT_TRUE(animation->Outdated()); animation->Update(kTimingUpdateOnDemand); - EXPECT_EQ(0, animation->TimeToEffectChange()->InSecondsF()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + animation->TimeToEffectChange().value()); EXPECT_FALSE(animation->Outdated()); } @@ -1302,7 +1370,7 @@ TEST_F(AnimationAnimationTestCompositing, PreCommitRecordsHistograms) { // Now make the playback rate 0. This trips both the invalid animation and // unsupported timing parameter reasons. animation->setPlaybackRate(0); - animation->NotifyReady(100); + animation->NotifyReady(AnimationTimeDelta::FromSecondsD(100)); { HistogramTester histogram; ASSERT_TRUE(animation->PreCommit(0, nullptr, true)); @@ -1403,6 +1471,163 @@ TEST_F(AnimationAnimationTestCompositing, InfiniteDurationAnimation) { animation->CheckCanStartAnimationOnCompositor(nullptr)); } +// This test ensures that a background-color animation can start on compositor. +TEST_F(AnimationAnimationTestCompositing, BackgroundColorComposited) { + ScopedCompositeBGColorAnimationForTest composite_bgcolor_animation(true); + SetBodyInnerHTML(R"HTML( + <div id ="target" style="width: 100px; height: 100px"> + </div> + )HTML"); + + Animation* animation = + CreateAnimation(CSSPropertyID::kBackgroundColor, "red", "green"); + + UpdateAllLifecyclePhasesForTest(); + animation->play(); + // A basic condition for an animation to be compositable is that it is set so + // by BackgroundColorPaintWorklet::GetBGColorPaintWorkletParams. + animation->SetCanCompositeBGColorAnim(); + EXPECT_EQ(animation->CheckCanStartAnimationOnCompositor(nullptr), + CompositorAnimations::kNoFailure); +} + +// crbug.com/1149012 +// Regression test to ensure proper restart logic for composited animations on +// relative transforms after a size change. In this test, the transform depends +// on the width and height of the box and a change to either triggers a restart +// of the animation if running. +TEST_F(AnimationAnimationTestCompositing, + RestartCompositedAnimationOnSizeChange) { + // TODO(crbug.com/389359): Remove forced feature enabling once on by + // default. + ScopedCompositeRelativeKeyframesForTest composite_relative_keyframes(true); + SetBodyInnerHTML(R"HTML( + <div id ="target" + style="width: 100px; height: 200px; will-change: transform"> + </div> + )HTML"); + + Animation* animation = CreateAnimation( + CSSPropertyID::kTransform, "translate(100%, 100%)", "translate(0%, 0%)"); + + UpdateAllLifecyclePhasesForTest(); + animation->play(); + KeyframeEffect* keyframe_effect = + DynamicTo<KeyframeEffect>(animation->effect()); + ASSERT_TRUE(keyframe_effect); + + EXPECT_EQ(animation->CheckCanStartAnimationOnCompositor(nullptr), + CompositorAnimations::kNoFailure); + + GetDocument().GetPendingAnimations().Update(nullptr, true); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + + // Kick the animation out of the play-pending state. + animation->setStartTime(CSSNumberish::FromDouble(0)); + + // No size change and animation does not require a restart. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(100, 200)); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + + // Restart animation on a width change. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(200, 200)); + EXPECT_FALSE(animation->HasActiveAnimationsOnCompositor()); + + GetDocument().GetPendingAnimations().Update(nullptr, true); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + + // Restart animation on a height change. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(200, 300)); + EXPECT_FALSE(animation->HasActiveAnimationsOnCompositor()); +} + +// crbug.com/1149012 +// Regression test to ensure proper restart logic for composited animations on +// relative transforms after a size change. In this test, the transform only +// depends on width and a change to the height does not trigger a restart. +TEST_F(AnimationAnimationTestCompositing, + RestartCompositedAnimationOnWidthChange) { + // TODO(crbug.com/389359): Remove forced feature enabling once on by + // default. + ScopedCompositeRelativeKeyframesForTest composite_relative_keyframes(true); + SetBodyInnerHTML(R"HTML( + <div id ="target" + style="width: 100px; height: 200px; will-change: transform"> + </div> + )HTML"); + + animation = CreateAnimation(CSSPropertyID::kTransform, "translateX(100%)", + "translateX(0%)"); + + UpdateAllLifecyclePhasesForTest(); + animation->play(); + KeyframeEffect* keyframe_effect = + DynamicTo<KeyframeEffect>(animation->effect()); + ASSERT_TRUE(keyframe_effect); + + GetDocument().GetPendingAnimations().Update(nullptr, true); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(100, 200)); + animation->setStartTime(CSSNumberish::FromDouble(0)); + + // Transform is not height dependent and a change to the height does not force + // an animation restart. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(100, 300)); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + + // Width change forces a restart. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(200, 300)); + EXPECT_FALSE(animation->HasActiveAnimationsOnCompositor()); +} + +// crbug.com/1149012 +// Regression test to ensure proper restart logic for composited animations on +// relative transforms after a size change. In this test, the transition only +// affects height and a change to the width does not trigger a restart. +TEST_F(AnimationAnimationTestCompositing, + RestartCompositedAnimationOnHeightChange) { + // TODO(crbug.com/389359): Remove forced feature enabling once on by + // default. + ScopedCompositeRelativeKeyframesForTest composite_relative_keyframes(true); + SetBodyInnerHTML(R"HTML( + <div id ="target" + style="width: 100px; height: 200px; will-change: transform"> + </div> + )HTML"); + + animation = CreateAnimation(CSSPropertyID::kTransform, "translateY(100%)", + "translateY(0%)"); + + UpdateAllLifecyclePhasesForTest(); + animation->play(); + KeyframeEffect* keyframe_effect = + DynamicTo<KeyframeEffect>(animation->effect()); + ASSERT_TRUE(keyframe_effect); + + GetDocument().GetPendingAnimations().Update(nullptr, true); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(100, 200)); + animation->setStartTime(CSSNumberish::FromDouble(0)); + + // Transform is not width dependent and a change to the width does not force + // an animation restart. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(300, 200)); + EXPECT_TRUE(animation->HasActiveAnimationsOnCompositor()); + + // Height change forces a restart. + keyframe_effect->UpdateBoxSizeAndCheckTransformAxisAlignment( + FloatSize(300, 400)); + EXPECT_FALSE(animation->HasActiveAnimationsOnCompositor()); +} + TEST_F(AnimationAnimationTestCompositing, ScrollLinkedAnimationCanBeComposited) { ResetWithCompositedAnimation(); @@ -1533,7 +1758,7 @@ TEST_F(AnimationAnimationTestCompositing, UpdateAllLifecyclePhasesForTest(); const double TEST_START_TIME = 10; - scroll_animation->setStartTime(TEST_START_TIME); + scroll_animation->setStartTime(CSSNumberish::FromDouble(TEST_START_TIME)); scroll_animation->play(); EXPECT_EQ(scroll_animation->CheckCanStartAnimationOnCompositor(nullptr), CompositorAnimations::kNoFailure); @@ -1581,25 +1806,25 @@ TEST_F(AnimationAnimationTestNoCompositing, ScrollLinkedAnimationCreation) { Animation::Create(MakeAnimation(), scroll_timeline, exception_state); // Verify start and current times in Idle state. - EXPECT_FALSE(scroll_animation->startTime().has_value()); - EXPECT_FALSE(scroll_animation->currentTime().has_value()); + EXPECT_FALSE(StartTimeIsSet(scroll_animation)); + EXPECT_FALSE(CurrentTimeIsSet(scroll_animation)); scroll_animation->play(); // Verify start and current times in Pending state. - EXPECT_EQ(0, scroll_animation->startTime()); - EXPECT_EQ(20, scroll_animation->currentTime()); + EXPECT_TIME(0, GetStartTimeMs(scroll_animation)); + EXPECT_TIME(20, GetCurrentTimeMs(scroll_animation)); UpdateAllLifecyclePhasesForTest(); // Verify start and current times in Playing state. - EXPECT_EQ(0, scroll_animation->startTime()); - EXPECT_EQ(20, scroll_animation->currentTime()); + EXPECT_TIME(0, GetStartTimeMs(scroll_animation)); + EXPECT_TIME(20, GetCurrentTimeMs(scroll_animation)); // Verify current time after scroll. scrollable_area->SetScrollOffset(ScrollOffset(0, 40), mojom::blink::ScrollType::kProgrammatic); SimulateFrameForScrollAnimations(); - EXPECT_EQ(40, scroll_animation->currentTime()); + EXPECT_TIME(40, GetCurrentTimeMs(scroll_animation)); } // Verifies that finished composited scroll-linked animations restart on @@ -1671,14 +1896,14 @@ TEST_F(AnimationAnimationTestCompositing, // Advances the animation to "finished" state. The composited animation will // be destroyed accordingly. - scroll_animation->setCurrentTime(50000); + scroll_animation->setCurrentTime(CSSNumberish::FromDouble(50000)); EXPECT_EQ(scroll_animation->playState(), "finished"); scroll_animation->Update(kTimingUpdateForAnimationFrame); GetDocument().GetPendingAnimations().Update(nullptr, true); EXPECT_FALSE(scroll_animation->HasActiveAnimationsOnCompositor()); // Restarting the animation should create a new compositor animation. - scroll_animation->setCurrentTime(100); + scroll_animation->setCurrentTime(CSSNumberish::FromDouble(100)); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(scroll_animation->playState(), "running"); scroll_animation->Update(kTimingUpdateForAnimationFrame); @@ -1715,7 +1940,7 @@ TEST_F(AnimationAnimationTestNoCompositing, EXPECT_TRUE(animation->Update(kTimingUpdateForAnimationFrame)); // Asynchronous completion. - animation->setCurrentTime(50000); + animation->setCurrentTime(CSSNumberish::FromDouble(50000)); EXPECT_EQ("finished", animation->playState()); EXPECT_FALSE(animation->Update(kTimingUpdateForAnimationFrame)); } @@ -1735,7 +1960,7 @@ TEST_F(AnimationAnimationTestNoCompositing, EXPECT_TRUE(animation->HasPendingActivity()); // Resolving the finished promise clears the pending activity. - animation->setCurrentTime(50000); + animation->setCurrentTime(CSSNumberish::FromDouble(50000)); EXPECT_EQ("finished", animation->playState()); SimulateMicrotask(); EXPECT_FALSE(animation->Update(kTimingUpdateForAnimationFrame)); @@ -1786,7 +2011,7 @@ TEST_F(AnimationAnimationTestNoCompositing, EXPECT_TRUE(animation->HasPendingActivity()); // Finishing the animation asynchronously clears the pending activity. - animation->setCurrentTime(50000); + animation->setCurrentTime(CSSNumberish::FromDouble(50000)); EXPECT_EQ("finished", animation->playState()); SimulateMicrotask(); EXPECT_FALSE(animation->Update(kTimingUpdateForAnimationFrame)); @@ -2012,4 +2237,62 @@ TEST_F(AnimationAnimationTestCompositing, CompositorAnimations::kTimelineSourceHasInvalidCompositingState); } +TEST_F(AnimationAnimationTestCompositing, ContentVisibleDisplayLockTest) { + animation->cancel(); + RunDocumentLifecycle(); + + SetBodyInnerHTML(R"HTML( + <style> + .container { + content-visibility: auto; + } + @keyframes anim { + from { opacity: 0; } + to { opacity: 1; } + } + #target { + background-color: blue; + width: 50px; + height: 50px; + animation: anim 1s linear alternate infinite; + } + </style> + <div id="outer" class="container"> + <div id="inner" class="container"> + <div id ="target"> + </div> + </div> + </div> + )HTML"); + + RunDocumentLifecycle(); + + Element* outer = GetElementById("outer"); + Element* inner = GetElementById("inner"); + Element* target = GetElementById("target"); + + ElementAnimations* element_animations = target->GetElementAnimations(); + EXPECT_EQ(1u, element_animations->Animations().size()); + + Animation* animation = element_animations->Animations().begin()->key; + ASSERT_TRUE(!!animation); + EXPECT_FALSE(animation->IsInDisplayLockedSubtree()); + + inner->setAttribute(html_names::kStyleAttr, "content-visibility: hidden"); + RunDocumentLifecycle(); + EXPECT_TRUE(animation->IsInDisplayLockedSubtree()); + + inner->setAttribute(html_names::kStyleAttr, "content-visibility: visible"); + RunDocumentLifecycle(); + EXPECT_FALSE(animation->IsInDisplayLockedSubtree()); + + outer->setAttribute(html_names::kStyleAttr, "content-visibility: hidden"); + RunDocumentLifecycle(); + EXPECT_TRUE(animation->IsInDisplayLockedSubtree()); + + // Ensure that the animation has not been canceled even though display locked. + EXPECT_EQ(1u, target->GetElementAnimations()->Animations().size()); + EXPECT_EQ(animation->playState(), "running"); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/animation_time_delta.h b/chromium/third_party/blink/renderer/core/animation/animation_time_delta.h index 9c330f3443b..b7ab9757fcd 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_time_delta.h +++ b/chromium/third_party/blink/renderer/core/animation/animation_time_delta.h @@ -41,6 +41,8 @@ class CORE_EXPORT AnimationTimeDelta { public: constexpr AnimationTimeDelta() : delta_(0) {} + constexpr explicit AnimationTimeDelta(base::TimeDelta time_delta) + : delta_(time_delta.InSecondsF()) {} static AnimationTimeDelta FromSecondsD(double time_s) { DCHECK(!std::isnan(time_s)); @@ -61,6 +63,9 @@ class CORE_EXPORT AnimationTimeDelta { bool is_max() const { return delta_ == std::numeric_limits<double>::infinity(); } + + bool is_inf() const { return std::isinf(delta_); } + bool is_zero() const { return delta_ == 0; } AnimationTimeDelta operator+(AnimationTimeDelta other) const { @@ -72,6 +77,7 @@ class CORE_EXPORT AnimationTimeDelta { AnimationTimeDelta operator-(AnimationTimeDelta other) const { return AnimationTimeDelta(delta_ - other.delta_); } + AnimationTimeDelta operator-() { return AnimationTimeDelta(-delta_); } template <typename T> AnimationTimeDelta operator*(T a) const { return AnimationTimeDelta(delta_ * a); diff --git a/chromium/third_party/blink/renderer/core/animation/animation_timeline.cc b/chromium/third_party/blink/renderer/core/animation/animation_timeline.cc index 3a3859f3f1c..e6a822b50c8 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_timeline.cc +++ b/chromium/third_party/blink/renderer/core/animation/animation_timeline.cc @@ -46,6 +46,12 @@ void AnimationTimeline::currentTime(CSSNumberish& currentTime) { : CSSNumberish(); } +base::Optional<AnimationTimeDelta> AnimationTimeline::CurrentTime() { + base::Optional<base::TimeDelta> result = CurrentPhaseAndTime().time; + return result ? base::make_optional(AnimationTimeDelta(result.value())) + : base::nullopt; +} + base::Optional<double> AnimationTimeline::CurrentTimeMilliseconds() { base::Optional<base::TimeDelta> result = CurrentPhaseAndTime().time; return result ? base::make_optional(result->InMillisecondsF()) diff --git a/chromium/third_party/blink/renderer/core/animation/animation_timeline.h b/chromium/third_party/blink/renderer/core/animation/animation_timeline.h index 6c16a91fd4e..8cc0a6252e8 100644 --- a/chromium/third_party/blink/renderer/core/animation/animation_timeline.h +++ b/chromium/third_party/blink/renderer/core/animation/animation_timeline.h @@ -36,6 +36,7 @@ class CORE_EXPORT AnimationTimeline : public ScriptWrappable { ~AnimationTimeline() override = default; virtual void currentTime(CSSNumberish&); + base::Optional<AnimationTimeDelta> CurrentTime(); base::Optional<double> CurrentTimeMilliseconds(); base::Optional<double> CurrentTimeSeconds(); @@ -48,7 +49,7 @@ class CORE_EXPORT AnimationTimeline : public ScriptWrappable { virtual bool IsScrollTimeline() const { return false; } virtual bool IsCSSScrollTimeline() const { return false; } virtual bool IsActive() const = 0; - virtual double ZeroTimeInSeconds() = 0; + virtual AnimationTimeDelta ZeroTime() = 0; // https://drafts.csswg.org/web-animations/#monotonically-increasing-timeline // A timeline is monotonically increasing if its reported current time is // always greater than or equal than its previously reported current time. diff --git a/chromium/third_party/blink/renderer/core/animation/build.gni b/chromium/third_party/blink/renderer/core/animation/build.gni index 65558126ec5..acf6f02e997 100644 --- a/chromium/third_party/blink/renderer/core/animation/build.gni +++ b/chromium/third_party/blink/renderer/core/animation/build.gni @@ -15,6 +15,8 @@ blink_core_tests_animation = [ "css/css_animations_test.cc", "css/css_scroll_timeline_test.cc", "css/css_transition_data_test.cc", + "css_color_interpolation_type_test.cc", + "css_interpolation_types_map_test.cc", "document_animations_test.cc", "document_timeline_test.cc", "effect_input_test.cc", diff --git a/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc b/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc index 51fb92a1f8c..204c414357d 100644 --- a/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc +++ b/chromium/third_party/blink/renderer/core/animation/compositor_animations.cc @@ -153,6 +153,16 @@ bool HasIncompatibleAnimations(const Element& target_element, return false; } +void DefaultToUnsupportedProperty( + PropertyHandleSet* unsupported_properties, + const PropertyHandle& property, + CompositorAnimations::FailureReasons* reasons) { + (*reasons) |= CompositorAnimations::kUnsupportedCSSProperty; + if (unsupported_properties) { + unsupported_properties->insert(property); + } +} + } // namespace CompositorElementIdNamespace @@ -169,10 +179,11 @@ CompositorAnimations::CompositorElementNamespaceForProperty( return CompositorElementIdNamespace::kPrimaryTransform; case CSSPropertyID::kFilter: return CompositorElementIdNamespace::kEffectFilter; + case CSSPropertyID::kBackgroundColor: case CSSPropertyID::kVariable: - // TODO(crbug.com/883721): Variables should not require the target - // element to have any composited property tree nodes - i.e. should - // not need to check for existence of a property tree node. + // TODO(crbug.com/883721): Variables and background color should not + // require the target element to have any composited property tree nodes - + // i.e. should not need to check for existence of a property tree node. // For now, variable animations target the primary animation target // node - the effect namespace. return CompositorElementIdNamespace::kPrimaryEffect; @@ -272,6 +283,13 @@ CompositorAnimations::CheckCanStartEffectOnCompositor( // Backdrop-filter pixel moving filters do not change the layer bounds // like regular filters do, so they can still be composited. break; + case CSSPropertyID::kBackgroundColor: + if (!RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled() || + !animation_to_add->CanCompositeBGColorAnim()) { + DefaultToUnsupportedProperty(unsupported_properties, property, + &reasons); + } + break; case CSSPropertyID::kVariable: { // Custom properties are supported only in the case of // OffMainThreadCSSPaintEnabled, and even then only for some specific @@ -287,10 +305,8 @@ CompositorAnimations::CheckCanStartEffectOnCompositor( !layout_object->Style()->HasCSSPaintImagesUsingCustomProperty( property.CustomPropertyName(), layout_object->GetDocument())) { - if (unsupported_properties) { - unsupported_properties->insert(property); - } - reasons |= kUnsupportedCSSProperty; + DefaultToUnsupportedProperty(unsupported_properties, property, + &reasons); } // TODO: Add support for keyframes containing different types if (!keyframes.front() || @@ -302,10 +318,8 @@ CompositorAnimations::CheckCanStartEffectOnCompositor( } else { // We skip the rest of the loop in this case for the same reason as // unsupported CSS properties - see below. - if (unsupported_properties) { - unsupported_properties->insert(property); - } - reasons |= kUnsupportedCSSProperty; + DefaultToUnsupportedProperty(unsupported_properties, property, + &reasons); continue; } break; @@ -316,10 +330,8 @@ CompositorAnimations::CheckCanStartEffectOnCompositor( // an unsupported property. // ii. GetCompositorKeyframeValue() will be false so we will // accidentally count this as kInvalidAnimationOrEffect as well. - if (unsupported_properties) { - unsupported_properties->insert(property); - } - reasons |= kUnsupportedCSSProperty; + DefaultToUnsupportedProperty(unsupported_properties, property, + &reasons); continue; } @@ -406,7 +418,7 @@ CompositorAnimations::CheckCanStartElementOnCompositor( (effect && effect->HasDirectCompositingReasons()); } if (!has_direct_compositing_reasons && - To<KeyframeEffectModelBase>(model).HasNonVariableProperty()) { + To<KeyframeEffectModelBase>(model).RequiresPropertyNode()) { reasons |= kTargetHasInvalidCompositingState; } } else { @@ -696,6 +708,8 @@ void CompositorAnimations::GetAnimationOnCompositor( DCHECK(!properties.IsEmpty()); for (const auto& property : properties) { AtomicString custom_property_name = ""; + CompositorPaintWorkletInput::NativePropertyType native_property_type = + CompositorPaintWorkletInput::NativePropertyType::kInvalid; // If the animation duration is infinite, it doesn't make sense to scale // the keyframe offset, so use a scale of 1.0. This is connected to // the known issue of how the Web Animations spec handles infinite @@ -749,6 +763,17 @@ void CompositorAnimations::GetAnimationOnCompositor( curve = std::move(transform_curve); break; } + case CSSPropertyID::kBackgroundColor: { + native_property_type = + CompositorPaintWorkletInput::NativePropertyType::kBackgroundColor; + auto float_curve = std::make_unique<CompositorFloatAnimationCurve>(); + target_property = compositor_target_property::NATIVE_PROPERTY; + AddKeyframesToCurve(*float_curve, values); + float_curve->SetTimingFunction(*timing.timing_function); + float_curve->SetScaledDuration(scale); + curve = std::move(float_curve); + break; + } case CSSPropertyID::kVariable: { DCHECK(RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()); custom_property_name = property.CustomPropertyName(); @@ -776,8 +801,18 @@ void CompositorAnimations::GetAnimationOnCompositor( } DCHECK(curve.get()); - auto keyframe_model = std::make_unique<CompositorKeyframeModel>( - *curve, target_property, 0, group, std::move(custom_property_name)); + std::unique_ptr<CompositorKeyframeModel> keyframe_model; + if (!custom_property_name.IsEmpty()) { + keyframe_model = std::make_unique<CompositorKeyframeModel>( + *curve, target_property, 0, group, std::move(custom_property_name)); + } else if (native_property_type != + CompositorPaintWorkletInput::NativePropertyType::kInvalid) { + keyframe_model = std::make_unique<CompositorKeyframeModel>( + *curve, target_property, 0, group, native_property_type); + } else { + keyframe_model = std::make_unique<CompositorKeyframeModel>( + *curve, target_property, 0, group); + } if (start_time) keyframe_model->SetStartTime(start_time.value()); @@ -834,8 +869,9 @@ CompositorAnimations::CheckCanStartTransformAnimationOnCompositorForSVG( if (layout_object->IsSVGViewportContainer()) { // Nested SVG doesn't support transforms for now. reasons |= kTransformRelatedPropertyCannotBeAcceleratedOnTarget; - } else if (layout_object->IsSVGForeignObject() && - layout_object->StyleRef().EffectiveZoom() != 1) { + } else if (layout_object->StyleRef().EffectiveZoom() != 1) { + // TODO(crbug.com/1186312): Composited transform animation with non-1 + // effective zoom is incorrectly scaled for now. // TODO(crbug.com/1134775): If a foreignObject's effect zoom is not 1, // its transform node contains an additional scale which would be removed // by composited animation. diff --git a/chromium/third_party/blink/renderer/core/animation/compositor_animations.h b/chromium/third_party/blink/renderer/core/animation/compositor_animations.h index ad0abb2462b..c107d8b8fed 100644 --- a/chromium/third_party/blink/renderer/core/animation/compositor_animations.h +++ b/chromium/third_party/blink/renderer/core/animation/compositor_animations.h @@ -47,6 +47,7 @@ class Animation; class CompositorAnimation; class Element; class KeyframeEffectModelBase; +class Node; class PaintArtifactCompositor; class SVGElement; diff --git a/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc b/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc index 69121bf03e4..63147572614 100644 --- a/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/compositor_animations_test.cc @@ -56,7 +56,6 @@ #include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/frame/frame_test_helpers.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" -#include "third_party/blink/renderer/core/frame/web_view_frame_widget.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.h" @@ -183,7 +182,8 @@ class AnimationCompositorAnimationsTest : public PaintTestConfigurations, const Timing& timing, const KeyframeEffectModelBase& effect) { // TODO(crbug.com/725385): Remove once compositor uses InterpolationTypes. - auto style = GetDocument().GetStyleResolver().StyleForElement(element_); + auto style = GetDocument().GetStyleResolver().StyleForElement( + element_, StyleRecalcContext()); effect.SnapshotAllCompositorKeyframesIfNecessary(*element_.Get(), *style, nullptr); return CheckCanStartEffectOnCompositor(timing, *element_.Get(), nullptr, @@ -455,7 +455,8 @@ class AnimationCompositorAnimationsTest : public PaintTestConfigurations, // As the compositor code only understands CompositorKeyframeValues, we must // snapshot the effect to make those available. // TODO(crbug.com/725385): Remove once compositor uses InterpolationTypes. - auto style = GetDocument().GetStyleResolver().StyleForElement(element_); + auto style = GetDocument().GetStyleResolver().StyleForElement( + element_, StyleRecalcContext()); effect.SnapshotAllCompositorKeyframesIfNecessary(*element_.Get(), *style, nullptr); @@ -635,7 +636,8 @@ TEST_P(AnimationCompositorAnimationsTest, SetCustomProperty("--x", "5"); UpdateAllLifecyclePhasesForTest(); - auto style = GetDocument().GetStyleResolver().StyleForElement(element_); + auto style = GetDocument().GetStyleResolver().StyleForElement( + element_, StyleRecalcContext()); EXPECT_TRUE(style->NonInheritedVariables()); EXPECT_TRUE(style->NonInheritedVariables() ->GetData(AtomicString("--foo")) @@ -1334,7 +1336,7 @@ TEST_P(AnimationCompositorAnimationsTest, CanStartEffectOnCompositorBasic) { MakeGarbageCollected<StringKeyframeEffectModel>(non_css_frames_vector); EXPECT_TRUE(CanStartEffectOnCompositor(timing_, *non_css_frames) & CompositorAnimations::kAnimationAffectsNonCSSProperties); - EXPECT_TRUE(non_css_frames->HasNonVariableProperty()); + EXPECT_TRUE(non_css_frames->RequiresPropertyNode()); // NB: Important that non_css_frames_vector goes away and cleans up // before fake_name. } @@ -1737,7 +1739,7 @@ TEST_P(AnimationCompositorAnimationsTest, keyframe_model->TargetProperty()); EXPECT_EQ(keyframe_model->GetCustomPropertyNameForTesting(), property_name.Utf8().data()); - EXPECT_FALSE(effect->HasNonVariableProperty()); + EXPECT_FALSE(effect->RequiresPropertyNode()); } TEST_P(AnimationCompositorAnimationsTest, @@ -2065,8 +2067,7 @@ TEST_P(AnimationCompositorAnimationsTest, CompositedTransformAnimation) { ASSERT_NE(nullptr, cc_transform); EXPECT_TRUE(cc_transform->has_potential_animation); EXPECT_TRUE(cc_transform->is_currently_animating); - EXPECT_EQ(cc::kNotScaled, cc_transform->starting_animation_scale); - EXPECT_EQ(cc::kNotScaled, cc_transform->maximum_animation_scale); + EXPECT_EQ(1.f, cc_transform->maximum_animation_scale); // Make sure the animation is started on the compositor. EXPECT_EQ( @@ -2100,7 +2101,6 @@ TEST_P(AnimationCompositorAnimationsTest, CompositedScaleAnimation) { ASSERT_NE(nullptr, cc_transform); EXPECT_TRUE(cc_transform->has_potential_animation); EXPECT_TRUE(cc_transform->is_currently_animating); - EXPECT_EQ(2.f, cc_transform->starting_animation_scale); EXPECT_EQ(5.f, cc_transform->maximum_animation_scale); // Make sure the animation is started on the compositor. @@ -2242,6 +2242,9 @@ TEST_P(AnimationCompositorAnimationsTest, <use id="use" href="#rect-useref" class="animate"/> <use id="use-offset" href="#rect-useref" x="10" class="animate"/> </svg> + <svg id="svg-zoomed" class="animate" style="zoom: 1.5"> + <rect id="rect-zoomed" class="animate"/> + </svg> )HTML"); auto CanStartAnimation = [&](const char* id) -> bool { @@ -2261,6 +2264,9 @@ TEST_P(AnimationCompositorAnimationsTest, EXPECT_TRUE(CanStartAnimation("use")); EXPECT_FALSE(CanStartAnimation("use-offset")); + EXPECT_FALSE(CanStartAnimation("svg-zoomed")); + EXPECT_FALSE(CanStartAnimation("rect-zoomed")); + To<SVGElement>(GetDocument().getElementById("rect")) ->SetWebAnimatedAttribute( svg_names::kXAttr, diff --git a/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h b/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h index 83be42ab243..7bd0c3fd1b7 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h +++ b/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h @@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value.h" #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc b/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc index d84627765c8..cede2395cd2 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc +++ b/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.cc @@ -34,7 +34,8 @@ static CompositorKeyframeValue* CreateFromTransformProperties( CompositorKeyframeValue* CompositorKeyframeValueFactory::Create( const PropertyHandle& property, - const ComputedStyle& style) { + const ComputedStyle& style, + double offset) { const CSSProperty& css_property = property.GetCSSProperty(); #if DCHECK_IS_ON() // Variables are conditionally interpolable and compositable. @@ -67,6 +68,9 @@ CompositorKeyframeValue* CompositorKeyframeValueFactory::Create( return CreateFromTransformProperties(style.Scale(), style.EffectiveZoom(), nullptr); } + case CSSPropertyID::kBackgroundColor: { + return MakeGarbageCollected<CompositorKeyframeDouble>(offset); + } case CSSPropertyID::kVariable: { if (!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) { return nullptr; diff --git a/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h b/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h index d4e55a9a6b1..17115618768 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h +++ b/chromium/third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h @@ -18,7 +18,8 @@ class CompositorKeyframeValueFactory { public: static CompositorKeyframeValue* Create(const PropertyHandle&, - const ComputedStyle&); + const ComputedStyle&, + double offset); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_animation.cc b/chromium/third_party/blink/renderer/core/animation/css/css_animation.cc index e2e7beb7993..9e637788e15 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_animation.cc +++ b/chromium/third_party/blink/renderer/core/animation/css/css_animation.cc @@ -60,7 +60,7 @@ void CSSAnimation::setTimeline(AnimationTimeline* timeline) { ignore_css_timeline_ = true; } -void CSSAnimation::setStartTime(base::Optional<double> start_time_ms, +void CSSAnimation::setStartTime(CSSNumberish start_time_ms, ExceptionState& exception_state) { PlayStateTransitionScope scope(*this); Animation::setStartTime(start_time_ms, exception_state); diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_animation.h b/chromium/third_party/blink/renderer/core/animation/css/css_animation.h index 745f14f4876..32bfc8a162c 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_animation.h +++ b/chromium/third_party/blink/renderer/core/animation/css/css_animation.h @@ -51,7 +51,7 @@ class CORE_EXPORT CSSAnimation : public Animation { void play(ExceptionState& = ASSERT_NO_EXCEPTION) override; void reverse(ExceptionState& = ASSERT_NO_EXCEPTION) override; void setTimeline(AnimationTimeline*) override; - void setStartTime(base::Optional<double>, ExceptionState&) override; + void setStartTime(CSSNumberish, ExceptionState&) override; // When set, subsequent changes to animation-play-state no longer affect the // play state. diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_animations.cc b/chromium/third_party/blink/renderer/core/animation/css/css_animations.cc index 601832605c7..18dcabd7910 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/chromium/third_party/blink/renderer/core/animation/css/css_animations.cc @@ -434,11 +434,11 @@ AnimationTimeDelta IterationElapsedTime(const AnimationEffect& effect, CSSScrollTimeline* CreateCSSScrollTimeline( Element* element, - const CSSScrollTimeline::Options& options) { + CSSScrollTimeline::Options&& options) { if (!options.IsValid()) return nullptr; - auto* scroll_timeline = - MakeGarbageCollected<CSSScrollTimeline>(&element->GetDocument(), options); + auto* scroll_timeline = MakeGarbageCollected<CSSScrollTimeline>( + &element->GetDocument(), std::move(options)); // It's is not allowed for a style resolve to create timelines that // needs timing updates (i.e. AnimationTimeline::NeedsAnimationTimingUpdate() // must return false). Servicing animations after creation preserves this @@ -486,7 +486,7 @@ AnimationTimeline* ComputeTimeline(Element* element, if (timeline->Matches(options)) return existing_timeline; } - if (auto* timeline = CreateCSSScrollTimeline(element, options)) + if (auto* timeline = CreateCSSScrollTimeline(element, std::move(options))) return timeline; } return nullptr; @@ -760,7 +760,7 @@ void CSSAnimations::CalculateAnimationUpdate(CSSAnimationUpdate& update, DCHECK(!is_animation_style_change); base::Optional<TimelinePhase> inherited_phase; - base::Optional<double> inherited_time; + base::Optional<AnimationTimeDelta> inherited_time; if (timeline) { inherited_phase = base::make_optional(timeline->Phase()); @@ -770,7 +770,7 @@ void CSSAnimations::CalculateAnimationUpdate(CSSAnimationUpdate& update, ((timeline != existing_animation->Timeline()) || animation->ResetsCurrentTimeOnResume())) { if (!timeline->IsMonotonicallyIncreasing()) - inherited_time = timeline->CurrentTimeSeconds(); + inherited_time = timeline->CurrentTime(); } } @@ -792,13 +792,13 @@ void CSSAnimations::CalculateAnimationUpdate(CSSAnimationUpdate& update, ComputeTimeline(&element, timeline_name, scroll_timeline_rule, nullptr /* existing_timeline */); base::Optional<TimelinePhase> inherited_phase; - base::Optional<double> inherited_time; + base::Optional<AnimationTimeDelta> inherited_time; if (timeline) { if (timeline->IsMonotonicallyIncreasing()) { - inherited_time = 0; + inherited_time = AnimationTimeDelta(); } else { inherited_phase = base::make_optional(timeline->Phase()); - inherited_time = timeline->CurrentTimeSeconds(); + inherited_time = timeline->CurrentTime(); } } update.StartAnimation( @@ -1039,8 +1039,9 @@ void CSSAnimations::MaybeApplyPendingUpdate(Element* element) { // Set the current time as the start time for retargeted transitions if (retargeted_compositor_transitions.Contains(property)) { - animation->setStartTime( - element->GetDocument().Timeline().CurrentTimeMilliseconds()); + CSSNumberish current_time; + element->GetDocument().Timeline().currentTime(current_time); + animation->setStartTime(current_time); } animation->Update(kTimingUpdateOnDemand); running_transition->animation = animation; @@ -1239,9 +1240,9 @@ void CSSAnimations::CalculateTransitionUpdateForProperty( if (property.GetCSSProperty().IsCompositableProperty()) { CompositorKeyframeValue* from = CompositorKeyframeValueFactory::Create( - property, *state.before_change_style); - CompositorKeyframeValue* to = - CompositorKeyframeValueFactory::Create(property, state.style); + property, *state.before_change_style, start_keyframe->Offset().value()); + CompositorKeyframeValue* to = CompositorKeyframeValueFactory::Create( + property, state.style, end_keyframe->Offset().value()); start_keyframe->SetCompositorValue(from); end_keyframe->SetCompositorValue(to); } @@ -1253,8 +1254,8 @@ void CSSAnimations::CalculateTransitionUpdateForProperty( state.update.StartTransition( property, state.before_change_style, state.cloned_style, reversing_adjusted_start_value, reversing_shortening_factor, - *MakeGarbageCollected<InertEffect>(model, timing, false, 0, - base::nullopt)); + *MakeGarbageCollected<InertEffect>(model, timing, false, + AnimationTimeDelta(), base::nullopt)); DCHECK(!state.animating_element->GetElementAnimations() || !state.animating_element->GetElementAnimations() ->IsAnimationStyleChange()); @@ -1288,7 +1289,7 @@ void CSSAnimations::CalculateTransitionUpdateForStandardProperty( } CSSPropertyID resolved_id = - resolveCSSPropertyID(transition_property.unresolved_property); + ResolveCSSPropertyID(transition_property.unresolved_property); bool animate_all = resolved_id == CSSPropertyID::kAll; const StylePropertyShorthand& property_list = animate_all ? PropertiesForTransitionAll() @@ -1299,7 +1300,7 @@ void CSSAnimations::CalculateTransitionUpdateForStandardProperty( CSSPropertyID longhand_id = property_list.length() ? property_list.properties()[i]->PropertyID() : resolved_id; - DCHECK_GE(longhand_id, firstCSSProperty); + DCHECK_GE(longhand_id, kFirstCSSProperty); const CSSProperty& property = CSSProperty::Get(longhand_id) .ResolveDirectionAwareProperty(style.Direction(), @@ -1425,19 +1426,27 @@ scoped_refptr<const ComputedStyle> CSSAnimations::CalculateBeforeChangeStyle( a, b, Animation::CompareAnimationsOrdering::kPointerOrder); }); - // Sample animations and add to the interpolations map. + // Sample animations and add to the interpolatzions map. for (Animation* animation : animations) { - base::Optional<double> current_time = animation->currentTime(); - if (!current_time) + CSSNumberish current_time_numberish; + animation->currentTime(current_time_numberish); + if (current_time_numberish.IsNull()) continue; + // CSSNumericValue is not yet supported, verify that it is not used + DCHECK(!current_time_numberish.IsCSSNumericValue()); + + base::Optional<AnimationTimeDelta> current_time = + AnimationTimeDelta::FromMillisecondsD( + current_time_numberish.GetAsDouble()); + auto* effect = DynamicTo<KeyframeEffect>(animation->effect()); if (!effect) continue; auto* inert_animation_for_sampling = MakeGarbageCollected<InertEffect>( - effect->Model(), effect->SpecifiedTiming(), false, - current_time.value() / 1000, base::nullopt); + effect->Model(), effect->SpecifiedTiming(), false, current_time, + base::nullopt); HeapVector<Member<Interpolation>> sample; inert_animation_for_sampling->Sample(sample); @@ -1692,10 +1701,9 @@ void CSSAnimations::AnimationEventDelegate::OnEventCondition( previous_phase_ != Timing::kPhaseAfter) { // TODO(crbug.com/1059968): Determine if animation direction or playback // rate factor into the calculation of the elapsed time. - double cancel_time = animation_node.GetCancelTime(); + AnimationTimeDelta cancel_time = animation_node.GetCancelTime(); MaybeDispatch(Document::kAnimationCancelListener, - event_type_names::kAnimationcancel, - AnimationTimeDelta::FromSecondsD(cancel_time)); + event_type_names::kAnimationcancel, cancel_time); } if (!phase_change && current_phase == Timing::kPhaseActive && diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_animations_test.cc b/chromium/third_party/blink/renderer/core/animation/css/css_animations_test.cc index 0857a0e4daa..9982f48ab31 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_animations_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/css/css_animations_test.cc @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/animation/animation.h" #include "third_party/blink/renderer/core/animation/document_timeline.h" #include "third_party/blink/renderer/core/animation/element_animations.h" +#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" @@ -38,7 +39,7 @@ class CSSAnimationsTest : public RenderingTest { SetUpAnimationClockForTesting(); // Advance timer to document time. platform()->AdvanceClockSeconds( - GetDocument().Timeline().ZeroTime().since_origin().InSecondsF()); + GetDocument().Timeline().ZeroTime().InSecondsF()); } void TearDown() override { @@ -263,8 +264,9 @@ class CSSAnimationsCompositorSyncTest : public CSSAnimationsTest { void VerifyCompositorOpacity(double expected_value) { cc::KeyframeModel* keyframe_model = GetCompositorKeyframeForOpacity(); base::TimeDelta iteration_time = CompositorIterationTime(); - const cc::FloatAnimationCurve* opacity_curve = - keyframe_model->curve()->ToFloatAnimationCurve(); + const gfx::FloatAnimationCurve* opacity_curve = + gfx::FloatAnimationCurve::ToFloatAnimationCurve( + keyframe_model->curve()); EXPECT_NEAR(expected_value, opacity_curve->GetValue(iteration_time), kTolerance); } @@ -347,15 +349,21 @@ TEST_F(CSSAnimationsCompositorSyncTest, SetStartTime) { Animation* animation = GetAnimation(); int compositor_group = animation->CompositorGroup(); + CSSNumberish start_time, current_time; + animation->startTime(start_time); + animation->currentTime(current_time); + // Partially rewind the animation via setStartTime. - double new_start_time = - animation->startTime().value() + animation->currentTime().value() / 2; + CSSNumberish new_start_time = CSSNumberish::FromDouble( + start_time.GetAsDouble() + (current_time.GetAsDouble() / 2)); + animation->setStartTime(new_start_time, ASSERT_NO_EXCEPTION); UpdateAllLifecyclePhasesForTest(); // Verify blink updates. - EXPECT_NEAR(250, animation->currentTime().value(), - kTimeToleranceMilliseconds); + animation->currentTime(current_time); + EXPECT_TRUE(current_time.IsDouble()); + EXPECT_NEAR(250, current_time.GetAsDouble(), kTimeToleranceMilliseconds); EXPECT_NEAR(0.75, element_->GetComputedStyle()->Opacity(), kTolerance); // Compositor animation needs to restart and will have a new compositor group. @@ -386,12 +394,14 @@ TEST_F(CSSAnimationsCompositorSyncTest, SetCurrentTime) { int compositor_group = animation->CompositorGroup(); // Advance current time. - animation->setCurrentTime(750, ASSERT_NO_EXCEPTION); + animation->setCurrentTime(CSSNumberish::FromDouble(750), ASSERT_NO_EXCEPTION); UpdateAllLifecyclePhasesForTest(); // Verify blink updates. - EXPECT_NEAR(750, animation->currentTime().value(), - kTimeToleranceMilliseconds); + CSSNumberish current_time; + animation->currentTime(current_time); + EXPECT_TRUE(current_time.IsDouble()); + EXPECT_NEAR(750, current_time.GetAsDouble(), kTimeToleranceMilliseconds); EXPECT_NEAR(0.25, element_->GetComputedStyle()->Opacity(), kTolerance); // Compositor animation needs to restart and will have a new compositor group. diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc b/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc index 179646a550c..d63409c1c28 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc +++ b/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc @@ -82,8 +82,10 @@ double ComputeElementOffsetThreshold(const CSSValue* value) { ScrollTimelineElementBasedOffset* ComputeElementBasedOffset( Document& document, const cssvalue::CSSElementOffsetValue* value) { - auto* offset = MakeGarbageCollected<ScrollTimelineElementBasedOffset>(); - offset->setTarget(ComputeElementOffsetTarget(document, value->Target())); + auto* offset = ScrollTimelineElementBasedOffset::Create(); + Element* target = ComputeElementOffsetTarget(document, value->Target()); + if (target) + offset->setTarget(target); offset->setEdge(ComputeElementOffsetEdge(value->Edge())); offset->setThreshold(ComputeElementOffsetThreshold(value->Threshold())); return offset; @@ -121,23 +123,22 @@ ScrollTimelineOffset* ComputeScrollOffset(Document& document, return MakeGarbageCollected<ScrollTimelineOffset>(); } -HeapVector<Member<ScrollTimelineOffset>>* ComputeScrollOffsets( +HeapVector<Member<ScrollTimelineOffset>> ComputeScrollOffsets( Document& document, const CSSValue* start, const CSSValue* end) { - auto* offsets = - MakeGarbageCollected<HeapVector<Member<ScrollTimelineOffset>>>(); + HeapVector<Member<ScrollTimelineOffset>> offsets; - bool start_is_auto = IsAuto(start) || !start; - bool end_is_auto = IsAuto(end) || !end; + const bool start_is_auto = !start || IsAuto(start); + const bool end_is_auto = !end || IsAuto(end); // TODO(crbug.com/1094014): scroll_offsets will replace start and end // offsets once spec decision on multiple scroll offsets is finalized. // https://github.com/w3c/csswg-drafts/issues/4912 if (!start_is_auto) - offsets->push_back(ComputeScrollOffset(document, start)); + offsets.push_back(ComputeScrollOffset(document, start)); if (!end_is_auto || !start_is_auto) - offsets->push_back(ComputeScrollOffset(document, end)); + offsets.push_back(ComputeScrollOffset(document, end)); return offsets; } @@ -212,11 +213,11 @@ CSSScrollTimeline::Options::Options(Element* element, time_range_(ComputeTimeRange(rule.GetTimeRange())), rule_(&rule) {} -CSSScrollTimeline::CSSScrollTimeline(Document* document, const Options& options) +CSSScrollTimeline::CSSScrollTimeline(Document* document, Options&& options) : ScrollTimeline(document, options.source_, options.direction_, - options.offsets_, + std::move(options.offsets_), *options.time_range_), rule_(options.rule_) { DCHECK(options.IsValid()); @@ -229,10 +230,9 @@ const AtomicString& CSSScrollTimeline::Name() const { } bool CSSScrollTimeline::Matches(const Options& options) const { - DCHECK(options.offsets_); return (scrollSource() == options.source_) && (GetOrientation() == options.direction_) && - (ScrollOffsetsEqual(*options.offsets_)) && + (ScrollOffsetsEqual(options.offsets_)) && (GetTimeRange() == options.time_range_) && (rule_ == options.rule_); } diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h b/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h index c06419f378e..ef6811a5c33 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h +++ b/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h @@ -33,12 +33,12 @@ class CORE_EXPORT CSSScrollTimeline : public ScrollTimeline { Element* source_; ScrollTimeline::ScrollDirection direction_; - HeapVector<Member<ScrollTimelineOffset>>* offsets_; + HeapVector<Member<ScrollTimelineOffset>> offsets_; base::Optional<double> time_range_; StyleRuleScrollTimeline* rule_; }; - CSSScrollTimeline(Document*, const Options&); + CSSScrollTimeline(Document*, Options&&); const AtomicString& Name() const; diff --git a/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc b/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc index a693940dd30..3e6b652a202 100644 --- a/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc @@ -4,10 +4,16 @@ #include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h" +#include "third_party/blink/renderer/core/animation/document_animations.h" #include "third_party/blink/renderer/core/dom/id_target_observer.h" #include "third_party/blink/renderer/core/dom/id_target_observer_registry.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" +#include "third_party/blink/renderer/core/html/html_div_element.h" #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/html/html_style_element.h" +#include "third_party/blink/renderer/core/resize_observer/resize_observer.h" +#include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h" +#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/platform/heap/thread_state.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" @@ -22,6 +28,10 @@ class CSSScrollTimelineTest : public PageTestBase, bool HasObservers(const AtomicString& id) { return GetDocument().GetIdTargetObserverRegistry().HasObservers(id); } + + DocumentAnimations& GetDocumentAnimations() const { + return GetDocument().GetDocumentAnimations(); + } }; TEST_F(CSSScrollTimelineTest, IdObserverElementRemoval) { @@ -217,4 +227,130 @@ TEST_F(CSSScrollTimelineTest, SharedTimelines) { EXPECT_NE(animations2[2]->timeline(), animations1[1]->timeline()); } +TEST_F(CSSScrollTimelineTest, MultipleLifecyclePasses) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes anim { + from { color: green; } + to { color: green; } + } + @scroll-timeline timeline { + source: selector(#scroller); + time-range: 10s; + start: auto; + end: auto; + } + #scroller { + height: 100px; + overflow: scroll; + } + #scroller > div { + height: 200px; + } + #element { + color: red; + animation: anim 10s timeline; + } + </style> + <div id=element></div> + <div id=scroller> + <div id=contents></div> + </div> + )HTML"); + + Element* element = GetDocument().getElementById("element"); + ASSERT_TRUE(element); + + // According to the rules of the spec [1], the timeline is now inactive, + // because #scroller did not have a layout box at the time style recalc + // for #element happened. + // + // However, we do an additional style/layout pass if we detect new + // CSSScrollTimelines in this situation, hence we ultimately do expect + // the animation to apply [2]. + // + // See also DocumentAnimations::ValidateTimelines. + // + // [1] https://drafts.csswg.org/scroll-animations-1/#avoiding-cycles + // [2] https://github.com/w3c/csswg-drafts/issues/5261 + EXPECT_EQ(MakeRGB(0, 128, 0), + element->GetComputedStyle()->VisitedDependentColor( + GetCSSPropertyColor())); +} + +namespace { + +class AnimationTriggeringDelegate : public ResizeObserver::Delegate { + public: + explicit AnimationTriggeringDelegate(Element* element) : element_(element) {} + + void OnResize( + const HeapVector<Member<ResizeObserverEntry>>& entries) override { + element_->setAttribute(blink::html_names::kClassAttr, "animate"); + } + + void Trace(Visitor* visitor) const override { + ResizeObserver::Delegate::Trace(visitor); + visitor->Trace(element_); + } + + private: + Member<Element> element_; +}; + +} // namespace + +TEST_F(CSSScrollTimelineTest, ResizeObserverTriggeredTimelines) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes anim { + from { width: 100px; } + to { width: 100px; } + } + @scroll-timeline timeline { + source: selector(#scroller); + time-range: 10s; + } + #scroller { + height: 100px; + overflow: scroll; + } + #scroller > div { + height: 200px; + } + #element { + width: 1px; + } + #element.animate { + animation: anim 10s timeline; + } + </style> + <div id=main></div> + )HTML"); + + ASSERT_TRUE( + GetDocumentAnimations().GetUnvalidatedTimelinesForTesting().IsEmpty()); + + Element* element = MakeGarbageCollected<HTMLDivElement>(GetDocument()); + element->setAttribute(blink::html_names::kIdAttr, "element"); + + Element* scroller = MakeGarbageCollected<HTMLDivElement>(GetDocument()); + scroller->setAttribute(blink::html_names::kIdAttr, "scroller"); + scroller->AppendChild(MakeGarbageCollected<HTMLDivElement>(GetDocument())); + + Element* main = GetDocument().getElementById("main"); + ASSERT_TRUE(main); + main->AppendChild(element); + main->AppendChild(scroller); + + auto* delegate = MakeGarbageCollected<AnimationTriggeringDelegate>(element); + ResizeObserver* observer = + ResizeObserver::Create(GetDocument().domWindow(), delegate); + observer->observe(element); + + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(1u, + GetDocumentAnimations().GetUnvalidatedTimelinesForTesting().size()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc new file mode 100644 index 00000000000..d0552b1a8d7 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc @@ -0,0 +1,164 @@ +// Copyright 2021 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/css_aspect_ratio_interpolation_type.h" + +#include <memory> +#include <utility> + +#include "base/memory/ptr_util.h" +#include "third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h" +#include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h" +#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" +#include "third_party/blink/renderer/core/style/computed_style.h" +#include "third_party/blink/renderer/core/style/style_aspect_ratio.h" + +namespace blink { + +class CSSAspectRatioNonInterpolableValue final : public NonInterpolableValue { + public: + ~CSSAspectRatioNonInterpolableValue() final = default; + + static scoped_refptr<CSSAspectRatioNonInterpolableValue> Create( + StyleAspectRatio aspect_ratio) { + return base::AdoptRef( + new CSSAspectRatioNonInterpolableValue(aspect_ratio.GetType())); + } + + EAspectRatioType GetAspectRatioType() const { return type_; } + + bool IsCompatibleWith(const CSSAspectRatioNonInterpolableValue& other) const { + if (GetAspectRatioType() == EAspectRatioType::kAuto || + GetAspectRatioType() != other.GetAspectRatioType()) + return false; + return true; + } + + DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); + + private: + explicit CSSAspectRatioNonInterpolableValue(EAspectRatioType type) + : type_(type) {} + + EAspectRatioType type_; +}; + +DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSAspectRatioNonInterpolableValue); +template <> +struct DowncastTraits<CSSAspectRatioNonInterpolableValue> { + static bool AllowFrom(const NonInterpolableValue* value) { + return value && AllowFrom(*value); + } + static bool AllowFrom(const NonInterpolableValue& value) { + return value.GetType() == CSSAspectRatioNonInterpolableValue::static_type_; + } +}; + +class InheritedAspectRatioChecker + : public CSSInterpolationType::CSSConversionChecker { + public: + explicit InheritedAspectRatioChecker(StyleAspectRatio aspect_ratio) + : aspect_ratio_(aspect_ratio) {} + + private: + bool IsValid(const StyleResolverState& state, + const InterpolationValue& underlying) const final { + return state.ParentStyle()->AspectRatio() == aspect_ratio_; + } + + const StyleAspectRatio aspect_ratio_; +}; + +std::unique_ptr<InterpolableValue> +CSSAspectRatioInterpolationType::CreateInterpolableAspectRatio( + const StyleAspectRatio& aspect_ratio) { + std::unique_ptr<InterpolableAspectRatio> result = + InterpolableAspectRatio::MaybeCreate(aspect_ratio); + return std::move(result); +} + +PairwiseInterpolationValue CSSAspectRatioInterpolationType::MaybeMergeSingles( + InterpolationValue&& start, + InterpolationValue&& end) const { + if (!To<CSSAspectRatioNonInterpolableValue>(*start.non_interpolable_value) + .IsCompatibleWith(To<CSSAspectRatioNonInterpolableValue>( + *end.non_interpolable_value))) { + return nullptr; + } + return PairwiseInterpolationValue(std::move(start.interpolable_value), + std::move(end.interpolable_value), + std::move(start.non_interpolable_value)); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertNeutral( + const InterpolationValue& underlying, + ConversionCheckers& conversion_checkers) const { + return InterpolationValue(underlying.interpolable_value->CloneAndZero(), + underlying.non_interpolable_value); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertInitial( + const StyleResolverState&, + ConversionCheckers& conversion_checkers) const { + StyleAspectRatio initial_ratio = ComputedStyle::InitialStyle().AspectRatio(); + return InterpolationValue( + CreateInterpolableAspectRatio(initial_ratio), + CSSAspectRatioNonInterpolableValue::Create(initial_ratio)); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertInherit( + const StyleResolverState& state, + ConversionCheckers& conversion_checkers) const { + if (!state.ParentStyle()) + return nullptr; + + StyleAspectRatio inherited_aspect_ratio = state.ParentStyle()->AspectRatio(); + conversion_checkers.push_back( + std::make_unique<InheritedAspectRatioChecker>(inherited_aspect_ratio)); + if (inherited_aspect_ratio.IsAuto()) + return nullptr; + + return InterpolationValue( + CreateInterpolableAspectRatio(inherited_aspect_ratio), + CSSAspectRatioNonInterpolableValue::Create(inherited_aspect_ratio)); +} + +InterpolationValue +CSSAspectRatioInterpolationType::MaybeConvertStandardPropertyUnderlyingValue( + const ComputedStyle& style) const { + return InterpolationValue( + CreateInterpolableAspectRatio(style.AspectRatio()), + CSSAspectRatioNonInterpolableValue::Create(style.AspectRatio())); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertValue( + const CSSValue& value, + const StyleResolverState* state, + ConversionCheckers&) const { + StyleAspectRatio ratio = + StyleBuilderConverter::ConvertAspectRatio(*state, value); + return InterpolationValue(CreateInterpolableAspectRatio(ratio), + CSSAspectRatioNonInterpolableValue::Create(ratio)); +} + +void CSSAspectRatioInterpolationType::ApplyStandardPropertyValue( + const InterpolableValue& interpolable_value, + const NonInterpolableValue* non_interpolable_value, + StyleResolverState& state) const { + const auto& aspect_ratio = To<InterpolableAspectRatio>(interpolable_value); + state.Style()->SetAspectRatio(StyleAspectRatio( + To<CSSAspectRatioNonInterpolableValue>(non_interpolable_value) + ->GetAspectRatioType(), + aspect_ratio.GetRatio())); +} +void CSSAspectRatioInterpolationType::Composite( + UnderlyingValueOwner& underlying_value_owner, + double underlying_fraction, + const InterpolationValue& value, + double interpolation_fraction) const { + underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd( + underlying_fraction, *value.interpolable_value); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h b/chromium/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h new file mode 100644 index 00000000000..93d854058f7 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h @@ -0,0 +1,53 @@ +// Copyright 2021 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_ASPECT_RATIO_INTERPOLATION_TYPE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_ASPECT_RATIO_INTERPOLATION_TYPE_H_ + +#include "third_party/blink/renderer/core/animation/css_interpolation_type.h" +#include "third_party/blink/renderer/core/css_value_keywords.h" + +namespace blink { + +class StyleAspectRatio; + +class CSSAspectRatioInterpolationType : public CSSInterpolationType { + public: + explicit CSSAspectRatioInterpolationType(PropertyHandle property) + : CSSInterpolationType(property) { + DCHECK_EQ(CSSPropertyID::kAspectRatio, + property.GetCSSProperty().PropertyID()); + } + + InterpolationValue MaybeConvertStandardPropertyUnderlyingValue( + const ComputedStyle&) const final; + PairwiseInterpolationValue MaybeMergeSingles( + InterpolationValue&& start, + InterpolationValue&& end) const final; + void ApplyStandardPropertyValue(const InterpolableValue&, + const NonInterpolableValue*, + StyleResolverState&) const final; + void Composite(UnderlyingValueOwner&, + double underlying_fraction, + const InterpolationValue&, + double interpolation_fraction) const final; + + static std::unique_ptr<InterpolableValue> CreateInterpolableAspectRatio( + const StyleAspectRatio&); + + private: + InterpolationValue MaybeConvertNeutral(const InterpolationValue& underlying, + ConversionCheckers&) const final; + InterpolationValue MaybeConvertInitial(const StyleResolverState&, + ConversionCheckers&) const final; + InterpolationValue MaybeConvertInherit(const StyleResolverState&, + ConversionCheckers&) const final; + InterpolationValue MaybeConvertValue(const CSSValue&, + const StyleResolverState*, + ConversionCheckers&) const final; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_ASPECT_RATIO_INTERPOLATION_TYPE_H_ diff --git a/chromium/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc index 1bb38aa0905..57b3fe7781d 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc @@ -34,8 +34,6 @@ const BasicShape* GetBasicShape(const CSSProperty& property, return nullptr; return style.ShapeOutside()->Shape(); case CSSPropertyID::kClipPath: { - if (!style.ClipPath()) - return nullptr; auto* clip_path_operation = DynamicTo<ShapeClipPathOperation>(style.ClipPath()); if (!clip_path_operation) diff --git a/chromium/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc index 1a816361073..09adb2a97fb 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc @@ -88,7 +88,7 @@ class InheritedClipChecker : public CSSInterpolationType::CSSConversionChecker { const Vector<Length> inherited_length_list_; }; -class CSSClipNonInterpolableValue : public NonInterpolableValue { +class CSSClipNonInterpolableValue final : public NonInterpolableValue { public: ~CSSClipNonInterpolableValue() final = default; @@ -121,7 +121,7 @@ struct DowncastTraits<CSSClipNonInterpolableValue> { } }; -class UnderlyingAutosChecker +class UnderlyingAutosChecker final : public CSSInterpolationType::CSSConversionChecker { public: explicit UnderlyingAutosChecker(const ClipAutos& underlying_autos) diff --git a/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc index 7276cf1c59f..5f2a4f6b362 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc @@ -68,7 +68,9 @@ CSSColorInterpolationType::CreateInterpolableColor(CSSValueID keyword) { case CSSValueID::kInternalQuirkInherit: return CreateInterpolableColorForIndex(kQuirkInherit); case CSSValueID::kWebkitFocusRingColor: - return CreateInterpolableColor(LayoutTheme::GetTheme().FocusRingColor()); + // TODO(crbug.com/929098) Need to pass an appropriate color scheme here. + return CreateInterpolableColor(LayoutTheme::GetTheme().FocusRingColor( + ComputedStyle::InitialStyle().UsedColorScheme())); default: DCHECK(StyleColor::IsColorKeyword(keyword)); // TODO(crbug.com/929098) Need to pass an appropriate color scheme here. @@ -96,6 +98,20 @@ CSSColorInterpolationType::MaybeCreateInterpolableColor(const CSSValue& value) { return CreateInterpolableColor(identifier_value->GetValueID()); } +Color CSSColorInterpolationType::GetRGBA(const InterpolableValue& value) { + const InterpolableList& list = To<InterpolableList>(value); + DCHECK_GE(list.length(), kAlpha); + double color[kAlpha + 1]; + for (unsigned i = kRed; i <= kAlpha; i++) { + const InterpolableValue& current_value = *(list.Get(i)); + color[i] = To<InterpolableNumber>(current_value).Value(); + } + return Color(MakeRGBA(std::round(color[kRed] / color[kAlpha]), + std::round(color[kGreen] / color[kAlpha]), + std::round(color[kBlue] / color[kAlpha]), + color[kAlpha])); +} + static void AddPremultipliedColor(double& red, double& green, double& blue, @@ -141,8 +157,9 @@ Color CSSColorInterpolationType::ResolveInterpolableColor( *state.Style()) .Access(); } - AddPremultipliedColor(red, green, blue, alpha, currentcolor_fraction, - current_style_color.GetColor()); + AddPremultipliedColor( + red, green, blue, alpha, currentcolor_fraction, + current_style_color.Resolve(Color(), state.Style()->UsedColorScheme())); } const TextLinkColors& colors = state.GetDocument().GetTextLinkColors(); if (double webkit_activelink_fraction = diff --git a/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.h b/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.h index 7708b1d428b..538e1d9171e 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.h +++ b/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type.h @@ -15,7 +15,7 @@ namespace blink { class StyleColor; struct OptionalStyleColor; -class CSSColorInterpolationType : public CSSInterpolationType { +class CORE_EXPORT CSSColorInterpolationType : public CSSInterpolationType { public: CSSColorInterpolationType(PropertyHandle property, const PropertyRegistration* registration = nullptr) @@ -44,6 +44,10 @@ class CSSColorInterpolationType : public CSSInterpolationType { bool is_visited = false, bool is_text_decoration = false); + // Extract color info from a InterpolableValue-result, the input value must be + // a InterpolableList. + static Color GetRGBA(const InterpolableValue&); + private: InterpolationValue MaybeConvertNeutral(const InterpolationValue& underlying, ConversionCheckers&) const final; diff --git a/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type_test.cc b/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type_test.cc new file mode 100644 index 00000000000..3cc0c4ee5f2 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/css_color_interpolation_type_test.cc @@ -0,0 +1,32 @@ +// Copyright 2020 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/css_color_interpolation_type.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/graphics/color.h" + +namespace blink { + +TEST(CSSColorInterpolationTypeTest, GetRGBA1) { + Color color(230, 120, 0, 255); + EXPECT_EQ(color, + CSSColorInterpolationType::GetRGBA( + *CSSColorInterpolationType::CreateInterpolableColor(color))); +} + +TEST(CSSColorInterpolationTypeTest, GetRGBA2) { + Color color(100, 190, 0, 1); + EXPECT_EQ(color, + CSSColorInterpolationType::GetRGBA( + *CSSColorInterpolationType::CreateInterpolableColor(color))); +} + +TEST(CSSColorInterpolationTypeTest, GetRGBA3) { + Color color(35, 140, 10, 10); + EXPECT_EQ(color, + CSSColorInterpolationType::GetRGBA( + *CSSColorInterpolationType::CreateInterpolableColor(color))); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/css_default_interpolation_type.h b/chromium/third_party/blink/renderer/core/animation/css_default_interpolation_type.h index 501fc9c4f09..ccd7a7045d0 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_default_interpolation_type.h +++ b/chromium/third_party/blink/renderer/core/animation/css_default_interpolation_type.h @@ -11,7 +11,8 @@ namespace blink { -class CORE_EXPORT CSSDefaultNonInterpolableValue : public NonInterpolableValue { +class CORE_EXPORT CSSDefaultNonInterpolableValue final + : public NonInterpolableValue { public: ~CSSDefaultNonInterpolableValue() final = default; diff --git a/chromium/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc index bee89cee486..c5d8b93ccfd 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc @@ -16,7 +16,7 @@ namespace blink { -class CSSFontVariationSettingsNonInterpolableValue +class CSSFontVariationSettingsNonInterpolableValue final : public NonInterpolableValue { public: ~CSSFontVariationSettingsNonInterpolableValue() final = default; @@ -65,7 +65,8 @@ static bool TagsMatch(const NonInterpolableValue& a, return GetTags(a) == GetTags(b); } -class UnderlyingTagsChecker : public InterpolationType::ConversionChecker { +class UnderlyingTagsChecker final + : public InterpolationType::ConversionChecker { public: explicit UnderlyingTagsChecker(const Vector<uint32_t>& tags) : tags_(tags) {} ~UnderlyingTagsChecker() final = default; @@ -79,7 +80,7 @@ class UnderlyingTagsChecker : public InterpolationType::ConversionChecker { const Vector<uint32_t> tags_; }; -class InheritedFontVariationSettingsChecker +class InheritedFontVariationSettingsChecker final : public CSSInterpolationType::CSSConversionChecker { public: explicit InheritedFontVariationSettingsChecker( diff --git a/chromium/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc index b7d1d7c02f0..d52bbbf5a33 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc @@ -34,7 +34,7 @@ const StyleImage* GetStyleImage(const CSSProperty& property, } } // namespace -class CSSImageNonInterpolableValue : public NonInterpolableValue { +class CSSImageNonInterpolableValue final : public NonInterpolableValue { public: ~CSSImageNonInterpolableValue() final = default; @@ -165,7 +165,7 @@ bool CSSImageInterpolationType::EqualNonInterpolableValues( To<CSSImageNonInterpolableValue>(*b)); } -class UnderlyingImageChecker +class UnderlyingImageChecker final : public CSSInterpolationType::CSSConversionChecker { public: UnderlyingImageChecker(const InterpolationValue& underlying) @@ -203,7 +203,7 @@ InterpolationValue CSSImageInterpolationType::MaybeConvertInitial( return nullptr; } -class InheritedImageChecker +class InheritedImageChecker final : public CSSInterpolationType::CSSConversionChecker { public: InheritedImageChecker(const CSSProperty& property, diff --git a/chromium/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc index 52fc77278e9..e3090ccf7fd 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc @@ -18,7 +18,7 @@ namespace blink { -class UnderlyingImageListChecker +class UnderlyingImageListChecker final : public CSSInterpolationType::CSSConversionChecker { public: explicit UnderlyingImageListChecker(const InterpolationValue& underlying) @@ -65,7 +65,7 @@ InterpolationValue CSSImageListInterpolationType::MaybeConvertStyleImageList( }); } -class InheritedImageListChecker +class InheritedImageListChecker final : public CSSInterpolationType::CSSConversionChecker { public: InheritedImageListChecker(const CSSProperty& property, diff --git a/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc b/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc index 71ea57ec1e4..27982868f56 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc @@ -9,6 +9,7 @@ #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h" #include "third_party/blink/renderer/core/animation/css_angle_interpolation_type.h" +#include "third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_clip_interpolation_type.h" @@ -83,28 +84,22 @@ const InterpolationTypes& CSSInterpolationTypesMap::Get( DEFINE_STATIC_LOCAL(ApplicableTypesMap, all_applicable_types_map, ()); DEFINE_STATIC_LOCAL(ApplicableTypesMap, composited_applicable_types_map, ()); - ApplicableTypesMap& applicable_types_map = - allow_all_animations_ ? all_applicable_types_map - : composited_applicable_types_map; - - auto entry = applicable_types_map.find(property); - bool found_entry = entry != applicable_types_map.end(); - // Custom property interpolation types may change over time so don't trust the - // applicableTypesMap without checking the registry. + // applicable_types_map without checking the registry. Also since the static + // map is shared between documents, the registered type may be different in + // the different documents. if (registry_ && property.IsCSSCustomProperty()) { - const auto* registration = GetRegistration(registry_, property); - if (registration) { - if (found_entry) { - applicable_types_map.erase(entry); - } + if (const auto* registration = GetRegistration(registry_, property)) return registration->GetInterpolationTypes(); - } } - if (found_entry) { + ApplicableTypesMap& applicable_types_map = + allow_all_animations_ ? all_applicable_types_map + : composited_applicable_types_map; + + auto entry = applicable_types_map.find(property); + if (entry != applicable_types_map.end()) return *entry->value; - } std::unique_ptr<InterpolationTypes> applicable_types = std::make_unique<InterpolationTypes>(); @@ -179,6 +174,10 @@ const InterpolationTypes& CSSInterpolationTypesMap::Get( applicable_types->push_back( std::make_unique<CSSLengthInterpolationType>(used_property)); break; + case CSSPropertyID::kAspectRatio: + applicable_types->push_back( + std::make_unique<CSSAspectRatioInterpolationType>(used_property)); + break; case CSSPropertyID::kFlexGrow: case CSSPropertyID::kFlexShrink: case CSSPropertyID::kFillOpacity: diff --git a/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map_test.cc b/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map_test.cc new file mode 100644 index 00000000000..8811ad19b65 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/css_interpolation_types_map_test.cc @@ -0,0 +1,49 @@ +// Copyright 2021 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/css_interpolation_types_map.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/feature_policy/document_policy.h" +#include "third_party/blink/renderer/core/css/css_test_helpers.h" +#include "third_party/blink/renderer/core/css/property_registry.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/document_init.h" +#include "third_party/blink/renderer/core/testing/null_execution_context.h" + +namespace blink { + +TEST(CSSInterpolationTypesMapTest, RegisteredCustomProperty) { + auto* execution_context = MakeGarbageCollected<NullExecutionContext>(); + execution_context->SetUpSecurityContextForTesting(); + execution_context->GetSecurityContext().SetDocumentPolicy( + DocumentPolicy::CreateWithHeaderPolicy({})); + + DocumentInit init = + DocumentInit::Create().WithExecutionContext(execution_context); + auto* document1 = MakeGarbageCollected<Document>(init); + auto* document2 = MakeGarbageCollected<Document>(init); + + AtomicString property_name("--x"); + PropertyRegistration* registration = + css_test_helpers::CreateLengthRegistration(property_name, 0); + PropertyRegistry* registry = MakeGarbageCollected<PropertyRegistry>(); + registry->RegisterProperty(property_name, *registration); + + CSSInterpolationTypesMap map1(nullptr, *document1); + CSSInterpolationTypesMap map2(registry, *document2); + + PropertyHandle handle(property_name); + auto& types1 = map1.Get(handle); + auto& types2 = map2.Get(handle); + EXPECT_NE(&types1, &types2); + EXPECT_EQ(types1.size(), 1u); + + auto& types1_1 = map1.Get(handle); + EXPECT_EQ(&types1, &types1_1); + + execution_context->NotifyContextDestroyed(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc index 12c18f32300..936b3fbf773 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc @@ -155,12 +155,14 @@ void CSSLengthInterpolationType::ApplyStandardPropertyValue( const float kSlack = 1e-6; const float before_length = FloatValueForLength(before, 100); const float after_length = FloatValueForLength(after, 100); - // Test relative difference for large values to avoid floating point - // inaccuracies tripping the check. - const float delta = std::abs(before_length) < kSlack - ? after_length - before_length - : (after_length - before_length) / before_length; - DCHECK_LT(std::abs(delta), kSlack); + if (std::isfinite(before_length) && std::isfinite(after_length)) { + // Test relative difference for large values to avoid floating point + // inaccuracies tripping the check. + const float delta = std::abs(before_length) < kSlack + ? after_length - before_length + : (after_length - before_length) / before_length; + DCHECK_LT(std::abs(delta), kSlack); + } #endif return; } diff --git a/chromium/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc index d05ce01f00f..93c37aa00ea 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc @@ -65,7 +65,7 @@ InterpolationValue CSSLengthListInterpolationType::MaybeConvertInitial( return MaybeConvertLengthList(initial_length_list, 1); } -class InheritedLengthListChecker +class InheritedLengthListChecker final : public CSSInterpolationType::CSSConversionChecker { public: InheritedLengthListChecker(const CSSProperty& property, diff --git a/chromium/third_party/blink/renderer/core/animation/css_paint_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_paint_interpolation_type.cc index 61fdbd23305..8e14fe549c4 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_paint_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_paint_interpolation_type.cc @@ -30,9 +30,9 @@ bool GetColor(const CSSProperty& property, StyleColor& result) { switch (property.PropertyID()) { case CSSPropertyID::kFill: - return GetColorFromPaint(style.SvgStyle().FillPaint(), result); + return GetColorFromPaint(style.FillPaint(), result); case CSSPropertyID::kStroke: - return GetColorFromPaint(style.SvgStyle().StrokePaint(), result); + return GetColorFromPaint(style.StrokePaint(), result); default: NOTREACHED(); return false; @@ -126,15 +126,14 @@ void CSSPaintInterpolationType::ApplyStandardPropertyValue( StyleResolverState& state) const { Color color = CSSColorInterpolationType::ResolveInterpolableColor( interpolable_color, state); - SVGComputedStyle& mutable_svg_style = state.Style()->AccessSVGStyle(); switch (CssProperty().PropertyID()) { case CSSPropertyID::kFill: - mutable_svg_style.SetFillPaint(SVGPaint(color)); - mutable_svg_style.SetInternalVisitedFillPaint(SVGPaint(color)); + state.Style()->SetFillPaint(SVGPaint(color)); + state.Style()->SetInternalVisitedFillPaint(SVGPaint(color)); break; case CSSPropertyID::kStroke: - mutable_svg_style.SetStrokePaint(SVGPaint(color)); - mutable_svg_style.SetInternalVisitedStrokePaint(SVGPaint(color)); + state.Style()->SetStrokePaint(SVGPaint(color)); + state.Style()->SetInternalVisitedStrokePaint(SVGPaint(color)); break; default: NOTREACHED(); diff --git a/chromium/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc index d60b39522ee..aa9fbc19052 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc @@ -24,7 +24,7 @@ const StylePath* GetPath(const CSSProperty& property, const ComputedStyle& style) { switch (property.PropertyID()) { case CSSPropertyID::kD: - return style.SvgStyle().D(); + return style.D(); case CSSPropertyID::kOffsetPath: return DynamicTo<StylePath>(style.OffsetPath()); case CSSPropertyID::kClipPath: { diff --git a/chromium/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc index 9da7670ecb9..a61bdf0b566 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc @@ -81,7 +81,7 @@ class InheritedScaleChecker } // namespace -class CSSScaleNonInterpolableValue : public NonInterpolableValue { +class CSSScaleNonInterpolableValue final : public NonInterpolableValue { public: ~CSSScaleNonInterpolableValue() final = default; diff --git a/chromium/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc index 829c2921e6e..cb79a29792b 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc @@ -18,7 +18,7 @@ namespace blink { -class UnderlyingSizeListChecker +class UnderlyingSizeListChecker final : public CSSInterpolationType::CSSConversionChecker { public: explicit UnderlyingSizeListChecker(const NonInterpolableList& underlying_list) @@ -47,7 +47,7 @@ class UnderlyingSizeListChecker scoped_refptr<const NonInterpolableList> underlying_list_; }; -class InheritedSizeListChecker +class InheritedSizeListChecker final : public CSSInterpolationType::CSSConversionChecker { public: InheritedSizeListChecker(const CSSProperty& property, diff --git a/chromium/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc index f3156672400..711c61fa9d2 100644 --- a/chromium/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc @@ -13,7 +13,7 @@ namespace blink { -class CSSVisibilityNonInterpolableValue : public NonInterpolableValue { +class CSSVisibilityNonInterpolableValue final : public NonInterpolableValue { public: ~CSSVisibilityNonInterpolableValue() final = default; @@ -59,7 +59,7 @@ struct DowncastTraits<CSSVisibilityNonInterpolableValue> { } }; -class UnderlyingVisibilityChecker +class UnderlyingVisibilityChecker final : public CSSInterpolationType::CSSConversionChecker { public: explicit UnderlyingVisibilityChecker(EVisibility visibility) diff --git a/chromium/third_party/blink/renderer/core/animation/document_animations.cc b/chromium/third_party/blink/renderer/core/animation/document_animations.cc index 74629dfc276..c7ed01c694e 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_animations.cc +++ b/chromium/third_party/blink/renderer/core/animation/document_animations.cc @@ -71,6 +71,7 @@ DocumentAnimations::DocumentAnimations(Document* document) void DocumentAnimations::AddTimeline(AnimationTimeline& timeline) { timelines_.insert(&timeline); + unvalidated_timelines_.insert(&timeline); } void DocumentAnimations::UpdateAnimationTimingForAnimationFrame() { @@ -159,6 +160,15 @@ HeapVector<Member<Animation>> DocumentAnimations::getAnimations( return animations; } +void DocumentAnimations::ValidateTimelines() { + for (auto& timeline : unvalidated_timelines_) { + if (auto* scroll_timeline = DynamicTo<CSSScrollTimeline>(timeline.Get())) + scroll_timeline->ValidateState(); + } + + unvalidated_timelines_.clear(); +} + void DocumentAnimations::CacheCSSScrollTimeline(CSSScrollTimeline& timeline) { // We cache the least seen CSSScrollTimeline for a given name. cached_css_timelines_.Set(timeline.Name(), &timeline); @@ -172,6 +182,7 @@ CSSScrollTimeline* DocumentAnimations::FindCachedCSSScrollTimeline( void DocumentAnimations::Trace(Visitor* visitor) const { visitor->Trace(document_); visitor->Trace(timelines_); + visitor->Trace(unvalidated_timelines_); visitor->Trace(cached_css_timelines_); } diff --git a/chromium/third_party/blink/renderer/core/animation/document_animations.h b/chromium/third_party/blink/renderer/core/animation/document_animations.h index f5e30f0a731..759d9f34600 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_animations.h +++ b/chromium/third_party/blink/renderer/core/animation/document_animations.h @@ -72,6 +72,22 @@ class CORE_EXPORT DocumentAnimations final HeapVector<Member<Animation>> getAnimations(const TreeScope&); + // All newly created AnimationTimelines are considered "unvalidated". This + // means that the internal state of the timeline is considered tentative, + // and that computing the actual state may require an additional style/layout + // pass. + // + // The lifecycle update will call this function after style and layout has + // completed. The function will then go though all unvalidated timelines, + // and compare the current state snapshot to a fresh state snapshot. If they + // are equal, then the tentative state turned out to be correct, and no + // further action is needed. Otherwise, all effects targets associated with + // the timeline are marked for recalc, which causes the style/layout phase + // to run again. + // + // https://github.com/w3c/csswg-drafts/issues/5261 + void ValidateTimelines(); + void CacheCSSScrollTimeline(CSSScrollTimeline&); CSSScrollTimeline* FindCachedCSSScrollTimeline(const AtomicString&); @@ -79,6 +95,10 @@ class CORE_EXPORT DocumentAnimations final const { return timelines_; } + const HeapHashSet<WeakMember<AnimationTimeline>>& + GetUnvalidatedTimelinesForTesting() const { + return unvalidated_timelines_; + } uint64_t current_transition_generation_; void Trace(Visitor*) const; @@ -90,6 +110,7 @@ class CORE_EXPORT DocumentAnimations final private: Member<Document> document_; HeapHashSet<WeakMember<AnimationTimeline>> timelines_; + HeapHashSet<WeakMember<AnimationTimeline>> unvalidated_timelines_; // We cache CSSScrollTimelines by name, such that multiple animations using // the same timeline can use the same CSSScrollTimeline instance. diff --git a/chromium/third_party/blink/renderer/core/animation/document_animations_test.cc b/chromium/third_party/blink/renderer/core/animation/document_animations_test.cc index 9e8ec4d63ba..75ab69b5cea 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_animations_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/document_animations_test.cc @@ -24,7 +24,7 @@ class MockAnimationTimeline : public AnimationTimeline { MOCK_METHOD0(Phase, TimelinePhase()); MOCK_CONST_METHOD0(IsActive, bool()); - MOCK_METHOD0(ZeroTimeInSeconds, double()); + MOCK_METHOD0(ZeroTime, AnimationTimeDelta()); MOCK_METHOD0(InitialStartTimeForAnimations, base::Optional<base::TimeDelta>()); MOCK_METHOD0(NeedsAnimationTimingUpdate, bool()); diff --git a/chromium/third_party/blink/renderer/core/animation/document_timeline.cc b/chromium/third_party/blink/renderer/core/animation/document_timeline.cc index c05b1f14b81..6abfb137535 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_timeline.cc +++ b/chromium/third_party/blink/renderer/core/animation/document_timeline.cc @@ -44,10 +44,11 @@ namespace { // Returns the current animation time for a given |document|. This is // the animation clock time capped to be at least this document's -// ZeroTime() such that the animation time is never negative when converted. +// CalculateZeroTime() such that the animation time is never negative when +// converted. base::TimeTicks CurrentAnimationTime(Document* document) { base::TimeTicks animation_time = document->GetAnimationClock().CurrentTime(); - base::TimeTicks document_zero_time = document->Timeline().ZeroTime(); + base::TimeTicks document_zero_time = document->Timeline().CalculateZeroTime(); // The AnimationClock time may be null or less than the local document's // zero time if we have not generated any frames for this document yet. If @@ -142,10 +143,11 @@ void DocumentTimeline::DocumentTimelineTiming::WakeAfter( void DocumentTimeline::DocumentTimelineTiming::Trace(Visitor* visitor) const { visitor->Trace(timeline_); + visitor->Trace(timer_); DocumentTimeline::PlatformTiming::Trace(visitor); } -base::TimeTicks DocumentTimeline::ZeroTime() { +base::TimeTicks DocumentTimeline::CalculateZeroTime() { if (!zero_time_initialized_ && document_->Loader()) { zero_time_ = document_->Loader()->GetTiming().ReferenceMonotonicTime() + origin_time_; @@ -172,12 +174,14 @@ AnimationTimeline::PhaseAndTime DocumentTimeline::CurrentPhaseAndTime() { base::Optional<base::TimeDelta> result = playback_rate_ == 0 - ? ZeroTime().since_origin() - : (CurrentAnimationTime(GetDocument()) - ZeroTime()) * playback_rate_; + ? CalculateZeroTime().since_origin() + : (CurrentAnimationTime(GetDocument()) - CalculateZeroTime()) * + playback_rate_; return {TimelinePhase::kActive, result}; } -void DocumentTimeline::PauseAnimationsForTesting(double pause_time) { +void DocumentTimeline::PauseAnimationsForTesting( + AnimationTimeDelta pause_time) { for (const auto& animation : animations_needing_update_) animation->PauseForTesting(pause_time); ServiceAnimations(kTimingUpdateOnDemand); diff --git a/chromium/third_party/blink/renderer/core/animation/document_timeline.h b/chromium/third_party/blink/renderer/core/animation/document_timeline.h index a1392ecdb62..87ae16853da 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_timeline.h +++ b/chromium/third_party/blink/renderer/core/animation/document_timeline.h @@ -78,12 +78,14 @@ class CORE_EXPORT DocumentTimeline : public AnimationTimeline { // The zero time of DocumentTimeline is computed by adding a separate // |origin_time_| from DocumentTimelineOptions. // https://drafts.csswg.org/web-animations/#origin-time - base::TimeTicks ZeroTime(); - double ZeroTimeInSeconds() override { - return ZeroTime().since_origin().InSecondsF(); + // TODO(crbug.com/1162960) Convert DocumentTimeline::zero_time_ from + // base::TimeTicks to AnimationTimeDelta + base::TimeTicks CalculateZeroTime(); + AnimationTimeDelta ZeroTime() override { + return AnimationTimeDelta(CalculateZeroTime().since_origin()); } - void PauseAnimationsForTesting(double); + void PauseAnimationsForTesting(AnimationTimeDelta); void InvalidateKeyframeEffects(const TreeScope&); @@ -107,6 +109,8 @@ class CORE_EXPORT DocumentTimeline : public AnimationTimeline { base::TimeDelta origin_time_; // The origin time. This is computed by adding |origin_time_| to the time // origin of the document. + // TODO(crbug.com/1162960) Convert DocumentTimeline::zero_time_ from + // base::TimeTicks to AnimationTimeDelta base::TimeTicks zero_time_; bool zero_time_initialized_; @@ -119,7 +123,7 @@ class CORE_EXPORT DocumentTimeline : public AnimationTimeline { class DocumentTimelineTiming final : public PlatformTiming { public: - DocumentTimelineTiming(DocumentTimeline* timeline) + explicit DocumentTimelineTiming(DocumentTimeline* timeline) : timeline_(timeline), timer_(timeline->GetDocument()->GetTaskRunner( TaskType::kInternalDefault), @@ -136,7 +140,7 @@ class CORE_EXPORT DocumentTimeline : public AnimationTimeline { private: Member<DocumentTimeline> timeline_; - TaskRunnerTimer<DocumentTimelineTiming> timer_; + HeapTaskRunnerTimer<DocumentTimelineTiming> timer_; }; friend class AnimationDocumentTimelineTest; diff --git a/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc b/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc index f165b142f00..6df9206afa0 100644 --- a/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/document_timeline_test.cc @@ -38,6 +38,7 @@ #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/pending_animations.h" +#include "third_party/blink/renderer/core/animation/timing_calculations.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/dom/qualified_name.h" @@ -53,6 +54,10 @@ namespace { base::TimeTicks TimeTicksFromMillisecondsD(double seconds) { return base::TimeTicks() + base::TimeDelta::FromMillisecondsD(seconds); } + +#define EXPECT_TIME_NEAR(expected, value) \ + EXPECT_NEAR((expected).InMillisecondsF(), (value).InMillisecondsF(), \ + Animation::kTimeToleranceMs) } // namespace namespace blink { @@ -209,16 +214,16 @@ TEST_F(AnimationDocumentTimelineTest, CurrentTimeSeconds) { } TEST_F(AnimationDocumentTimelineTest, PlaybackRateNormal) { - base::TimeTicks zero_time = timeline->ZeroTime(); + base::TimeTicks zero_time = timeline->CalculateZeroTime(); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(1000)); - EXPECT_EQ(zero_time, timeline->ZeroTime()); + EXPECT_EQ(zero_time, timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(2000)); - EXPECT_EQ(zero_time, timeline->ZeroTime()); + EXPECT_EQ(zero_time, timeline->CalculateZeroTime()); EXPECT_EQ(2000, timeline->CurrentTimeMilliseconds()); } @@ -229,33 +234,33 @@ TEST_F(AnimationDocumentTimelineTest, PlaybackRateNormalWithOriginTime) { timeline->ResetForTesting(); EXPECT_EQ(1.0, timeline->PlaybackRate()); - EXPECT_EQ(base::TimeTicks() + origin_time, timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks() + origin_time, timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(100)); - EXPECT_EQ(base::TimeTicks() + origin_time, timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks() + origin_time, timeline->CalculateZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(200)); - EXPECT_EQ(base::TimeTicks() + origin_time, timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks() + origin_time, timeline->CalculateZeroTime()); EXPECT_EQ(1200, timeline->CurrentTimeMilliseconds()); } TEST_F(AnimationDocumentTimelineTest, PlaybackRatePause) { GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(1000)); - EXPECT_EQ(base::TimeTicks(), timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks(), timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(0.0); EXPECT_EQ(0.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(2000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(1000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(1000), timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(4000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(1000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(1000), timeline->CalculateZeroTime()); EXPECT_EQ(3000, timeline->CurrentTimeMilliseconds()); } @@ -265,61 +270,61 @@ TEST_F(AnimationDocumentTimelineTest, PlaybackRatePauseWithOriginTime) { document.Get(), origin_time, platform_timing); timeline->ResetForTesting(); - EXPECT_EQ(base::TimeTicks() + origin_time, timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks() + origin_time, timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(100)); - EXPECT_EQ(base::TimeTicks() + origin_time, timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks() + origin_time, timeline->CalculateZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(0.0); EXPECT_EQ(0.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(200)); - EXPECT_EQ(TimeTicksFromMillisecondsD(1100), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(1100), timeline->CalculateZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); - EXPECT_EQ(TimeTicksFromMillisecondsD(-900), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-900), timeline->CalculateZeroTime()); EXPECT_EQ(1100, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(400)); - EXPECT_EQ(TimeTicksFromMillisecondsD(-900), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-900), timeline->CalculateZeroTime()); EXPECT_EQ(1300, timeline->CurrentTimeMilliseconds()); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateSlow) { GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(1000)); - EXPECT_EQ(base::TimeTicks(), timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks(), timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(0.5); EXPECT_EQ(0.5, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(3000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(-1000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-1000), timeline->CalculateZeroTime()); EXPECT_EQ(2000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(4000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(1000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(1000), timeline->CalculateZeroTime()); EXPECT_EQ(3000, timeline->CurrentTimeMilliseconds()); } TEST_F(AnimationDocumentTimelineTest, PlaybackRateFast) { GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(1000)); - EXPECT_EQ(base::TimeTicks(), timeline->ZeroTime()); + EXPECT_EQ(base::TimeTicks(), timeline->CalculateZeroTime()); EXPECT_EQ(1000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(2.0); EXPECT_EQ(2.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(3000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(500), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(500), timeline->CalculateZeroTime()); EXPECT_EQ(5000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(4000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(-2000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-2000), timeline->CalculateZeroTime()); EXPECT_EQ(6000, timeline->CurrentTimeMilliseconds()); } @@ -329,30 +334,33 @@ TEST_F(AnimationDocumentTimelineTest, PlaybackRateFastWithOriginTime) { timeline->ResetForTesting(); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(100000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(-1000000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-1000000), + timeline->CalculateZeroTime()); EXPECT_EQ(1100000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(2.0); EXPECT_EQ(2.0, timeline->PlaybackRate()); - EXPECT_EQ(TimeTicksFromMillisecondsD(-450000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-450000), timeline->CalculateZeroTime()); EXPECT_EQ(1100000, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(300000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(-450000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-450000), timeline->CalculateZeroTime()); EXPECT_EQ(1500000, timeline->CurrentTimeMilliseconds()); timeline->SetPlaybackRate(1.0); EXPECT_EQ(1.0, timeline->PlaybackRate()); - EXPECT_EQ(TimeTicksFromMillisecondsD(-1200000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-1200000), + timeline->CalculateZeroTime()); EXPECT_EQ(1500000, timeline->CurrentTimeMilliseconds()); GetAnimationClock().UpdateTime(TimeTicksFromMillisecondsD(400000)); - EXPECT_EQ(TimeTicksFromMillisecondsD(-1200000), timeline->ZeroTime()); + EXPECT_EQ(TimeTicksFromMillisecondsD(-1200000), + timeline->CalculateZeroTime()); EXPECT_EQ(1600000, timeline->CurrentTimeMilliseconds()); } TEST_F(AnimationDocumentTimelineTest, PauseForTesting) { - float seek_time = 1; + AnimationTimeDelta seek_time = AnimationTimeDelta::FromSecondsD(1); timing.fill_mode = Timing::FillMode::FORWARDS; auto* anim1 = MakeGarbageCollected<KeyframeEffect>( element.Get(), CreateEmptyEffectModel(), timing); @@ -362,8 +370,13 @@ TEST_F(AnimationDocumentTimelineTest, PauseForTesting) { Animation* animation2 = timeline->Play(anim2); timeline->PauseAnimationsForTesting(seek_time); - EXPECT_FLOAT_EQ(seek_time * 1000, animation1->currentTime().value()); - EXPECT_FLOAT_EQ(seek_time * 1000, animation2->currentTime().value()); + CSSNumberish current_time; + animation1->currentTime(current_time); + EXPECT_NEAR(seek_time.InMillisecondsF(), current_time.GetAsDouble(), + Animation::kTimeToleranceMs); + animation2->currentTime(current_time); + EXPECT_NEAR(seek_time.InMillisecondsF(), current_time.GetAsDouble(), + Animation::kTimeToleranceMs); } TEST_F(AnimationDocumentTimelineTest, DelayBeforeAnimationStart) { @@ -397,7 +410,7 @@ TEST_F(AnimationDocumentTimelineTest, UseAnimationAfterTimelineDeref) { Animation* animation = timeline->Play(nullptr); timeline.Clear(); // Test passes if this does not crash. - animation->setStartTime(0); + animation->setStartTime(CSSNumberish::FromDouble(0)); } TEST_F(AnimationDocumentTimelineTest, PlayAfterDocumentDeref) { @@ -459,8 +472,9 @@ TEST_F(AnimationDocumentTimelineRealTimeTest, DocumentTimeline* timeline = MakeGarbageCollected<DocumentTimeline>(document.Get(), origin_time); timeline->SetPlaybackRate(0.5); - EXPECT_EQ(origin_time * 2, - timeline->ZeroTime() - document->Timeline().ZeroTime()); + + EXPECT_TIME_NEAR(AnimationTimeDelta(origin_time) * 2, + timeline->ZeroTime() - document->Timeline().ZeroTime()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/effect_stack_test.cc b/chromium/third_party/blink/renderer/core/animation/effect_stack_test.cc index 8f4ea26b32f..bf4f93ecf76 100644 --- a/chromium/third_party/blink/renderer/core/animation/effect_stack_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/effect_stack_test.cc @@ -34,14 +34,14 @@ class AnimationEffectStackTest : public PageTestBase { Animation* Play(KeyframeEffect* effect, double start_time) { Animation* animation = timeline->Play(effect); - animation->setStartTime(start_time * 1000); + animation->setStartTime(CSSNumberish::FromDouble(start_time * 1000)); animation->Update(kTimingUpdateOnDemand); return animation; } void UpdateTimeline(base::TimeDelta time) { GetDocument().GetAnimationClock().UpdateTime( - GetDocument().Timeline().ZeroTime() + time); + GetDocument().Timeline().CalculateZeroTime() + time); timeline->ServiceAnimations(kTimingUpdateForAnimationFrame); } @@ -68,8 +68,8 @@ class AnimationEffectStackTest : public PageTestBase { InertEffect* MakeInertEffect(KeyframeEffectModelBase* effect) { Timing timing; timing.fill_mode = Timing::FillMode::BOTH; - return MakeGarbageCollected<InertEffect>(effect, timing, false, 0, - base::nullopt); + return MakeGarbageCollected<InertEffect>( + effect, timing, false, AnimationTimeDelta(), base::nullopt); } KeyframeEffect* MakeKeyframeEffect(KeyframeEffectModelBase* effect, diff --git a/chromium/third_party/blink/renderer/core/animation/element_animations.cc b/chromium/third_party/blink/renderer/core/animation/element_animations.cc index 705df935c98..afad6b763cf 100644 --- a/chromium/third_party/blink/renderer/core/animation/element_animations.cc +++ b/chromium/third_party/blink/renderer/core/animation/element_animations.cc @@ -51,6 +51,8 @@ void UpdateAnimationFlagsForEffect(const KeyframeEffect& effect, style.SetHasCurrentFilterAnimation(true); if (effect.Affects(PropertyHandle(GetCSSPropertyBackdropFilter()))) style.SetHasCurrentBackdropFilterAnimation(true); + if (effect.Affects(PropertyHandle(GetCSSPropertyBackgroundColor()))) + style.SetHasCurrentBackgroundColorAnimation(true); } } // namespace @@ -151,4 +153,16 @@ bool ElementAnimations::UpdateBoxSizeAndCheckTransformAxisAlignment( return preserves_axis_alignment; } +bool ElementAnimations::IsIdentityOrTranslation() const { + for (auto& entry : animations_) { + if (auto* effect = DynamicTo<KeyframeEffect>(entry.key->effect())) { + if (!effect->IsCurrent() && !effect->IsInEffect()) + continue; + if (!effect->IsIdentityOrTranslation()) + return false; + } + } + return true; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/element_animations.h b/chromium/third_party/blink/renderer/core/animation/element_animations.h index a0277a0724a..babb8cbff4c 100644 --- a/chromium/third_party/blink/renderer/core/animation/element_animations.h +++ b/chromium/third_party/blink/renderer/core/animation/element_animations.h @@ -89,6 +89,7 @@ class CORE_EXPORT ElementAnimations final void ClearBaseComputedStyle(); bool UpdateBoxSizeAndCheckTransformAxisAlignment(const FloatSize& box_size); + bool IsIdentityOrTranslation() const; void Trace(Visitor*) const; diff --git a/chromium/third_party/blink/renderer/core/animation/inert_effect.cc b/chromium/third_party/blink/renderer/core/animation/inert_effect.cc index 7599fe76593..210a2dfced5 100644 --- a/chromium/third_party/blink/renderer/core/animation/inert_effect.cc +++ b/chromium/third_party/blink/renderer/core/animation/inert_effect.cc @@ -37,7 +37,7 @@ namespace blink { InertEffect::InertEffect(KeyframeEffectModelBase* model, const Timing& timing, bool paused, - base::Optional<double> inherited_time, + base::Optional<AnimationTimeDelta> inherited_time, base::Optional<TimelinePhase> inherited_phase) : AnimationEffect(timing), model_(model), diff --git a/chromium/third_party/blink/renderer/core/animation/inert_effect.h b/chromium/third_party/blink/renderer/core/animation/inert_effect.h index 157b1207093..76e785839a5 100644 --- a/chromium/third_party/blink/renderer/core/animation/inert_effect.h +++ b/chromium/third_party/blink/renderer/core/animation/inert_effect.h @@ -45,7 +45,7 @@ class CORE_EXPORT InertEffect final : public AnimationEffect { InertEffect(KeyframeEffectModelBase*, const Timing&, bool paused, - base::Optional<double> inherited_time, + base::Optional<AnimationTimeDelta> inherited_time, base::Optional<TimelinePhase> inherited_phase); void Sample(HeapVector<Member<Interpolation>>&) const; @@ -66,7 +66,7 @@ class CORE_EXPORT InertEffect final : public AnimationEffect { private: Member<KeyframeEffectModelBase> model_; bool paused_; - base::Optional<double> inherited_time_; + base::Optional<AnimationTimeDelta> inherited_time_; base::Optional<TimelinePhase> inherited_phase_; }; diff --git a/chromium/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc b/chromium/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc new file mode 100644 index 00000000000..cfcbc5408f0 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc @@ -0,0 +1,65 @@ +// Copyright 2021 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/interpolable_aspect_ratio.h" +#include "third_party/blink/renderer/core/animation/interpolable_value.h" +#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" +#include "third_party/blink/renderer/core/style/style_aspect_ratio.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" + +namespace blink { + +// static +std::unique_ptr<InterpolableAspectRatio> InterpolableAspectRatio::MaybeCreate( + const StyleAspectRatio& aspect_ratio) { + if (!RuntimeEnabledFeatures::CSSAspectRatioInterpolationEnabled()) + return nullptr; + + // Auto aspect ratio cannot be interpolated to / from. + if (aspect_ratio.IsAuto()) + return nullptr; + return std::make_unique<InterpolableAspectRatio>(aspect_ratio.GetRatio()); +} + +InterpolableAspectRatio::InterpolableAspectRatio( + const FloatSize& aspect_ratio) { + // The StyleAspectRatio::IsAuto check in MaybeCreate should return true if we + // have a degenerate aspect ratio. + DCHECK(aspect_ratio.Height() > 0 && aspect_ratio.Width() > 0); + + value_ = std::make_unique<InterpolableNumber>( + log(aspect_ratio.Width() / aspect_ratio.Height())); +} + +FloatSize InterpolableAspectRatio::GetRatio() const { + return FloatSize(exp(To<InterpolableNumber>(*value_).Value()), 1); +} + +void InterpolableAspectRatio::Scale(double scale) { + value_->Scale(scale); +} + +void InterpolableAspectRatio::Add(const InterpolableValue& other) { + value_->Add(*To<InterpolableAspectRatio>(other).value_); +} + +void InterpolableAspectRatio::AssertCanInterpolateWith( + const InterpolableValue& other) const { + const InterpolableAspectRatio& other_aspect_ratio = + To<InterpolableAspectRatio>(other); + value_->AssertCanInterpolateWith(*other_aspect_ratio.value_); +} + +void InterpolableAspectRatio::Interpolate(const InterpolableValue& to, + const double progress, + InterpolableValue& result) const { + const InterpolableAspectRatio& aspect_ratio_to = + To<InterpolableAspectRatio>(to); + InterpolableAspectRatio& aspect_ratio_result = + To<InterpolableAspectRatio>(result); + value_->Interpolate(*aspect_ratio_to.value_, progress, + *aspect_ratio_result.value_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h b/chromium/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h new file mode 100644 index 00000000000..36d29a1d8df --- /dev/null +++ b/chromium/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h @@ -0,0 +1,62 @@ +// Copyright 2021 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_ + +#include <memory> +#include "third_party/blink/renderer/core/animation/interpolable_value.h" +#include "third_party/blink/renderer/core/style/style_aspect_ratio.h" + +namespace blink { + +// Represents a blink::StyleAspectRatio, converted into its logarithm for +// interpolation. +class CORE_EXPORT InterpolableAspectRatio final : public InterpolableValue { + public: + explicit InterpolableAspectRatio(const FloatSize& ratio); + explicit InterpolableAspectRatio(std::unique_ptr<InterpolableValue> value) + : value_(std::move(value)) {} + + static std::unique_ptr<InterpolableAspectRatio> MaybeCreate( + const StyleAspectRatio&); + + FloatSize GetRatio() const; + + // InterpolableValue implementation: + void Interpolate(const InterpolableValue& to, + const double progress, + InterpolableValue& result) const final; + bool IsAspectRatio() const final { return true; } + bool Equals(const InterpolableValue& other) const final { + NOTREACHED(); + return false; + } + void Scale(double scale) final; + void Add(const InterpolableValue& other) final; + void AssertCanInterpolateWith(const InterpolableValue& other) const final; + + private: + InterpolableAspectRatio* RawClone() const final { + return new InterpolableAspectRatio(value_->Clone()); + } + InterpolableAspectRatio* RawCloneAndZero() const final { + return new InterpolableAspectRatio(value_->CloneAndZero()); + } + + // Interpolable aspect ratio value is stored and interpolated as the log of + // the real aspect ratio. + std::unique_ptr<InterpolableValue> value_; +}; + +template <> +struct DowncastTraits<InterpolableAspectRatio> { + static bool AllowFrom(const InterpolableValue& interpolable_value) { + return interpolable_value.IsAspectRatio(); + } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_ diff --git a/chromium/third_party/blink/renderer/core/animation/interpolable_length.cc b/chromium/third_party/blink/renderer/core/animation/interpolable_length.cc index 08e6cc60d5a..4e958c4de06 100644 --- a/chromium/third_party/blink/renderer/core/animation/interpolable_length.cc +++ b/chromium/third_party/blink/renderer/core/animation/interpolable_length.cc @@ -204,8 +204,11 @@ static UnitType IndexToUnitType(wtf_size_t index) { Length InterpolableLength::CreateLength( const CSSToLengthConversionData& conversion_data, ValueRange range) const { + // Passing true for ToCalcValue is a dirty hack to ensure that we don't create + // a degenerate value when animating 'background-position', while we know it + // may cause some minor animation glitches for the other properties. if (IsExpression()) - return Length(expression_->ToCalcValue(conversion_data, range)); + return Length(expression_->ToCalcValue(conversion_data, range, true)); bool has_percentage = HasPercentage(); double pixels = 0; diff --git a/chromium/third_party/blink/renderer/core/animation/interpolable_value.h b/chromium/third_party/blink/renderer/core/animation/interpolable_value.h index 0a846d2f464..21428f41228 100644 --- a/chromium/third_party/blink/renderer/core/animation/interpolable_value.h +++ b/chromium/third_party/blink/renderer/core/animation/interpolable_value.h @@ -39,6 +39,7 @@ class CORE_EXPORT InterpolableValue { virtual bool IsBool() const { return false; } virtual bool IsList() const { return false; } virtual bool IsLength() const { return false; } + virtual bool IsAspectRatio() const { return false; } virtual bool IsShadow() const { return false; } virtual bool IsFilter() const { return false; } virtual bool IsTransformList() const { return false; } diff --git a/chromium/third_party/blink/renderer/core/animation/keyframe_effect.cc b/chromium/third_party/blink/renderer/core/animation/keyframe_effect.cc index 082b5a3ee9f..3cc8831033b 100644 --- a/chromium/third_party/blink/renderer/core/animation/keyframe_effect.cc +++ b/chromium/third_party/blink/renderer/core/animation/keyframe_effect.cc @@ -432,7 +432,7 @@ void KeyframeEffect::AttachCompositedLayers() { // very special element id for this animation so that the compositor animation // system recognize it. We do not use 0 as the element id because 0 is // kInvalidElementId. - if (compositor_animation && !Model()->HasNonVariableProperty()) { + if (compositor_animation && !Model()->RequiresPropertyNode()) { compositor_animation->AttachNoElement(); return; } @@ -503,12 +503,10 @@ bool KeyframeEffect::UpdateBoxSizeAndCheckTransformAxisAlignment( if (effect_target_size_) { if ((size_dependencies & TransformOperation::kDependsWidth) && (effect_target_size_->Width() != box_size.Width())) - GetAnimation()->RestartAnimationOnCompositor(); + RestartRunningAnimationOnCompositor(); else if ((size_dependencies & TransformOperation::kDependsHeight) && - (effect_target_size_->Width() != box_size.Height())) - GetAnimation()->RestartAnimationOnCompositor(); - } else if (size_dependencies) { - GetAnimation()->RestartAnimationOnCompositor(); + (effect_target_size_->Height() != box_size.Height())) + RestartRunningAnimationOnCompositor(); } } @@ -517,6 +515,40 @@ bool KeyframeEffect::UpdateBoxSizeAndCheckTransformAxisAlignment( return preserves_axis_alignment; } +void KeyframeEffect::RestartRunningAnimationOnCompositor() { + Animation* animation = GetAnimation(); + if (!animation) + return; + + // No need to to restart an animation that is in the process of starting up, + // paused or idle. + if (!animation->StartTimeInternal()) + return; + + animation->RestartAnimationOnCompositor(); +} + +bool KeyframeEffect::IsIdentityOrTranslation() const { + static const auto** properties = TransformProperties(); + for (size_t i = 0; i < num_transform_properties; i++) { + const auto* keyframes = + Model()->GetPropertySpecificKeyframes(PropertyHandle(*properties[i])); + if (!keyframes) + continue; + + for (const auto& keyframe : *keyframes) { + if (const auto* value = keyframe->GetCompositorKeyframeValue()) { + if (!To<CompositorKeyframeTransform>(value) + ->GetTransformOperations() + .IsIdentityOrTranslation()) { + return false; + } + } + } + } + return true; +} + EffectModel::CompositeOperation KeyframeEffect::CompositeInternal() const { return model_->Composite(); } @@ -744,7 +776,7 @@ void KeyframeEffect::CountAnimatedProperties() const { Document& document = target_element_->GetDocument(); for (const auto& property : model_->Properties()) { if (property.IsCSSProperty()) { - DCHECK(isValidCSSPropertyID(property.GetCSSProperty().PropertyID())); + DCHECK(IsValidCSSPropertyID(property.GetCSSProperty().PropertyID())); document.CountAnimatedProperty(property.GetCSSProperty().PropertyID()); } } diff --git a/chromium/third_party/blink/renderer/core/animation/keyframe_effect.h b/chromium/third_party/blink/renderer/core/animation/keyframe_effect.h index e0e72423223..7e880f50f65 100644 --- a/chromium/third_party/blink/renderer/core/animation/keyframe_effect.h +++ b/chromium/third_party/blink/renderer/core/animation/keyframe_effect.h @@ -136,6 +136,7 @@ class CORE_EXPORT KeyframeEffect final : public AnimationEffect { void Trace(Visitor*) const override; bool UpdateBoxSizeAndCheckTransformAxisAlignment(const FloatSize& box_size); + bool IsIdentityOrTranslation() const; ActiveInterpolationsMap InterpolationsForCommitStyles(); @@ -165,6 +166,7 @@ class CORE_EXPORT KeyframeEffect final : public AnimationEffect { AnimationTimeDelta time_to_next_iteration) const override; bool HasIncompatibleStyle() const; bool HasMultipleTransformProperties() const; + void RestartRunningAnimationOnCompositor(); Member<Element> effect_target_; Member<Element> target_element_; diff --git a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.cc b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.cc index db13009db28..5eea620d145 100644 --- a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.cc +++ b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.cc @@ -96,15 +96,15 @@ bool KeyframeEffectModelBase::Sample( namespace { -static const size_t num_compositable_properties = 7; +static const size_t num_compositable_properties = 8; const CSSProperty** CompositableProperties() { static const CSSProperty* kCompositableProperties[num_compositable_properties] = { - &GetCSSPropertyOpacity(), &GetCSSPropertyRotate(), - &GetCSSPropertyScale(), &GetCSSPropertyTransform(), - &GetCSSPropertyTranslate(), &GetCSSPropertyFilter(), - &GetCSSPropertyBackdropFilter()}; + &GetCSSPropertyOpacity(), &GetCSSPropertyRotate(), + &GetCSSPropertyScale(), &GetCSSPropertyTransform(), + &GetCSSPropertyTranslate(), &GetCSSPropertyFilter(), + &GetCSSPropertyBackdropFilter(), &GetCSSPropertyBackgroundColor()}; return kCompositableProperties; } @@ -351,11 +351,13 @@ void KeyframeEffectModelBase::EnsureKeyframeGroups() const { } } -bool KeyframeEffectModelBase::HasNonVariableProperty() const { +bool KeyframeEffectModelBase::RequiresPropertyNode() const { for (const auto& keyframe : keyframes_) { for (const auto& property : keyframe->Properties()) { if (!property.IsCSSProperty() || - property.GetCSSProperty().PropertyID() != CSSPropertyID::kVariable) + (property.GetCSSProperty().PropertyID() != CSSPropertyID::kVariable && + property.GetCSSProperty().PropertyID() != + CSSPropertyID::kBackgroundColor)) return true; } } diff --git a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.h b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.h index 4331383890e..61ae955cf4d 100644 --- a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.h +++ b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model.h @@ -157,7 +157,7 @@ class CORE_EXPORT KeyframeEffectModelBase : public EffectModel { return has_revert_; } - bool HasNonVariableProperty() const; + bool RequiresPropertyNode() const; bool IsTransformRelatedEffect() const override; diff --git a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc index 681cc986ef0..4a75261a456 100644 --- a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_model_test.cc @@ -160,7 +160,8 @@ const PropertySpecificKeyframeVector& ConstructEffectAndGetKeyframes( auto* effect = MakeGarbageCollected<StringKeyframeEffectModel>(keyframes); - auto style = document->GetStyleResolver().StyleForElement(element); + auto style = document->GetStyleResolver().StyleForElement( + element, StyleRecalcContext()); // Snapshot should update first time after construction EXPECT_TRUE(effect->SnapshotAllCompositorKeyframesIfNecessary( @@ -634,7 +635,8 @@ TEST_F(AnimationKeyframeEffectModel, CompositorSnapshotUpdateBasic) { KeyframesAtZeroAndOne(CSSPropertyID::kOpacity, "0", "1"); auto* effect = MakeGarbageCollected<StringKeyframeEffectModel>(keyframes); - auto style = GetDocument().GetStyleResolver().StyleForElement(element); + auto style = GetDocument().GetStyleResolver().StyleForElement( + element, StyleRecalcContext()); const CompositorKeyframeValue* value; @@ -670,7 +672,8 @@ TEST_F(AnimationKeyframeEffectModel, auto* effect = MakeGarbageCollected<StringKeyframeEffectModel>(opacity_keyframes); - auto style = GetDocument().GetStyleResolver().StyleForElement(element); + auto style = GetDocument().GetStyleResolver().StyleForElement( + element, StyleRecalcContext()); EXPECT_TRUE(effect->SnapshotAllCompositorKeyframesIfNecessary( *element, *style, nullptr)); diff --git a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_test.cc b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_test.cc index 8a17f8569f7..c7d170c625a 100644 --- a/chromium/third_party/blink/renderer/core/animation/keyframe_effect_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/keyframe_effect_test.cc @@ -7,6 +7,7 @@ #include <memory> #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.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_binding_for_testing.h" #include "third_party/blink/renderer/bindings/core/v8/v8_effect_timing.h" @@ -29,6 +30,10 @@ namespace blink { +#define EXPECT_TIMEDELTA(expected, observed) \ + EXPECT_NEAR(expected.InMillisecondsF(), observed.InMillisecondsF(), \ + Animation::kTimeToleranceMs) + using animation_test_helpers::SetV8ObjectPropertyAsNumber; using animation_test_helpers::SetV8ObjectPropertyAsString; @@ -227,8 +232,8 @@ TEST_F(AnimationKeyframeEffectV8Test, CanSetDuration) { KeyframeEffect* animation = CreateAnimationFromTiming( script_state, element.Get(), js_keyframes, duration); - EXPECT_EQ(duration / 1000, - animation->SpecifiedTiming().iteration_duration->InSecondsF()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromMillisecondsD(duration), + animation->SpecifiedTiming().iteration_duration.value()); } TEST_F(AnimationKeyframeEffectV8Test, CanOmitSpecifiedDuration) { @@ -367,35 +372,38 @@ TEST_F(KeyframeEffectTest, TimeToEffectChange) { Animation* animation = GetDocument().Timeline().Play(keyframe_effect); // Beginning of the animation. - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(100), - keyframe_effect->TimeToForwardsEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(100), + keyframe_effect->TimeToForwardsEffectChange()); EXPECT_EQ(AnimationTimeDelta::Max(), keyframe_effect->TimeToReverseEffectChange()); // End of the before phase. - animation->setCurrentTime(100000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(100), - keyframe_effect->TimeToForwardsEffectChange()); - EXPECT_EQ(AnimationTimeDelta(), keyframe_effect->TimeToReverseEffectChange()); + animation->setCurrentTime(CSSNumberish::FromDouble(100000)); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(100), + keyframe_effect->TimeToForwardsEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + keyframe_effect->TimeToReverseEffectChange()); // Nearing the end of the active phase. - animation->setCurrentTime(199000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(1), - keyframe_effect->TimeToForwardsEffectChange()); - EXPECT_EQ(AnimationTimeDelta(), keyframe_effect->TimeToReverseEffectChange()); + animation->setCurrentTime(CSSNumberish::FromDouble(199000)); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(1), + keyframe_effect->TimeToForwardsEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + keyframe_effect->TimeToReverseEffectChange()); // End of the active phase. - animation->setCurrentTime(200000); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(100), - keyframe_effect->TimeToForwardsEffectChange()); - EXPECT_EQ(AnimationTimeDelta(), keyframe_effect->TimeToReverseEffectChange()); + animation->setCurrentTime(CSSNumberish::FromDouble(200000)); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(100), + keyframe_effect->TimeToForwardsEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta(), + keyframe_effect->TimeToReverseEffectChange()); // End of the animation. - animation->setCurrentTime(300000); + animation->setCurrentTime(CSSNumberish::FromDouble(300000)); EXPECT_EQ(AnimationTimeDelta::Max(), keyframe_effect->TimeToForwardsEffectChange()); - EXPECT_EQ(AnimationTimeDelta::FromSecondsD(100), - keyframe_effect->TimeToReverseEffectChange()); + EXPECT_TIMEDELTA(AnimationTimeDelta::FromSecondsD(100), + keyframe_effect->TimeToReverseEffectChange()); } TEST_F(KeyframeEffectTest, CheckCanStartAnimationOnCompositorNoKeyframes) { diff --git a/chromium/third_party/blink/renderer/core/animation/length_property_functions.cc b/chromium/third_party/blink/renderer/core/animation/length_property_functions.cc index de2cc0c91df..2b78a53fb3f 100644 --- a/chromium/third_party/blink/renderer/core/animation/length_property_functions.cc +++ b/chromium/third_party/blink/renderer/core/animation/length_property_functions.cc @@ -130,10 +130,10 @@ bool LengthPropertyFunctions::GetLength(const CSSProperty& property, result = style.Bottom(); return true; case CSSPropertyID::kCx: - result = style.SvgStyle().Cx(); + result = style.Cx(); return true; case CSSPropertyID::kCy: - result = style.SvgStyle().Cy(); + result = style.Cy(); return true; case CSSPropertyID::kFlexBasis: result = style.FlexBasis(); @@ -184,16 +184,16 @@ bool LengthPropertyFunctions::GetLength(const CSSProperty& property, result = style.PaddingTop(); return true; case CSSPropertyID::kR: - result = style.SvgStyle().R(); + result = style.R(); return true; case CSSPropertyID::kRight: result = style.Right(); return true; case CSSPropertyID::kRx: - result = style.SvgStyle().Rx(); + result = style.Rx(); return true; case CSSPropertyID::kRy: - result = style.SvgStyle().Ry(); + result = style.Ry(); return true; case CSSPropertyID::kShapeMargin: result = style.ShapeMargin(); @@ -223,10 +223,10 @@ bool LengthPropertyFunctions::GetLength(const CSSProperty& property, result = style.Width(); return true; case CSSPropertyID::kX: - result = style.SvgStyle().X(); + result = style.X(); return true; case CSSPropertyID::kY: - result = style.SvgStyle().Y(); + result = style.Y(); return true; case CSSPropertyID::kBorderBottomWidth: @@ -277,9 +277,9 @@ bool LengthPropertyFunctions::GetLength(const CSSProperty& property, return true; case CSSPropertyID::kBaselineShift: - if (style.BaselineShift() != BS_LENGTH) + if (style.BaselineShiftType() != EBaselineShiftType::kLength) return false; - result = style.BaselineShiftValue(); + result = style.BaselineShift(); return true; case CSSPropertyID::kLineHeight: // Percent Lengths are used to represent numbers on line-height. @@ -322,7 +322,8 @@ bool LengthPropertyFunctions::SetLength(const CSSProperty& property, switch (property.PropertyID()) { // Setters that take a Length value. case CSSPropertyID::kBaselineShift: - style.SetBaselineShiftValue(value); + style.SetBaselineShiftType(EBaselineShiftType::kLength); + style.SetBaselineShift(value); return true; case CSSPropertyID::kBottom: style.SetBottom(value); diff --git a/chromium/third_party/blink/renderer/core/animation/list_interpolation_functions.h b/chromium/third_party/blink/renderer/core/animation/list_interpolation_functions.h index 82d035e4a71..80ead64fe8e 100644 --- a/chromium/third_party/blink/renderer/core/animation/list_interpolation_functions.h +++ b/chromium/third_party/blink/renderer/core/animation/list_interpolation_functions.h @@ -81,7 +81,7 @@ class CORE_EXPORT ListInterpolationFunctions { const NonInterpolableValue* b); }; -class CORE_EXPORT NonInterpolableList : public NonInterpolableValue { +class CORE_EXPORT NonInterpolableList final : public NonInterpolableValue { public: ~NonInterpolableList() final = default; diff --git a/chromium/third_party/blink/renderer/core/animation/path_interpolation_functions.cc b/chromium/third_party/blink/renderer/core/animation/path_interpolation_functions.cc index f3d696942e3..a4596e6a2f9 100644 --- a/chromium/third_party/blink/renderer/core/animation/path_interpolation_functions.cc +++ b/chromium/third_party/blink/renderer/core/animation/path_interpolation_functions.cc @@ -100,7 +100,7 @@ InterpolationValue PathInterpolationFunctions::ConvertValue( path_seg_types, style_path->GetWindRule())); } -class UnderlyingPathSegTypesChecker +class UnderlyingPathSegTypesChecker final : public InterpolationType::ConversionChecker { public: ~UnderlyingPathSegTypesChecker() final = default; diff --git a/chromium/third_party/blink/renderer/core/animation/pending_animations.cc b/chromium/third_party/blink/renderer/core/animation/pending_animations.cc index 8b96385792b..0377a3a87be 100644 --- a/chromium/third_party/blink/renderer/core/animation/pending_animations.cc +++ b/chromium/third_party/blink/renderer/core/animation/pending_animations.cc @@ -72,27 +72,27 @@ bool PendingAnimations::Update( animation->HasActiveAnimationsOnCompositor(); // Animations with a start time do not participate in compositor start-time // grouping. - if (animation->PreCommit(animation->startTime() ? 1 : compositor_group, - paint_artifact_compositor, start_on_compositor)) { + if (animation->PreCommit( + animation->StartTimeInternal() ? 1 : compositor_group, + paint_artifact_compositor, start_on_compositor)) { if (animation->HasActiveAnimationsOnCompositor() && - !had_compositor_animation && !animation->startTime()) { + !had_compositor_animation && !animation->StartTimeInternal()) { started_synchronized_on_compositor = true; } if (!animation->timeline() || !animation->timeline()->IsActive()) continue; - if (animation->Playing() && !animation->startTime()) { + if (animation->Playing() && !animation->StartTimeInternal()) { waiting_for_start_time.push_back(animation.Get()); } else if (animation->PendingInternal()) { DCHECK(animation->timeline()->IsActive() && - animation->timeline()->CurrentTimeSeconds()); + animation->timeline()->CurrentTime()); // A pending animation that is not waiting on a start time does not need // to be synchronized with animations that are starting up. Nonetheless, // it needs to notify the animation to resolve the ready promise and // commit the pending state. - animation->NotifyReady( - animation->timeline()->CurrentTimeSeconds().value_or(0)); + animation->NotifyReady(animation->timeline()->CurrentTime().value()); } } else { deferred.push_back(animation); @@ -108,11 +108,10 @@ bool PendingAnimations::Update( waiting_for_start_time); } else { for (auto& animation : waiting_for_start_time) { - DCHECK(!animation->startTime()); + DCHECK(!animation->StartTimeInternal()); DCHECK(animation->timeline()->IsActive() && - animation->timeline()->CurrentTimeSeconds()); - animation->NotifyReady( - animation->timeline()->CurrentTimeSeconds().value_or(0)); + animation->timeline()->CurrentTime()); + animation->NotifyReady(animation->timeline()->CurrentTime().value()); } } @@ -155,7 +154,7 @@ void PendingAnimations::NotifyCompositorAnimationStarted( animations.swap(waiting_for_compositor_animation_start_); for (auto animation : animations) { - if (animation->startTime() || !animation->PendingInternal() || + if (animation->StartTimeInternal() || !animation->PendingInternal() || !animation->timeline() || !animation->timeline()->IsActive()) { // Already started or no longer relevant. continue; @@ -168,10 +167,11 @@ void PendingAnimations::NotifyCompositorAnimationStarted( if (animation->timeline() && !animation->timeline()->IsMonotonicallyIncreasing()) { animation->NotifyReady( - animation->timeline()->CurrentTimeSeconds().value_or(0)); + animation->timeline()->CurrentTime().value_or(AnimationTimeDelta())); } else { - animation->NotifyReady(monotonic_animation_start_time - - animation->timeline()->ZeroTimeInSeconds()); + animation->NotifyReady( + AnimationTimeDelta::FromSecondsD(monotonic_animation_start_time) - + animation->timeline()->ZeroTime()); } } } @@ -192,7 +192,7 @@ void PendingAnimations::FlushWaitingNonCompositedAnimations() { return; // Start any main thread animations that were scheduled to wait on - // compositor synchronization from a previous frame. Otherwise, an + // compositor synchronization from a previous frame. Otherwise, a // continuous influx of new composited animations could delay the start // of non-composited animations indefinitely (crbug.com/666710). HeapVector<Member<Animation>> animations; @@ -202,9 +202,8 @@ void PendingAnimations::FlushWaitingNonCompositedAnimations() { waiting_for_compositor_animation_start_.push_back(animation); } else { DCHECK(animation->timeline()->IsActive() && - animation->timeline()->CurrentTimeSeconds()); - animation->NotifyReady( - animation->timeline()->CurrentTimeSeconds().value_or(0)); + animation->timeline()->CurrentTime()); + animation->NotifyReady(animation->timeline()->CurrentTime().value()); } } } @@ -212,6 +211,7 @@ void PendingAnimations::FlushWaitingNonCompositedAnimations() { void PendingAnimations::Trace(Visitor* visitor) const { visitor->Trace(pending_); visitor->Trace(waiting_for_compositor_animation_start_); + visitor->Trace(timer_); } void PendingAnimations::TimerFired(TimerBase*) { diff --git a/chromium/third_party/blink/renderer/core/animation/pending_animations.h b/chromium/third_party/blink/renderer/core/animation/pending_animations.h index bc700e8b808..5a7c0f7deda 100644 --- a/chromium/third_party/blink/renderer/core/animation/pending_animations.h +++ b/chromium/third_party/blink/renderer/core/animation/pending_animations.h @@ -103,7 +103,7 @@ class CORE_EXPORT PendingAnimations final HeapVector<Member<Animation>> pending_; HeapVector<Member<Animation>> waiting_for_compositor_animation_start_; - TaskRunnerTimer<PendingAnimations> timer_; + HeapTaskRunnerTimer<PendingAnimations> timer_; int compositor_group_; bool inside_timer_fired_; }; diff --git a/chromium/third_party/blink/renderer/core/animation/scroll_timeline.cc b/chromium/third_party/blink/renderer/core/animation/scroll_timeline.cc index b260ad37970..259917a785b 100644 --- a/chromium/third_party/blink/renderer/core/animation/scroll_timeline.cc +++ b/chromium/third_party/blink/renderer/core/animation/scroll_timeline.cc @@ -138,17 +138,16 @@ ScrollTimeline* ScrollTimeline::Create(Document& document, return nullptr; } - HeapVector<Member<ScrollTimelineOffset>>* scroll_offsets = - MakeGarbageCollected<HeapVector<Member<ScrollTimelineOffset>>>(); + HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; if (options->scrollOffsets().IsEmpty()) { // TODO(crbug.com/1094014): scroll_offsets will replace start and end // offsets once spec decision on multiple scroll offsets is finalized. // https://github.com/w3c/csswg-drafts/issues/4912 if (!start_scroll_offset->IsDefaultValue()) - scroll_offsets->push_back(start_scroll_offset); + scroll_offsets.push_back(start_scroll_offset); if (!end_scroll_offset->IsDefaultValue() || !start_scroll_offset->IsDefaultValue()) - scroll_offsets->push_back(end_scroll_offset); + scroll_offsets.push_back(end_scroll_offset); } else { for (auto& offset : options->scrollOffsets()) { ScrollTimelineOffset* scroll_offset = @@ -159,13 +158,13 @@ ScrollTimeline* ScrollTimeline::Create(Document& document, } if (scroll_offset->IsDefaultValue() && (options->scrollOffsets().size() == 1 || - (scroll_offsets->size() + 1) < options->scrollOffsets().size())) { + (scroll_offsets.size() + 1) < options->scrollOffsets().size())) { exception_state.ThrowTypeError( "Invalid scrollOffsets: 'auto' can only be set as an end " "offset when start offset presents."); return nullptr; } - scroll_offsets->push_back(scroll_offset); + scroll_offsets.push_back(scroll_offset); } } @@ -182,15 +181,14 @@ ScrollTimeline::ScrollTimeline( Document* document, Element* scroll_source, ScrollDirection orientation, - HeapVector<Member<ScrollTimelineOffset>>* scroll_offsets, + HeapVector<Member<ScrollTimelineOffset>> scroll_offsets, base::Optional<double> time_range) : AnimationTimeline(document), scroll_source_(scroll_source), resolved_scroll_source_(ResolveScrollSource(scroll_source_)), orientation_(orientation), - scroll_offsets_(scroll_offsets), + scroll_offsets_(std::move(scroll_offsets)), time_range_(time_range) { - DCHECK(scroll_offsets_); if (resolved_scroll_source_) { ScrollTimelineSet& set = GetScrollTimelineSet(); if (!set.Contains(resolved_scroll_source_)) { @@ -222,14 +220,12 @@ bool ScrollTimeline::ComputeIsActive() const { ScrollTimelineOffset* ScrollTimeline::StartScrollOffset() const { // Single entry offset in scrollOffsets is considered as 'end'. Thus, // resolving start offset only if there is at least 2 offsets. - return scroll_offsets_ && scroll_offsets_->size() >= 2 - ? scroll_offsets_->at(0) - : nullptr; + return scroll_offsets_.size() >= 2 ? scroll_offsets_.at(0) : nullptr; } ScrollTimelineOffset* ScrollTimeline::EndScrollOffset() const { // End offset is always the last offset in scrollOffsets if exists. - return scroll_offsets_ && scroll_offsets_->size() >= 1 - ? scroll_offsets_->at(scroll_offsets_->size() - 1) + return scroll_offsets_.size() >= 1 + ? scroll_offsets_.at(scroll_offsets_.size() - 1) : nullptr; } @@ -255,16 +251,16 @@ bool ScrollTimeline::ResolveScrollOffsets( auto orientation = ToPhysicalScrollOrientation(orientation_, *layout_box); - if (scroll_offsets_->size() == 0) { + if (scroll_offsets_.size() == 0) { // Start and end offsets resolve to 'auto'. resolved_offsets.push_back(0); resolved_offsets.push_back(max_offset); return true; } // Single entry offset in scrollOffsets is considered as 'end'. - if (scroll_offsets_->size() == 1) + if (scroll_offsets_.size() == 1) resolved_offsets.push_back(0); - for (auto& offset : *scroll_offsets_) { + for (auto& offset : scroll_offsets_) { auto resolved_offset = offset->ResolveOffset( resolved_scroll_source_, orientation, max_offset, max_offset); if (!resolved_offset) { @@ -274,7 +270,6 @@ bool ScrollTimeline::ResolveScrollOffsets( } resolved_offsets.push_back(resolved_offset.value()); } - // TODO(crbug.com/1094014): Implement clamping for overlapping offsets. DCHECK_GE(resolved_offsets.size(), 2u); return true; } @@ -286,12 +281,11 @@ AnimationTimeline::PhaseAndTime ScrollTimeline::CurrentPhaseAndTime() { bool ScrollTimeline::ScrollOffsetsEqual( const HeapVector<Member<ScrollTimelineOffset>>& other) const { - DCHECK(scroll_offsets_); - if (scroll_offsets_->size() != other.size()) + if (scroll_offsets_.size() != other.size()) return false; - size_t size = scroll_offsets_->size(); + size_t size = scroll_offsets_.size(); for (size_t i = 0; i < size; ++i) { - if (!DataEquivalent(scroll_offsets_->at(i), other.at(i))) + if (!DataEquivalent(scroll_offsets_.at(i), other.at(i))) return false; } return true; @@ -477,11 +471,7 @@ void ScrollTimeline::endScrollOffset(ScrollTimelineOffsetValue& out) const { const HeapVector<ScrollTimelineOffsetValue> ScrollTimeline::scrollOffsets() const { HeapVector<ScrollTimelineOffsetValue> scroll_offsets; - - if (!scroll_offsets_) - return scroll_offsets; - - for (auto& offset : *scroll_offsets_) { + for (auto& offset : scroll_offsets_) { scroll_offsets.push_back(offset->ToScrollTimelineOffsetValue()); // 'auto' can only be the end offset. DCHECK(!offset->IsDefaultValue() || scroll_offsets.size() == 2); @@ -614,6 +604,14 @@ void ScrollTimeline::InvalidateEffectTargetStyle() { animation->InvalidateEffectTargetStyle(); } +void ScrollTimeline::ValidateState() { + auto state = ComputeTimelineState(); + if (timeline_state_snapshotted_ == state) + return; + timeline_state_snapshotted_ = state; + InvalidateEffectTargetStyle(); +} + CompositorAnimationTimeline* ScrollTimeline::EnsureCompositorTimeline() { if (compositor_timeline_) return compositor_timeline_.get(); diff --git a/chromium/third_party/blink/renderer/core/animation/scroll_timeline.h b/chromium/third_party/blink/renderer/core/animation/scroll_timeline.h index 019885d471d..445af19f636 100644 --- a/chromium/third_party/blink/renderer/core/animation/scroll_timeline.h +++ b/chromium/third_party/blink/renderer/core/animation/scroll_timeline.h @@ -44,7 +44,7 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { ScrollTimeline(Document*, Element*, ScrollDirection, - HeapVector<Member<ScrollTimelineOffset>>*, + HeapVector<Member<ScrollTimelineOffset>>, base::Optional<double>); bool IsScrollTimeline() const override { return true; } @@ -53,7 +53,7 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { // https://github.com/WICG/scroll-animations/issues/31 bool IsActive() const override; base::Optional<base::TimeDelta> InitialStartTimeForAnimations() override; - double ZeroTimeInSeconds() override { return 0; } + AnimationTimeDelta ZeroTime() override { return AnimationTimeDelta(); } void ServiceAnimations(TimingUpdateReason) override; void ScheduleNextService() override; @@ -95,6 +95,9 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { // for style recalc. void InvalidateEffectTargetStyle(); + // See DocumentAnimations::ValidateTimelines + void ValidateState(); + CompositorAnimationTimeline* EnsureCompositorTimeline() override; void UpdateCompositorTimeline() override; @@ -126,6 +129,7 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { size_t AttachedAnimationsCount() const { return scroll_animations_.size(); } private: + FRIEND_TEST_ALL_PREFIXES(ScrollTimelineTest, MultipleScrollOffsetsClamping); // https://wicg.github.io/scroll-animations/#avoiding-cycles // Snapshots scroll timeline current time and phase. // Called once per animation frame. @@ -166,7 +170,7 @@ class CORE_EXPORT ScrollTimeline : public AnimationTimeline { Member<Element> scroll_source_; Member<Node> resolved_scroll_source_; ScrollDirection orientation_; - Member<HeapVector<Member<ScrollTimelineOffset>>> scroll_offsets_; + HeapVector<Member<ScrollTimelineOffset>> scroll_offsets_; base::Optional<double> time_range_; diff --git a/chromium/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc b/chromium/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc index 7555b92eca1..6631be00e8e 100644 --- a/chromium/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc +++ b/chromium/third_party/blink/renderer/core/animation/scroll_timeline_offset.cc @@ -58,8 +58,11 @@ bool ElementBasedOffsetsEqual(ScrollTimelineElementBasedOffset* o1, return true; if (!o1 || !o2) return false; - return (o1->edge() == o2->edge()) && (o1->target() == o2->target()) && - (o1->threshold() == o2->threshold()); + // TODO(crbug.com/1070871): Use targetOr(nullptr) after migration is done. + Element* target_or_null1 = o1->hasTarget() ? o1->target() : nullptr; + Element* target_or_null2 = o2->hasTarget() ? o2->target() : nullptr; + return target_or_null1 == target_or_null2 && o1->edge() == o2->edge() && + o1->threshold() == o2->threshold(); } CSSKeywordValue* GetCSSKeywordValue(const ScrollTimelineOffsetValue& offset) { diff --git a/chromium/third_party/blink/renderer/core/animation/scroll_timeline_test.cc b/chromium/third_party/blink/renderer/core/animation/scroll_timeline_test.cc index 5321591f55a..ac3d1cd6811 100644 --- a/chromium/third_party/blink/renderer/core/animation/scroll_timeline_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/scroll_timeline_test.cc @@ -30,7 +30,7 @@ static constexpr double time_error_ms = 0.001 + 1e-13; #define EXPECT_TIME_NEAR(expected, value) \ EXPECT_NEAR(expected, value, time_error_ms) -HeapVector<Member<ScrollTimelineOffset>>* CreateScrollOffsets( +HeapVector<Member<ScrollTimelineOffset>> CreateScrollOffsets( ScrollTimelineOffset* start_scroll_offset = MakeGarbageCollected<ScrollTimelineOffset>( CSSNumericLiteralValue::Create( @@ -41,10 +41,9 @@ HeapVector<Member<ScrollTimelineOffset>>* CreateScrollOffsets( CSSNumericLiteralValue::Create( 90.0, CSSPrimitiveValue::UnitType::kPixels))) { - HeapVector<Member<ScrollTimelineOffset>>* scroll_offsets = - MakeGarbageCollected<HeapVector<Member<ScrollTimelineOffset>>>(); - scroll_offsets->push_back(start_scroll_offset); - scroll_offsets->push_back(end_scroll_offset); + HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; + scroll_offsets.push_back(start_scroll_offset); + scroll_offsets.push_back(end_scroll_offset); return scroll_offsets; } @@ -82,12 +81,12 @@ class TestScrollTimeline : public ScrollTimeline { public: TestScrollTimeline(Document* document, Element* scroll_source, - HeapVector<Member<ScrollTimelineOffset>>* scroll_offsets = + HeapVector<Member<ScrollTimelineOffset>> scroll_offsets = CreateScrollOffsets()) : ScrollTimeline(document, scroll_source, ScrollTimeline::Vertical, - scroll_offsets, + std::move(scroll_offsets), 100.0), next_service_scheduled_(false) {} @@ -801,7 +800,7 @@ TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) { auto* scroller = To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); ASSERT_TRUE(scroller); - ASSERT_TRUE(scroller->HasNonVisibleOverflow()); + ASSERT_TRUE(scroller->IsScrollContainer()); PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); ASSERT_TRUE(scrollable_area); double time_range = 100.0; @@ -879,4 +878,68 @@ TEST_F(ScrollTimelineTest, MultipleScrollOffsetsCurrentTimeCalculations) { EXPECT_EQ(100, scroll_timeline->CurrentTimeMilliseconds().value()); } +TEST_F(ScrollTimelineTest, OverlappingScrollOffsets) { + SetBodyInnerHTML(R"HTML( + <style> + #scroller { overflow: scroll; width: 100px; height: 100px; } + #spacer { height: 1000px; } + </style> + <div id='scroller'> + <div id ='spacer'></div> + </div> + )HTML"); + + auto* scroller = + To<LayoutBoxModelObject>(GetLayoutObjectByElementId("scroller")); + ASSERT_TRUE(scroller); + PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); + ASSERT_TRUE(scrollable_area); + double time_range = 100.0; + ScrollTimelineOptions* options = ScrollTimelineOptions::Create(); + options->setTimeRange( + DoubleOrScrollTimelineAutoKeyword::FromDouble(time_range)); + options->setScrollSource(GetElementById("scroller")); + HeapVector<ScrollTimelineOffsetValue> scroll_offsets = { + OffsetFromString("90px"), OffsetFromString("40px"), + OffsetFromString("10px")}; + options->setScrollOffsets(scroll_offsets); + + ScrollTimeline* scroll_timeline = + ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); + + scrollable_area->SetScrollOffset(ScrollOffset(0, 80), + mojom::blink::ScrollType::kProgrammatic); + SimulateFrame(); + EXPECT_EQ(0, scroll_timeline->CurrentTimeMilliseconds().value()); + + scrollable_area->SetScrollOffset(ScrollOffset(0, 95), + mojom::blink::ScrollType::kProgrammatic); + SimulateFrame(); + EXPECT_EQ(100, scroll_timeline->CurrentTimeMilliseconds().value()); + + scroll_offsets = {OffsetFromString("0px"), OffsetFromString("100px"), + OffsetFromString("50px")}; + options->setScrollOffsets(scroll_offsets); + + scroll_timeline = + ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); + + scrollable_area->SetScrollOffset(ScrollOffset(0, 40), + mojom::blink::ScrollType::kProgrammatic); + SimulateFrame(); + EXPECT_EQ(20, scroll_timeline->CurrentTimeMilliseconds().value()); + + scroll_offsets = {OffsetFromString("50px"), OffsetFromString("0px"), + OffsetFromString("100px")}; + options->setScrollOffsets(scroll_offsets); + + scroll_timeline = + ScrollTimeline::Create(GetDocument(), options, ASSERT_NO_EXCEPTION); + + scrollable_area->SetScrollOffset(ScrollOffset(0, 60), + mojom::blink::ScrollType::kProgrammatic); + SimulateFrame(); + EXPECT_EQ(80, scroll_timeline->CurrentTimeMilliseconds().value()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc b/chromium/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc index 9fe7b941f90..3e3a139d403 100644 --- a/chromium/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/scroll_timeline_util_test.cc @@ -16,13 +16,12 @@ namespace blink { namespace { -HeapVector<Member<ScrollTimelineOffset>>* CreateScrollOffsets( +HeapVector<Member<ScrollTimelineOffset>> CreateScrollOffsets( ScrollTimelineOffset* start_scroll_offset, ScrollTimelineOffset* end_scroll_offset) { - HeapVector<Member<ScrollTimelineOffset>>* scroll_offsets = - MakeGarbageCollected<HeapVector<Member<ScrollTimelineOffset>>>(); - scroll_offsets->push_back(start_scroll_offset); - scroll_offsets->push_back(end_scroll_offset); + HeapVector<Member<ScrollTimelineOffset>> scroll_offsets; + scroll_offsets.push_back(start_scroll_offset); + scroll_offsets.push_back(end_scroll_offset); return scroll_offsets; } diff --git a/chromium/third_party/blink/renderer/core/animation/string_keyframe.cc b/chromium/third_party/blink/renderer/core/animation/string_keyframe.cc index 0b2840a787d..4503d3bb652 100644 --- a/chromium/third_party/blink/renderer/core/animation/string_keyframe.cc +++ b/chromium/third_party/blink/renderer/core/animation/string_keyframe.cc @@ -340,7 +340,7 @@ bool StringKeyframe::CSSPropertySpecificKeyframe:: const ComputedStyle* parent_style) const { compositor_keyframe_value_cache_ = StyleResolver::CreateCompositorKeyframeValueSnapshot( - element, base_style, parent_style, property, value_.Get()); + element, base_style, parent_style, property, value_.Get(), offset_); return true; } diff --git a/chromium/third_party/blink/renderer/core/animation/svg_path_interpolation_type.cc b/chromium/third_party/blink/renderer/core/animation/svg_path_interpolation_type.cc index 9c256e7127d..2162168aa4d 100644 --- a/chromium/third_party/blink/renderer/core/animation/svg_path_interpolation_type.cc +++ b/chromium/third_party/blink/renderer/core/animation/svg_path_interpolation_type.cc @@ -47,7 +47,7 @@ SVGPropertyBase* SVGPathInterpolationType::AppliedSVGValue( const InterpolableValue& interpolable_value, const NonInterpolableValue* non_interpolable_value) const { return MakeGarbageCollected<SVGPath>( - MakeGarbageCollected<cssvalue::CSSPathValue>( + *MakeGarbageCollected<cssvalue::CSSPathValue>( PathInterpolationFunctions::AppliedValue(interpolable_value, non_interpolable_value))); } diff --git a/chromium/third_party/blink/renderer/core/animation/timing.cc b/chromium/third_party/blink/renderer/core/animation/timing.cc index 5a6d39467f4..8d57548ca6c 100644 --- a/chromium/third_party/blink/renderer/core/animation/timing.cc +++ b/chromium/third_party/blink/renderer/core/animation/timing.cc @@ -121,7 +121,7 @@ ComputedEffectTiming* Timing::getComputedTiming( computed_timing->setLocalTime( CSSNumberish::FromDouble(calculated_timing.local_time.value() * 1000)); } else { - computed_timing->setLocalTimeToNull(); + computed_timing->setLocalTime(CSSNumberish()); } if (calculated_timing.is_in_effect) { diff --git a/chromium/third_party/blink/renderer/core/animation/timing_calculations.h b/chromium/third_party/blink/renderer/core/animation/timing_calculations.h index b1ca293f721..2173cc75e7a 100644 --- a/chromium/third_party/blink/renderer/core/animation/timing_calculations.h +++ b/chromium/third_party/blink/renderer/core/animation/timing_calculations.h @@ -56,10 +56,14 @@ static inline bool IsWithinAnimationTimeEpsilon(double a, double b) { return std::abs(a - b) <= TimingCalculationEpsilon(); } -inline bool LessThanOrEqualToWithinEpsilon(double a, double b) { +static inline bool LessThanOrEqualToWithinEpsilon(double a, double b) { return a <= b + TimingCalculationEpsilon(); } +static inline bool GreaterThanOrEqualToWithinEpsilon(double a, double b) { + return a >= b - TimingCalculationEpsilon(); +} + static inline double MultiplyZeroAlwaysGivesZero(double x, double y) { DCHECK(!std::isnan(x)); DCHECK(!std::isnan(y)); diff --git a/chromium/third_party/blink/renderer/core/animation/timing_input_test.cc b/chromium/third_party/blink/renderer/core/animation/timing_input_test.cc index f5b7c8f7498..98834571a87 100644 --- a/chromium/third_party/blink/renderer/core/animation/timing_input_test.cc +++ b/chromium/third_party/blink/renderer/core/animation/timing_input_test.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/animation/timing_input.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.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_binding_for_testing.h" diff --git a/chromium/third_party/blink/renderer/core/animation/transition_keyframe.h b/chromium/third_party/blink/renderer/core/animation/transition_keyframe.h index e8c75ae0538..a5b39a1c6a0 100644 --- a/chromium/third_party/blink/renderer/core/animation/transition_keyframe.h +++ b/chromium/third_party/blink/renderer/core/animation/transition_keyframe.h @@ -78,6 +78,8 @@ class CORE_EXPORT TransitionKeyframe : public Keyframe { bool IsTransitionPropertySpecificKeyframe() const final { return true; } + const TypedInterpolationValue* GetValue() const { return value_.get(); } + void Trace(Visitor*) const override; private: diff --git a/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.cc b/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.cc index fd899c25b55..0ab3b9a41db 100644 --- a/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.cc +++ b/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.cc @@ -94,19 +94,17 @@ void WorkletAnimationController::ScrollSourceCompositingStateChanged( base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> WorkletAnimationController::EnsureMainThreadMutatorDispatcher( - scoped_refptr<base::SingleThreadTaskRunner>* mutator_task_runner) { + scoped_refptr<base::SingleThreadTaskRunner> mutator_task_runner) { base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> mutator_dispatcher; - if (!mutator_task_runner_) { + if (!main_thread_mutator_client_) { main_thread_mutator_client_ = AnimationWorkletMutatorDispatcherImpl::CreateMainThreadClient( - &mutator_dispatcher, &mutator_task_runner_); + mutator_dispatcher, std::move(mutator_task_runner)); main_thread_mutator_client_->SetDelegate(this); } DCHECK(main_thread_mutator_client_); - DCHECK(mutator_task_runner_); DCHECK(mutator_dispatcher); - *mutator_task_runner = mutator_task_runner_; return mutator_dispatcher; } diff --git a/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.h b/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.h index b9f5cad9e1d..b365819b6da 100644 --- a/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.h +++ b/chromium/third_party/blink/renderer/core/animation/worklet_animation_controller.h @@ -20,6 +20,7 @@ namespace blink { class AnimationWorkletMutatorDispatcherImpl; class Document; class MainThreadMutatorClient; +class Node; class WorkletAnimationBase; // Handles AnimationWorklet animations on the main-thread. @@ -36,7 +37,7 @@ class CORE_EXPORT WorkletAnimationController : public GarbageCollected<WorkletAnimationController>, public MutatorClient { public: - WorkletAnimationController(Document*); + explicit WorkletAnimationController(Document*); ~WorkletAnimationController() override; void AttachAnimation(WorkletAnimationBase&); @@ -54,7 +55,7 @@ class CORE_EXPORT WorkletAnimationController base::WeakPtr<AnimationWorkletMutatorDispatcherImpl> EnsureMainThreadMutatorDispatcher( - scoped_refptr<base::SingleThreadTaskRunner>* mutator_task_runner); + scoped_refptr<base::SingleThreadTaskRunner> mutator_task_runner); void SetMutationUpdate( std::unique_ptr<AnimationWorkletOutput> output) override; @@ -79,7 +80,6 @@ class CORE_EXPORT WorkletAnimationController // TODO(crbug.com/1090515): The following proxy is needed for platform/ to // access this class. We should bypass it eventually. std::unique_ptr<MainThreadMutatorClient> main_thread_mutator_client_; - scoped_refptr<base::SingleThreadTaskRunner> mutator_task_runner_; Member<Document> document_; }; |