Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ScrollTimeline.cpp

  Sprache: C
 

/* 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/AlreadyAddRefed.h"
#include "mozilla/AnimationTarget.h"
#include "mozilla/DisplayPortUtils.h"
#include "mozilla/ElementAnimationData.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/dom/AnimationTimelinesController.h"
#include "mozilla/dom/CSSNumericValueBinding.h"
#include "mozilla/dom/CSSUnitValue.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/ElementInlines.h"
#include "mozilla/dom/ScrollTimelineBinding.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "nsRefreshDriver.h"

namespace mozilla::dom {

// ---------------------------------
// Methods of ScrollTimeline
// ---------------------------------

NS_IMPL_CYCLE_COLLECTION_CLASS(ScrollTimeline)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScrollTimeline,
                                                AnimationTimeline)
  tmp->Teardown();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollerInfo.ElementForCycleCollection())
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ScrollTimeline,
                                                  AnimationTimeline)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollerInfo.ElementForCycleCollection())
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ScrollTimeline,
                                               AnimationTimeline)

JSObject* ScrollTimeline::WrapObject(JSContext* aCx,
                                     JS::Handle<JSObject*> aGivenProto) {
  return ScrollTimeline_Binding::Wrap(aCx, this, aGivenProto);
}

/* static */
already_AddRefed<ScrollTimeline> ScrollTimeline::Constructor(
    const GlobalObject& aGlobal, const ScrollTimelineOptions& aOptions,
    ErrorResult& aRv) {
  RefPtr<Document> doc =
      AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
  if (!doc) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  // Implements
  // <https://drafts.csswg.org/scroll-animations-1/#dom-scrolltimeline-scrolltimeline>

  // Step 2 -- get the source for the timeline.
  Element* source = aOptions.mSource.WasPassed()
                        ? aOptions.mSource.Value().get()
                        : doc->GetScrollingElement();
  ScrollerInfo scroller = ScrollerInfo::Anonymous(
      ScrollerInfo::Type::Provided, source, PseudoStyleRequest::NotPseudo());

  // Step 3 -- set the axis for the timeline.
  StyleScrollAxis axis;
  switch (aOptions.mAxis) {
    case dom::ScrollAxis::Block:
      axis = StyleScrollAxis::Block;
      break;
    case dom::ScrollAxis::Inline:
      axis = StyleScrollAxis::Inline;
      break;
    case dom::ScrollAxis::X:
      axis = StyleScrollAxis::X;
      break;
    case dom::ScrollAxis::Y:
      axis = StyleScrollAxis::Y;
      break;
  }

  // Step 1 -- create the new ScrollTimeline object.
  RefPtr<ScrollTimeline> result =
      MakeAndAddRef<ScrollTimeline>(doc, scroller, axis);
  if (source) {
    result->UpdateCachedCurrentTime();
  }
  return result.forget();
}

Element* ScrollTimeline::GetSource() const { return SourceElement(); }

ScrollTimeline::State ScrollTimeline::GetState() const {
  const auto source = mScrollerInfo.Source();
  // Use document.scrollingElement to tell whether it's the root scroll
  // container. Note that we can't use mScrollerInfo.mType since Type::Nearest
  // can also reach the root scroll container.
  const bool isRoot =
      source.mElement &&
      source.mElement->OwnerDoc()->GetScrollingElementNoFlush() ==
          source.mElement;
  return State{source, mAxis, isRoot};
}

dom::ScrollAxis ScrollTimeline::GetScrollAxis() const {
  switch (mAxis) {
    case StyleScrollAxis::Block:
      return dom::ScrollAxis::Block;
    case StyleScrollAxis::Inline:
      return dom::ScrollAxis::Inline;
    case StyleScrollAxis::X:
      return dom::ScrollAxis::X;
    case StyleScrollAxis::Y:
      return dom::ScrollAxis::Y;
  }
  MOZ_ASSERT_UNREACHABLE("Unknown scroll axis");
  return dom::ScrollAxis::Block;
}

