tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DocumentTimeline.cpp (7982B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DocumentTimeline.h"
      8 
      9 #include "AnimationUtils.h"
     10 #include "mozilla/dom/DocumentInlines.h"
     11 #include "mozilla/dom/DocumentTimelineBinding.h"
     12 #include "nsContentUtils.h"
     13 #include "nsDOMMutationObserver.h"
     14 #include "nsDOMNavigationTiming.h"
     15 #include "nsPresContext.h"
     16 #include "nsRefreshDriver.h"
     17 
     18 namespace mozilla::dom {
     19 
     20 NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentTimeline)
     21 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocumentTimeline,
     22                                                AnimationTimeline)
     23  if (tmp->isInList()) {
     24    tmp->remove();
     25  }
     26  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
     27 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocumentTimeline,
     29                                                  AnimationTimeline)
     30  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
     31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     32 
     33 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DocumentTimeline,
     34                                               AnimationTimeline)
     35 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     36 
     37 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentTimeline)
     38 NS_INTERFACE_MAP_END_INHERITING(AnimationTimeline)
     39 
     40 NS_IMPL_ADDREF_INHERITED(DocumentTimeline, AnimationTimeline)
     41 NS_IMPL_RELEASE_INHERITED(DocumentTimeline, AnimationTimeline)
     42 
     43 DocumentTimeline::DocumentTimeline(Document* aDocument,
     44                                   const TimeDuration& aOriginTime)
     45    : AnimationTimeline(aDocument->GetParentObject(),
     46                        aDocument->GetScopeObject()->GetRTPCallerType()),
     47      mDocument(aDocument),
     48      mOriginTime(aOriginTime) {
     49  if (mDocument) {
     50    mDocument->TimelinesController().AddDocumentTimeline(*this);
     51  }
     52  // Ensure mLastRefreshDriverTime is valid.
     53  UpdateLastRefreshDriverTime();
     54 }
     55 
     56 DocumentTimeline::~DocumentTimeline() {
     57  if (isInList()) {
     58    remove();
     59  }
     60 }
     61 
     62 JSObject* DocumentTimeline::WrapObject(JSContext* aCx,
     63                                       JS::Handle<JSObject*> aGivenProto) {
     64  return DocumentTimeline_Binding::Wrap(aCx, this, aGivenProto);
     65 }
     66 
     67 /* static */
     68 already_AddRefed<DocumentTimeline> DocumentTimeline::Constructor(
     69    const GlobalObject& aGlobal, const DocumentTimelineOptions& aOptions,
     70    ErrorResult& aRv) {
     71  Document* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
     72  if (!doc) {
     73    aRv.Throw(NS_ERROR_FAILURE);
     74    return nullptr;
     75  }
     76  TimeDuration originTime =
     77      TimeDuration::FromMilliseconds(aOptions.mOriginTime);
     78 
     79  if (originTime == TimeDuration::Forever() ||
     80      originTime == -TimeDuration::Forever()) {
     81    aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>("Origin time");
     82    return nullptr;
     83  }
     84  RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime);
     85 
     86  return timeline.forget();
     87 }
     88 
     89 Nullable<TimeDuration> DocumentTimeline::GetCurrentTimeAsDuration() const {
     90  return ToTimelineTime(GetCurrentTimeStamp());
     91 }
     92 
     93 bool DocumentTimeline::TracksWallclockTime() const {
     94  nsRefreshDriver* refreshDriver = GetRefreshDriver();
     95  return !refreshDriver || !refreshDriver->IsTestControllingRefreshesEnabled();
     96 }
     97 
     98 TimeStamp DocumentTimeline::GetCurrentTimeStamp() const {
     99  nsRefreshDriver* refreshDriver = GetRefreshDriver();
    100  TimeStamp result = refreshDriver ? refreshDriver->MostRecentRefresh()
    101                                   : mLastRefreshDriverTime;
    102 
    103  return EnsureValidTimestamp(result);
    104 }
    105 
    106 void DocumentTimeline::UpdateLastRefreshDriverTime() {
    107  TimeStamp result = [&] {
    108    if (auto* rd = GetRefreshDriver()) {
    109      return rd->MostRecentRefresh();
    110    };
    111    return mLastRefreshDriverTime;
    112  }();
    113 
    114  result = EnsureValidTimestamp(result);
    115 
    116  if (!result.IsNull()) {
    117    mLastRefreshDriverTime = result;
    118  }
    119 }
    120 
    121 TimeStamp DocumentTimeline::EnsureValidTimestamp(
    122    const TimeStamp& aTimestamp) const {
    123  if (nsDOMNavigationTiming* timing = mDocument->GetNavigationTiming()) {
    124    // If we don't have a refresh driver and we've never had one use the
    125    // timeline's zero time.
    126    // In addition, it's possible that our refresh driver's timestamp is behind
    127    // from the navigation start time because the refresh driver timestamp is
    128    // sent through an IPC call whereas the navigation time is set by calling
    129    // TimeStamp::Now() directly. In such cases we also use the timeline's zero
    130    // time.
    131    // Also, let this time represent the current refresh time. This way we'll
    132    // save it as the last refresh time and skip looking up navigation start
    133    // time each time.
    134    if (aTimestamp.IsNull() ||
    135        aTimestamp < timing->GetNavigationStartTimeStamp()) {
    136      return timing->GetNavigationStartTimeStamp();
    137    }
    138  }
    139  return aTimestamp;
    140 }
    141 
    142 Nullable<TimeDuration> DocumentTimeline::ToTimelineTime(
    143    const TimeStamp& aTimeStamp) const {
    144  Nullable<TimeDuration> result;  // Initializes to null
    145  if (aTimeStamp.IsNull()) {
    146    return result;
    147  }
    148 
    149  nsDOMNavigationTiming* timing = mDocument->GetNavigationTiming();
    150  if (MOZ_UNLIKELY(!timing)) {
    151    return result;
    152  }
    153 
    154  result.SetValue(aTimeStamp - timing->GetNavigationStartTimeStamp() -
    155                  mOriginTime);
    156  return result;
    157 }
    158 
    159 void DocumentTimeline::NotifyAnimationUpdated(Animation& aAnimation) {
    160  AnimationTimeline::NotifyAnimationUpdated(aAnimation);
    161 
    162  if (!mAnimationOrder.isEmpty()) {
    163    if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
    164      MOZ_ASSERT(isInList(),
    165                 "We should not register with the refresh driver if we are not"
    166                 " in the document's list of timelines");
    167      refreshDriver->EnsureAnimationUpdate();
    168    }
    169  }
    170 }
    171 
    172 void DocumentTimeline::TriggerAllPendingAnimationsNow() {
    173  for (Animation* animation :
    174       ToTArray<AutoTArray<RefPtr<Animation>, 32>>(mAnimationOrder)) {
    175    animation->TryTriggerNow();
    176  }
    177 }
    178 
    179 void DocumentTimeline::WillRefresh() {
    180  if (!mDocument->GetPresShell()) {
    181    // If we're not displayed, don't tick animations.
    182    return;
    183  }
    184  UpdateLastRefreshDriverTime();
    185  if (mAnimationOrder.isEmpty()) {
    186    return;
    187  }
    188  nsAutoAnimationMutationBatch mb(mDocument);
    189 
    190  TickState state;
    191  bool ticked = Tick(state);
    192  if (!ticked) {
    193    return;
    194  }
    195  // We already assert that GetRefreshDriver() is non-null at the beginning
    196  // of this function but we check it again here to be sure that ticking
    197  // animations does not have any side effects that cause us to lose the
    198  // connection with the refresh driver, such as triggering the destruction
    199  // of mDocument's PresShell.
    200  if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
    201    refreshDriver->EnsureAnimationUpdate();
    202  }
    203 }
    204 
    205 void DocumentTimeline::NotifyAnimationContentVisibilityChanged(
    206    Animation* aAnimation, bool aIsVisible) {
    207  AnimationTimeline::NotifyAnimationContentVisibilityChanged(aAnimation,
    208                                                             aIsVisible);
    209 
    210  if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
    211    MOZ_ASSERT(isInList(),
    212               "We should not register with the refresh driver if we are not"
    213               " in the document's list of timelines");
    214    refreshDriver->EnsureAnimationUpdate();
    215  }
    216 }
    217 
    218 TimeStamp DocumentTimeline::ToTimeStamp(
    219    const TimeDuration& aTimeDuration) const {
    220  TimeStamp result;
    221  nsDOMNavigationTiming* timing = mDocument->GetNavigationTiming();
    222  if (MOZ_UNLIKELY(!timing)) {
    223    return result;
    224  }
    225 
    226  result =
    227      timing->GetNavigationStartTimeStamp() + (aTimeDuration + mOriginTime);
    228  return result;
    229 }
    230 
    231 nsRefreshDriver* DocumentTimeline::GetRefreshDriver() const {
    232  nsPresContext* presContext = mDocument->GetPresContext();
    233  if (MOZ_UNLIKELY(!presContext)) {
    234    return nullptr;
    235  }
    236  return presContext->RefreshDriver();
    237 }
    238 
    239 }  // namespace mozilla::dom