Bug 1814786 - Part 6: Create timeline objects when mutating scroll-timeline property. r=emilio

And so we can lookup the timeline from TimelineCollection.

Differential Revision: https://phabricator.services.mozilla.com/D169273
This commit is contained in:
Boris Chiou 2023-03-07 23:57:55 +00:00
parent 95a2a27769
commit 4fa8c3ce41
10 changed files with 103 additions and 21 deletions

View File

@ -63,6 +63,7 @@
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoTraversalStatistics.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimelineManager.h"
#include "mozilla/RWLock.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
@ -508,6 +509,18 @@ bool Gecko_StyleAnimationsEquals(const nsStyleAutoArray<StyleAnimation>* aA,
return *aA == *aB;
}
bool Gecko_StyleScrollTimelinesEquals(
const nsStyleAutoArray<StyleScrollTimeline>* aA,
const nsStyleAutoArray<StyleScrollTimeline>* aB) {
return *aA == *aB;
}
bool Gecko_StyleViewTimelinesEquals(
const nsStyleAutoArray<StyleViewTimeline>* aA,
const nsStyleAutoArray<StyleViewTimeline>* aB) {
return *aA == *aB;
}
void Gecko_CopyAnimationNames(nsStyleAutoArray<StyleAnimation>* aDest,
const nsStyleAutoArray<StyleAnimation>* aSrc) {
size_t srcLength = aSrc->Length();
@ -544,7 +557,17 @@ void Gecko_UpdateAnimations(const Element* aElement,
const auto [element, pseudoType] =
AnimationUtils::GetElementPseudoPair(aElement);
// TODO: call UpdateTimelines() in the next patch.
// Handle scroll/view timelines first because CSS animations may refer to the
// timeline defined by itself.
if (aTasks & UpdateAnimationsTasks::ScrollTimelines) {
presContext->TimelineManager()->UpdateTimelines(
const_cast<Element*>(element), pseudoType, aComputedData,
TimelineManager::ProgressTimelineType::Scroll);
}
if (aTasks & UpdateAnimationsTasks::ViewTimelines) {
// TODO: Bug 1737920. Add support for view timelines.
}
if (aTasks & UpdateAnimationsTasks::CSSAnimations) {
presContext->AnimationManager()->UpdateAnimations(

View File

@ -216,6 +216,14 @@ bool Gecko_StyleAnimationsEquals(
const nsStyleAutoArray<mozilla::StyleAnimation>*,
const nsStyleAutoArray<mozilla::StyleAnimation>*);
bool Gecko_StyleScrollTimelinesEquals(
const nsStyleAutoArray<mozilla::StyleScrollTimeline>*,
const nsStyleAutoArray<mozilla::StyleScrollTimeline>*);
bool Gecko_StyleViewTimelinesEquals(
const nsStyleAutoArray<mozilla::StyleViewTimeline>*,
const nsStyleAutoArray<mozilla::StyleViewTimeline>*);
void Gecko_CopyAnimationNames(
nsStyleAutoArray<mozilla::StyleAnimation>* aDest,
const nsStyleAutoArray<mozilla::StyleAnimation>* aSrc);

View File

@ -75,6 +75,8 @@ enum class UpdateAnimationsTasks : uint8_t {
EffectProperties = 1 << 2,
CascadeResults = 1 << 3,
DisplayChangedFromNone = 1 << 4,
ScrollTimelines = 1 << 5,
ViewTimelines = 1 << 6,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(UpdateAnimationsTasks)

View File

@ -38,6 +38,10 @@ class TimelineCollection final
~TimelineCollection();
already_AddRefed<TimelineType> Lookup(const nsAtom* aName) const {
return mTimelines.Get(aName).forget();
}
already_AddRefed<TimelineType> Extract(const nsAtom* aName) {
Maybe<RefPtr<TimelineType>> timeline = mTimelines.Extract(aName);
return timeline ? timeline->forget() : nullptr;

View File

@ -9,10 +9,12 @@
#include "nsTransitionManager.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/ElementAnimationData.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/TimelineCollection.h"
#include "mozilla/dom/AnimationEffect.h"
#include "mozilla/dom/DocumentTimeline.h"
#include "mozilla/dom/KeyframeEffect.h"
@ -213,31 +215,23 @@ static already_AddRefed<dom::AnimationTimeline> GetNamedProgressTimeline(
// 2. that elements descendants
// 3. that elements following siblings and their descendants
// https://drafts.csswg.org/scroll-animations-1/#timeline-scope
for (Element* curr = aTarget.mElement; curr;
curr = curr->GetParentElement()) {
for (Element* curr = AnimationUtils::GetElementForRestyle(
aTarget.mElement, aTarget.mPseudoType);
curr; curr = curr->GetParentElement()) {
// If multiple elements have declared the same timeline name, the matching
// timeline is the one declared on the nearest element in tree order, which
// considers siblings closer than parents.
// Note: This is fine for parallel traversal because we update animations by
// SequentialTask.
for (Element* e = curr; e; e = e->GetPreviousElementSibling()) {
// TODO: Look up the named timelines from the corresponding
// TimelineCollection in the next patch.
const ComputedStyle* style = Servo_Element_GetMaybeOutOfDateStyle(e);
// The elements in the shadow dom might not be in the flat tree.
if (!style) {
continue;
}
const nsStyleUIReset* ui = style->StyleUIReset();
// In case of a name conflict on the same element, scroll progress
// timelines take precedence over view progress timelines.
// TODO: Will reuse named progress timelines in the following patches.
for (uint32_t i = 0; i < ui->mScrollTimelineNameCount; ++i) {
const auto& timeline = ui->mScrollTimelines[i];
if (timeline.GetName() == aName) {
return ScrollTimeline::MakeNamed(aDocument, e, timeline);
const auto [element, pseudoType] =
AnimationUtils::GetElementPseudoPair(e);
if (auto* collection =
TimelineCollection<ScrollTimeline>::Get(element, pseudoType)) {
if (RefPtr<ScrollTimeline> timeline = collection->Lookup(aName)) {
return timeline.forget();
}
}

View File

@ -419,6 +419,10 @@ bitflags! {
/// the second animation restyles for the script animations in the case where
/// the display property was changed from 'none' to others.
const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
/// Update CSS named scroll progress timelines.
const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
/// Update CSS named view progress timelines.
const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
}
}

View File

@ -418,6 +418,21 @@ trait PrivateMatchMethods: TElement {
// in addition to the unvisited styles.
let mut tasks = UpdateAnimationsTasks::empty();
if old_values.as_deref().map_or_else(
|| new_values.get_ui().specifies_scroll_timelines(),
|old| !old.get_ui().scroll_timelines_equals(new_values.get_ui()),
) {
tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
}
if old_values.as_deref().map_or_else(
|| new_values.get_ui().specifies_view_timelines(),
|old| !old.get_ui().view_timelines_equals(new_values.get_ui()),
) {
tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
}
if self.needs_animations_update(
context,
old_values.as_deref(),

View File

@ -1907,9 +1907,32 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-
${impl_coordinated_property('scroll_timeline', 'name', 'Name')}
${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}
pub fn scroll_timelines_equals(&self, other: &Self) -> bool {
self.gecko.mScrollTimelineNameCount == other.gecko.mScrollTimelineNameCount
&& self.gecko.mScrollTimelineAxisCount == other.gecko.mScrollTimelineAxisCount
&& unsafe {
bindings::Gecko_StyleScrollTimelinesEquals(
&self.gecko.mScrollTimelines,
&other.gecko.mScrollTimelines,
)
}
}
${impl_coordinated_property('view_timeline', 'name', 'Name')}
${impl_coordinated_property('view_timeline', 'axis', 'Axis')}
${impl_coordinated_property('view_timeline', 'inset', 'Inset')}
pub fn view_timelines_equals(&self, other: &Self) -> bool {
self.gecko.mViewTimelineNameCount == other.gecko.mViewTimelineNameCount
&& self.gecko.mViewTimelineAxisCount == other.gecko.mViewTimelineAxisCount
&& self.gecko.mViewTimelineInsetCount == other.gecko.mViewTimelineInsetCount
&& unsafe {
bindings::Gecko_StyleViewTimelinesEquals(
&self.gecko.mViewTimelines,
&other.gecko.mViewTimelines,
)
}
}
</%self:impl_trait>
<%self:impl_trait style_struct_name="XUL">

View File

@ -2953,6 +2953,18 @@ pub mod style_structs {
})
}
/// Returns whether there is any named progress timeline specified with
/// scroll-timeline-name other than `none`.
pub fn specifies_scroll_timelines(&self) -> bool {
self.scroll_timeline_name_iter().any(|name| !name.is_none())
}
/// Returns whether there is any named progress timeline specified with
/// view-timeline-name other than `none`.
pub fn specifies_view_timelines(&self) -> bool {
self.view_timeline_name_iter().any(|name| !name.is_none())
}
/// Returns true if animation properties are equal between styles, but without
/// considering keyframe data and animation-timeline.
#[cfg(feature = "servo")]

View File

@ -33,6 +33,3 @@
[Timeline lookup updates candidate when match becomes available.]
expected: FAIL
[scroll-timeline-axis is mutated]
expected: FAIL