ScrollTimeline::ScrollTimeline(Document* aDocument,
                               const ScrollerInfo& aScrollerInfo,
                               StyleScrollAxis aAxis)
    : AnimationTimeline(aDocument->GetParentObject(),
                        aDocument->GetScopeObject()->GetRTPCallerType()),
      mDocument(aDocument),
      mScrollerInfo(aScrollerInfo),
      mAxis(aAxis) {
  MOZ_ASSERT(aDocument);

  mDocument->TimelinesController().AddScrollTimeline(*this);
}

/* static */
std::pair<const Element*, PseudoStyleRequest>
ScrollTimeline::FindNearestScroller(Element* aSubject,
                                    const PseudoStyleRequest& aPseudoRequest) {
  MOZ_ASSERT(aSubject);
  if (!aSubject->GetPrimaryFrame()) {
    return {nullptr, PseudoStyleRequest{}};
  }
  Element* subject = aSubject->GetPseudoElement(aPseudoRequest);
  if (!subject) {
    return {nullptr, PseudoStyleRequest{}};
  }

  // Rely on the behaviour of document.scrollingElement.
  Element* root = subject->OwnerDoc()->GetScrollingElementNoFlush();
  if (root == subject) {
    // If the element is the scrollingElement, we don't need to walk up the
    // frame tree.
    return {root, PseudoStyleRequest::NotPseudo()};
  }

  nsIFrame* subjectFrame = subject->GetPrimaryFrame();
  if (!subjectFrame) {
    return {nullptr, PseudoStyleRequest{}};
  }
  // Walk the frame tree rather than the flattened DOM tree.
  for (nsIFrame* curr = subjectFrame->GetParent(); curr;
       curr = curr->GetParent()) {
    nsIContent* content = curr->GetContent();
    if (!content || !content->IsElement()) {
      continue;
    }
    Element* element = content->AsElement();
    if (element == root) {
      break;
    }
    if (curr->IsScrollContainerFrame()) {
      return AnimationUtils::GetElementPseudoPair(element);
    }
  }
  return {root, PseudoStyleRequest::NotPseudo()};
}

/* static */
already_AddRefed<ScrollTimeline> ScrollTimeline::MakeAnonymous(
    Document* aDocument, const NonOwningAnimationTarget& aTarget,
    StyleScrollAxis aAxis, StyleScroller aScroller) {
  MOZ_ASSERT(aTarget);
  auto scroller = ScrollerInfo::Anonymous(aScroller, aTarget);
  // Each use of scroll() corresponds to its own instance of ScrollTimeline in
  // the Web Animations API, even if multiple elements use scroll() to refer to
  // the same scroll container with the same arguments.
  // https://drafts.csswg.org/scroll-animations-1/#scroll-notation
  return MakeAndAddRef<ScrollTimeline>(aDocument, scroller, aAxis);
}

/* static*/
already_AddRefed<ScrollTimeline> ScrollTimeline::MakeNamed(
    Document* aDocument, Element* aReferenceElement,
    const PseudoStyleRequest& aPseudoRequest, StyleScrollAxis aAxis) {
  MOZ_ASSERT(NS_IsMainThread());

  ScrollerInfo scroller =
      ScrollerInfo::Named(aReferenceElement, aPseudoRequest);
  return MakeAndAddRef<ScrollTimeline>(aDocument, std::move(scroller), aAxis);
}

Nullable<TimeDuration> ScrollTimeline::GetCurrentTimeAsDuration() const {
  const auto& data = ComputeTimelineData();
  if (!data) {
    return nullptr;
  }

  // FIXME: Scroll offsets on the RTL container is complicated specifically on
  // mobile, see https://github.com/w3c/csswg-drafts/issues/12893. For now, we
  // use the absoluate value to make things simple.
  const double progress =
      static_cast<double>(std::abs(data->mPosition) - data->mStart) /
      static_cast<double>(data->mEnd - data->mStart);
  return TimeDuration::FromMilliseconds(progress *
                                        PROGRESS_TIMELINE_DURATION_MILLISEC);
}

