2021-12-08 04:16:28 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "ScrollTimeline.h"
|
|
|
|
|
|
|
|
#include "mozilla/dom/Animation.h"
|
2021-12-08 04:16:31 +03:00
|
|
|
#include "mozilla/AnimationTarget.h"
|
2022-04-05 21:48:17 +03:00
|
|
|
#include "mozilla/DisplayPortUtils.h"
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
#include "mozilla/PresShell.h"
|
2021-12-08 04:16:29 +03:00
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsIScrollableFrame.h"
|
|
|
|
#include "nsLayoutUtils.h"
|
2021-12-08 04:16:28 +03:00
|
|
|
|
2021-12-08 04:16:29 +03:00
|
|
|
#define SCROLL_TIMELINE_DURATION_MILLISEC 100000
|
|
|
|
|
2021-12-08 04:16:28 +03:00
|
|
|
namespace mozilla::dom {
|
|
|
|
|
2021-12-08 04:16:28 +03:00
|
|
|
// ---------------------------------
|
|
|
|
// Methods of ScrollTimeline
|
|
|
|
// ---------------------------------
|
|
|
|
|
2021-12-08 04:16:28 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(ScrollTimeline)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScrollTimeline,
|
|
|
|
AnimationTimeline)
|
2021-12-08 04:16:28 +03:00
|
|
|
tmp->Teardown();
|
2021-12-08 04:16:28 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource.mElement)
|
2021-12-08 04:16:28 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScrollTimeline,
|
|
|
|
AnimationTimeline)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource.mElement)
|
2021-12-08 04:16:28 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ScrollTimeline,
|
|
|
|
AnimationTimeline)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ScrollTimeline,
|
|
|
|
AnimationTimeline)
|
|
|
|
|
2021-12-08 04:16:29 +03:00
|
|
|
TimingParams ScrollTimeline::sTiming;
|
|
|
|
|
2022-02-08 21:35:23 +03:00
|
|
|
ScrollTimeline::ScrollTimeline(Document* aDocument, const Scroller& aScroller,
|
|
|
|
StyleScrollDirection aDirection)
|
2021-12-08 04:16:28 +03:00
|
|
|
: AnimationTimeline(aDocument->GetParentObject()),
|
|
|
|
mDocument(aDocument),
|
2021-12-08 04:16:28 +03:00
|
|
|
// FIXME: Bug 1737918: We may have to udpate the constructor arguments
|
|
|
|
// because this can be nearest, root, or a specific container. For now,
|
|
|
|
// the input is a source element directly and it is the root element.
|
2021-12-08 04:16:28 +03:00
|
|
|
mSource(aScroller),
|
2022-02-08 21:35:23 +03:00
|
|
|
mDirection(aDirection) {
|
2021-12-08 04:16:28 +03:00
|
|
|
MOZ_ASSERT(aDocument);
|
|
|
|
|
2021-12-08 04:16:29 +03:00
|
|
|
// Use default values except for |mDuration| and |mFill|.
|
|
|
|
// Use a fixed duration defined in SCROLL_TIMELINE_DURATIONMILLISEC, and use
|
|
|
|
// FillMode::Both to make sure the animation is in effect at 100%.
|
2021-12-15 12:54:42 +03:00
|
|
|
sTiming = TimingParams(SCROLL_TIMELINE_DURATION_MILLISEC, 0.0,
|
|
|
|
std::numeric_limits<float>::infinity(),
|
|
|
|
PlaybackDirection::Alternate, FillMode::Both);
|
2021-12-08 04:16:28 +03:00
|
|
|
}
|
|
|
|
|
2021-12-08 04:16:31 +03:00
|
|
|
already_AddRefed<ScrollTimeline> ScrollTimeline::FromRule(
|
|
|
|
const RawServoScrollTimelineRule& aRule, Document* aDocument,
|
|
|
|
const NonOwningAnimationTarget& aTarget) {
|
|
|
|
// Note: If the rules changes after we build the scroll-timeline rule, we
|
2022-02-08 21:35:23 +03:00
|
|
|
// rebuild all CSS animtions, and then try to look up the scroll-timeline by
|
|
|
|
// the new source and the new direction. If we cannot find a specific
|
|
|
|
// timeline, we create one, and the unused scroll-timeline object will be
|
|
|
|
// dropped automatically becuase no animation owns it and its ref-count
|
|
|
|
// becomes zero.
|
|
|
|
|
|
|
|
StyleScrollDirection direction =
|
|
|
|
Servo_ScrollTimelineRule_GetOrientation(&aRule);
|
|
|
|
|
|
|
|
// FIXME: Bug 1737918: applying new spec update, e.g. other scrollers and
|
|
|
|
// other style values.
|
|
|
|
RefPtr<ScrollTimeline> timeline;
|
|
|
|
auto autoScroller = Scroller::Auto(aTarget.mElement->OwnerDoc());
|
|
|
|
auto* set =
|
|
|
|
ScrollTimelineSet::GetOrCreateScrollTimelineSet(autoScroller.mElement);
|
|
|
|
auto p = set->LookupForAdd(direction);
|
|
|
|
if (!p) {
|
|
|
|
timeline = new ScrollTimeline(aDocument, autoScroller, direction);
|
|
|
|
set->Add(p, direction, timeline);
|
|
|
|
} else {
|
|
|
|
timeline = p->value();
|
|
|
|
}
|
2021-12-08 04:16:31 +03:00
|
|
|
return timeline.forget();
|
|
|
|
}
|
|
|
|
|
2021-12-08 04:16:29 +03:00
|
|
|
Nullable<TimeDuration> ScrollTimeline::GetCurrentTimeAsDuration() const {
|
2022-04-05 21:48:16 +03:00
|
|
|
// If no layout box, this timeline is inactive.
|
|
|
|
if (!mSource || !mSource.mElement->GetPrimaryFrame()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if this is not a scroller container, this timeline is inactive.
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
const nsIScrollableFrame* scrollFrame = GetScrollFrame();
|
2022-04-05 21:48:16 +03:00
|
|
|
if (!scrollFrame) {
|
2021-12-08 04:16:29 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-04-05 21:48:16 +03:00
|
|
|
const auto orientation = Axis();
|
|
|
|
|
2021-12-08 04:16:29 +03:00
|
|
|
// If this orientation is not ready for scrolling (i.e. the scroll range is
|
|
|
|
// not larger than or equal to one device pixel), we make it 100%.
|
|
|
|
if (!scrollFrame->GetAvailableScrollingDirections().contains(orientation)) {
|
|
|
|
return TimeDuration::FromMilliseconds(SCROLL_TIMELINE_DURATION_MILLISEC);
|
|
|
|
}
|
|
|
|
|
|
|
|
const nsPoint& scrollOffset = scrollFrame->GetScrollPosition();
|
|
|
|
const nsRect& scrollRange = scrollFrame->GetScrollRange();
|
|
|
|
const bool isHorizontal = orientation == layers::ScrollDirection::eHorizontal;
|
|
|
|
|
|
|
|
// Note: For RTL, scrollOffset.x or scrollOffset.y may be negative, e.g. the
|
|
|
|
// range of its value is [0, -range], so we have to use the absolute value.
|
|
|
|
double position = std::abs(isHorizontal ? scrollOffset.x : scrollOffset.y);
|
|
|
|
double range = isHorizontal ? scrollRange.width : scrollRange.height;
|
|
|
|
MOZ_ASSERT(range > 0.0);
|
|
|
|
// Use the definition of interval progress to compute the progress.
|
|
|
|
// Note: We simplify the scroll offsets to [0%, 100%], so offset weight and
|
|
|
|
// offset index are ignored here.
|
|
|
|
// https://drafts.csswg.org/scroll-animations-1/#progress-calculation-algorithm
|
|
|
|
double progress = position / range;
|
|
|
|
return TimeDuration::FromMilliseconds(progress *
|
|
|
|
SCROLL_TIMELINE_DURATION_MILLISEC);
|
|
|
|
}
|
|
|
|
|
2022-04-05 21:48:16 +03:00
|
|
|
layers::ScrollDirection ScrollTimeline::Axis() const {
|
|
|
|
MOZ_ASSERT(mSource && mSource.mElement->GetPrimaryFrame());
|
|
|
|
|
|
|
|
const WritingMode wm = mSource.mElement->GetPrimaryFrame()->GetWritingMode();
|
|
|
|
return mDirection == StyleScrollDirection::Horizontal ||
|
|
|
|
(!wm.IsVertical() &&
|
|
|
|
mDirection == StyleScrollDirection::Inline) ||
|
|
|
|
(wm.IsVertical() &&
|
|
|
|
(mDirection == StyleScrollDirection::Block ||
|
|
|
|
mDirection == StyleScrollDirection::Auto))
|
|
|
|
? layers::ScrollDirection::eHorizontal
|
|
|
|
: layers::ScrollDirection::eVertical;
|
|
|
|
}
|
|
|
|
|
2022-04-05 21:48:17 +03:00
|
|
|
StyleOverflow ScrollTimeline::SourceScrollStyle() const {
|
|
|
|
MOZ_ASSERT(mSource && mSource.mElement->GetPrimaryFrame());
|
|
|
|
|
|
|
|
const nsIScrollableFrame* scrollFrame = GetScrollFrame();
|
|
|
|
MOZ_ASSERT(scrollFrame);
|
|
|
|
|
|
|
|
const ScrollStyles scrollStyles = scrollFrame->GetScrollStyles();
|
|
|
|
|
|
|
|
return Axis() == layers::ScrollDirection::eHorizontal
|
|
|
|
? scrollStyles.mHorizontal
|
|
|
|
: scrollStyles.mVertical;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScrollTimeline::APZIsActiveForSource() const {
|
|
|
|
MOZ_ASSERT(mSource);
|
|
|
|
return gfxPlatform::AsyncPanZoomEnabled() &&
|
|
|
|
!nsLayoutUtils::ShouldDisableApzForElement(mSource.mElement) &&
|
|
|
|
DisplayPortUtils::HasNonMinimalNonZeroDisplayPort(mSource.mElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScrollTimeline::ScrollingDirectionIsAvailable() const {
|
|
|
|
const nsIScrollableFrame* scrollFrame = GetScrollFrame();
|
|
|
|
MOZ_ASSERT(scrollFrame);
|
|
|
|
return scrollFrame->GetAvailableScrollingDirections().contains(Axis());
|
|
|
|
}
|
|
|
|
|
2021-12-08 04:16:28 +03:00
|
|
|
void ScrollTimeline::UnregisterFromScrollSource() {
|
|
|
|
if (!mSource) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ScrollTimelineSet* scrollTimelineSet =
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
ScrollTimelineSet::GetScrollTimelineSet(mSource.mElement)) {
|
2022-02-08 21:35:23 +03:00
|
|
|
scrollTimelineSet->Remove(mDirection);
|
2021-12-08 04:16:28 +03:00
|
|
|
if (scrollTimelineSet->IsEmpty()) {
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
ScrollTimelineSet::DestroyScrollTimelineSet(mSource.mElement);
|
2021-12-08 04:16:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
const nsIScrollableFrame* ScrollTimeline::GetScrollFrame() const {
|
|
|
|
if (!mSource) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mSource.mType) {
|
|
|
|
case Scroller::Type::Auto:
|
|
|
|
if (const PresShell* presShell =
|
|
|
|
mSource.mElement->OwnerDoc()->GetPresShell()) {
|
|
|
|
return presShell->GetRootScrollFrameAsScrollable();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Scroller::Type::Other:
|
|
|
|
default:
|
|
|
|
return nsLayoutUtils::FindScrollableFrameFor(mSource.mElement);
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-12-08 04:16:28 +03:00
|
|
|
// ---------------------------------
|
|
|
|
// Methods of ScrollTimelineSet
|
|
|
|
// ---------------------------------
|
|
|
|
|
|
|
|
/* static */ ScrollTimelineSet* ScrollTimelineSet::GetScrollTimelineSet(
|
|
|
|
Element* aElement) {
|
Bug 1676791 - Part 9: Define Scroller to handle the source of ScrollTimeline. r=emilio
Per spec, "auto" represents the scrolling element of the document.
However, the scrolling element might be changed, based on the layout, in
quirks mode. Besides, the content of the root scroll frame is the root
element, instead of the scrolling element (e.g. body element) in both
standard and quirks modes. So now we define a special type, Scroller, to
represent the source of scroll-timeline, and use its |mType| to decide
which scroll frame we would like to use. In addition, hope this change let
us easier to implement nearest scroller.
Note: for auto scroller, we register this ScrollTimeline to the root
element, in both modes. Once we expose ScrollTimeline interface to the
script, we can rely on the |mType| of Scroller to return the correct
source element, whether it is scrolling element or not.
Differential Revision: https://phabricator.services.mozilla.com/D131578
2021-12-08 04:16:31 +03:00
|
|
|
return aElement ? static_cast<ScrollTimelineSet*>(aElement->GetProperty(
|
|
|
|
nsGkAtoms::scrollTimelinesProperty))
|
|
|
|
: nullptr;
|
2021-12-08 04:16:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ ScrollTimelineSet* ScrollTimelineSet::GetOrCreateScrollTimelineSet(
|
|
|
|
Element* aElement) {
|
|
|
|
MOZ_ASSERT(aElement);
|
|
|
|
ScrollTimelineSet* scrollTimelineSet = GetScrollTimelineSet(aElement);
|
|
|
|
if (scrollTimelineSet) {
|
|
|
|
return scrollTimelineSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
scrollTimelineSet = new ScrollTimelineSet();
|
|
|
|
nsresult rv = aElement->SetProperty(
|
|
|
|
nsGkAtoms::scrollTimelinesProperty, scrollTimelineSet,
|
|
|
|
nsINode::DeleteProperty<ScrollTimelineSet>, true);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_WARNING("SetProperty failed");
|
|
|
|
delete scrollTimelineSet;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return scrollTimelineSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ void ScrollTimelineSet::DestroyScrollTimelineSet(
|
|
|
|
Element* aElement) {
|
|
|
|
aElement->RemoveProperty(nsGkAtoms::scrollTimelinesProperty);
|
2021-12-08 04:16:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mozilla::dom
|