void ScrollTimeline::GetCurrentTime(
    Nullable<OwningCSSNumberish>& aRetVal) const {
  if (!StaticPrefs::layout_css_typed_om_enabled()) {
    // If Typed-OM isn't exposed, return progress encoded as milliseconds over
    // PROGRESS_TIMELINE_DURATION_MILLISEC.
    AnimationTimeline::GetCurrentTime(aRetVal);
    return;
  }

  const auto& data = ComputeTimelineData();
  if (!data) {
    aRetVal.SetNull();
    return;
  }
  // See the FIXME in our GetCurrentTimeAsDuration() override about
  // RTL/sideways scrollers. We do the same here.
  const double progress =
      static_cast<double>(std::abs(data->mPosition) - data->mStart) /
      static_cast<double>(data->mEnd - data->mStart);
  aRetVal.SetValue().SetAsCSSNumericValue() =
      MakeRefPtr<CSSUnitValue>(mWindow, progress * 100.0"percent"_ns);
}

void ScrollTimeline::WillRefresh() {
  UpdateCachedCurrentTime();

  if (!mDocument->GetPresShell()) {
    // If we're not displayed, don't tick animations.
    return;
  }

  if (mAnimationOrder.isEmpty()) {
    return;
  }

  // FIXME: Bug 1737927: Need to check the animation mutation observers for
  // animations with scroll timelines.
  // nsAutoAnimationMutationBatch mb(mDocument);

  TickState dummyState;
  Tick(dummyState);
}

bool ScrollTimeline::UpdateIfStale() {
  // The scroll timeline may be stale if there are any updates in
  // RenderingPhase::AnimationFrameCallbacks and RenderingPhase::Layout.
  // We have to check if the ranges are still valid.
  // https://drafts.csswg.org/scroll-animations-1/#event-loop
  const bool currentTimeUpdated = UpdateCachedCurrentTime();

  if (mAnimations.IsEmpty()) {
    return false;
  }

  // Check all animations and request restyle.
  // NOTE: Even if the animation doesn't have the target, it would be okay to
  // post update. We can optimize the case later.
  for (const auto& animation :
       ToTArray<AutoTArray<RefPtr<Animation>, 32>>(mAnimationOrder)) {
    const bool triggered = animation->MakeReadyAndMaybeTrigger();
    if (currentTimeUpdated || triggered) {
      animation->PostUpdate();
    }
  }
  return true;
}

bool ScrollTimeline::SourceMatches(
    const Element* aElement, const PseudoStyleRequest& aPseudoRequest) const {
  if (mScrollerInfo.IsAnonymous()) {
    // Anonymous timelines are considered unique.
    return false;
  }
  const auto source = mScrollerInfo.Source();
  return source.mElement == aElement && source.mPseudoRequest == aPseudoRequest;
}

layers::ScrollDirection ScrollTimeline::State::Axis() const {
  const auto* e = mSource.mElement;
  MOZ_ASSERT(e && e->GetPrimaryFrame());
  const WritingMode wm = e->GetPrimaryFrame()->GetWritingMode();
  return mAxis == StyleScrollAxis::X ||
                 (!wm.IsVertical() && mAxis == StyleScrollAxis::Inline) ||
                 (wm.IsVertical() && mAxis == StyleScrollAxis::Block)
             ? layers::ScrollDirection::eHorizontal
             : layers::ScrollDirection::eVertical;
}

StyleOverflow ScrollTimeline::State::SourceScrollStyle() const {
  DebugOnly<const Element*> e = mSource.mElement;
  MOZ_ASSERT(e && e->GetPrimaryFrame());

  const ScrollContainerFrame* scrollContainerFrame = GetScrollContainerFrame();
  MOZ_ASSERT(scrollContainerFrame);

  const ScrollStyles scrollStyles = scrollContainerFrame->GetScrollStyles();

  return Axis() == layers::ScrollDirection::eHorizontal
             ? scrollStyles.mHorizontal
             : scrollStyles.mVertical;
}

bool ScrollTimeline::State::APZIsActiveForSource() const {
  auto* e = mSource.mElement;
  MOZ_ASSERT(e, "HasNonMinimalNonZeroDisplayPort requires a source element");
  return gfxPlatform::AsyncPanZoomEnabled() &&
         !nsLayoutUtils::ShouldDisableApzForElement(e) &&
         DisplayPortUtils::HasNonMinimalNonZeroDisplayPort(e);
}

bool ScrollTimeline::State::ScrollingDirectionIsAvailable() const {
  const ScrollContainerFrame* scrollContainerFrame = GetScrollContainerFrame();
  MOZ_ASSERT(scrollContainerFrame);
  return scrollContainerFrame->GetAvailableScrollingDirections().contains(
      Axis());
}

const ScrollContainerFrame* ScrollTimeline::State::GetScrollContainerFrame()
    const {
  auto* e = mSource.mElement;
  if (!e) {
    return nullptr;
  }

  if (mIsRoot) {
    // document.scrollingElement may point to <body> in quirks mode, but the
    // root scroll container frame is what actually scrolls - return it.
    if (const PresShell* presShell = e->OwnerDoc()->GetPresShell()) {
      return presShell->GetRootScrollContainerFrame();
    }
    return nullptr;
  }
  return nsLayoutUtils::FindScrollContainerFrameFor(e);
}

void ScrollTimeline::ReplacePropertiesWith(
    const Element* aReferenceElement, const PseudoStyleRequest& aPseudoRequest,
    nsAtom* aName, StyleScrollAxis aAxis) {
  MOZ_ASSERT(!mScrollerInfo.IsAnonymous());
  MOZ_ASSERT(aReferenceElement == mScrollerInfo.Source().mElement &&
             aPseudoRequest == mScrollerInfo.Source().mPseudoRequest);
  mAxis = aAxis;

  for (auto* anim = mAnimationOrder.getFirst(); anim;
       anim = static_cast<LinkedListElement<Animation>*>(anim)->getNext()) {
    MOZ_ASSERT(anim->GetTimeline() == this);
    MOZ_ASSERT(anim->GetTimelineName() == aName);
    // Set this so we just PostUpdate() for this animation.
    // FIXME(dshin, bug 1737927): Mutation observer may need to be notified.
    anim->SetTimeline(this, aName, Animation::FromJS::No);
  }
}

ScrollTimeline::~ScrollTimeline() { Teardown(); }

bool ScrollTimeline::UpdateCachedCurrentTime() {
  const auto prevCachedCurrentTime = std::move(mCachedCurrentTime);

  mCachedCurrentTime.reset();

  const auto state = GetState();
  // If no layout box, this timeline is inactive.
  if (const auto* e = state.mSource.mElement; !e || !e->GetPrimaryFrame()) {
    return prevCachedCurrentTime.isSome();
  }

  // if this is not a scroller container, this timeline is inactive.
  const ScrollContainerFrame* scrollContainerFrame =
      state.GetScrollContainerFrame();
  if (!scrollContainerFrame) {
    return prevCachedCurrentTime.isSome();
  }

  const auto orientation = state.Axis();

  // If there is no scrollable overflow, then the ScrollTimeline is inactive.
  // https://drafts.csswg.org/scroll-animations-1/#scrolltimeline-interface
  if (!scrollContainerFrame->GetAvailableScrollingDirections().contains(
          orientation)) {
    return prevCachedCurrentTime.isSome();
  }

  const nsPoint& scrollPosition = scrollContainerFrame->GetScrollPosition();
  const nsRect& scrollRange = scrollContainerFrame->GetScrollRange();

  mCachedCurrentTime.emplace(CurrentTimeData{
      orientation == layers::ScrollDirection::eHorizontal ? scrollPosition.x
                                                          : scrollPosition.y,
      orientation == layers::ScrollDirection::eHorizontal
          ? scrollRange.width
          : scrollRange.height});

  if (!prevCachedCurrentTime || mCachedCurrentTime->mMaxScrollOffset !=
                                    prevCachedCurrentTime->mMaxScrollOffset) {
    TimelineDataDidChange();
  }
  return mCachedCurrentTime != prevCachedCurrentTime;
}

void ScrollTimeline::TimelineDataDidChange() {
  for (auto* anim = mAnimationOrder.getFirst(); anim;
       anim = static_cast<LinkedListElement<Animation>*>(anim)->getNext()) {
    anim->UpdateNormalizedTimingForTimelineDataChange();
    anim->MaybeUpdateKeyframeComputedOffsets();
  }
}

std::pair<doubledouble> ScrollTimeline::IntervalForAttachmentRange(
    const AnimationRange& aStyleRange) const {
  if (!mCachedCurrentTime || aStyleRange.IsNormal()) {
    return {0.01.0};
  }

  auto computeRangeEdgeAsPercentage =
      [&](const StyleGenericAnimationRangeValue<StyleLengthPercentage>&
              aValue) {
        const auto range = mCachedCurrentTime->mMaxScrollOffset;
        return static_cast<double>(aValue.lp.Resolve(range)) /
               static_cast<double>(range);
      };
  // We skip the unsupported timeline range anmes here. The spec doesn't address
  // this but other browsers agree with this behavior now.
  return {computeRangeEdgeAsPercentage(aStyleRange.mStart),
          computeRangeEdgeAsPercentage(aStyleRange.mEnd)};
};

void ScrollTimeline::AutoAlignStartTime() {
  for (Animation* animation : mAnimations) {
    animation->AutoAlignStartTime();
  }
}

Maybe<ScrollTimeline::ComputedTimelineData>
ScrollTimeline::ComputeTimelineData() const {
  return mCachedCurrentTime
             ? Some(ComputedTimelineData{mCachedCurrentTime->mPosition, 0,
                                         mCachedCurrentTime->mMaxScrollOffset})
             : Nothing();
}

static nsRefreshDriver* GetRefreshDriver(Document* aDocument) {
  nsPresContext* presContext = aDocument->GetPresContext();
  if (MOZ_UNLIKELY(!presContext)) {
    return nullptr;
  }
  return presContext->RefreshDriver();
}

void ScrollTimeline::NotifyAnimationUpdated(Animation& aAnimation) {
  AnimationTimeline::NotifyAnimationUpdated(aAnimation);

  if (!mAnimationOrder.isEmpty()) {
    if (auto* rd = GetRefreshDriver(mDocument)) {
      MOZ_ASSERT(isInList(),
                 "We should not register with the refresh driver if we are not"
                 " in the document's list of timelines");
      rd->EnsureAnimationUpdate();
    }
  }
}

void ScrollTimeline::NotifyAnimationContentVisibilityChanged(
    Animation* aAnimation, bool aIsVisible) {
  AnimationTimeline::NotifyAnimationContentVisibilityChanged(aAnimation,
                                                             aIsVisible);
  if (auto* rd = GetRefreshDriver(mDocument)) {
    MOZ_ASSERT(isInList(),
               "We should not register with the refresh driver if we are not"
               " in the document's list of timelines");
    rd->EnsureAnimationUpdate();
  }
}

NonOwningAnimationTarget ScrollTimeline::ScrollerInfo::Source() const {
  switch (mType) {
    case Type::Name:
      return NonOwningAnimationTarget{mSourceOrTarget};
    case Type::Nearest: {
      auto [element, pseudo] = FindNearestScroller(
          mSourceOrTarget.mElement, mSourceOrTarget.mPseudoRequest);
      return {const_cast<Element*>(element), pseudo};
    }
    case Type::Provided:
    case Type::Self:
      return NonOwningAnimationTarget{mSourceOrTarget};
    case Type::Root:
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("Unhandled timeline type");
  }
  // Specifies to use the document viewport as the scroll container.
  //
  // We use the owner doc of the animation target. This may be different
  // from |mDocument| after we implement ScrollTimeline interface for
  // script.
  return {mSourceOrTarget.mElement->OwnerDoc()->GetScrollingElementNoFlush(),
          PseudoStyleRequest{}};
}

NS_IMPL_CYCLE_COLLECTION_CLASS(InactiveTimeline)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InactiveTimeline,
                                                ScrollTimeline)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InactiveTimeline,
                                                  ScrollTimeline)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(InactiveTimeline,
                                               AnimationTimeline)

InactiveTimeline::InactiveTimeline(Document* aDocument)
    : ScrollTimeline{
          aDocument,
          ScrollerInfo::Anonymous(ScrollerInfo::Type::Provided, nullptr, {}),
          StyleScrollAxis::Y} {}

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142