tor-browser

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

AnimationEventDispatcher.cpp (8677B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/AnimationEventDispatcher.h"
      8 
      9 #include "mozilla/EventDispatcher.h"
     10 #include "mozilla/dom/AnimationEffect.h"
     11 #include "nsCSSProps.h"
     12 #include "nsGlobalWindowInner.h"
     13 #include "nsPresContext.h"
     14 #include "nsRefreshDriver.h"
     15 
     16 using namespace mozilla;
     17 
     18 namespace geckoprofiler::markers {
     19 
     20 struct CSSAnimationMarker {
     21  static constexpr Span<const char> MarkerTypeName() {
     22    return MakeStringSpan("CSSAnimation");
     23  }
     24  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
     25                                   const nsCString& aName,
     26                                   const nsCString& aTarget,
     27                                   const nsCString& aProperties,
     28                                   const nsCString& aOnCompositor) {
     29    aWriter.StringProperty("Name", aName);
     30    aWriter.StringProperty("Target", aTarget);
     31    aWriter.StringProperty("properties", aProperties);
     32    aWriter.StringProperty("oncompositor", aOnCompositor);
     33  }
     34  static MarkerSchema MarkerTypeDisplay() {
     35    using MS = MarkerSchema;
     36    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
     37    schema.AddKeyFormat("Name", MS::Format::String,
     38                        MS::PayloadFlags::Searchable);
     39    schema.AddKeyLabelFormat("properties", "Animated Properties",
     40                             MS::Format::String);
     41    schema.AddKeyLabelFormat("oncompositor", "Can Run on Compositor",
     42                             MS::Format::String);
     43    schema.AddKeyFormat("Target", MS::Format::String);
     44    schema.SetChartLabel("{marker.data.Name}");
     45    schema.SetTableLabel("{marker.data.Name}: {marker.data.properties}");
     46    return schema;
     47  }
     48 };
     49 
     50 struct CSSTransitionMarker {
     51  static constexpr Span<const char> MarkerTypeName() {
     52    return MakeStringSpan("CSSTransition");
     53  }
     54  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
     55                                   const nsCString& aTarget,
     56                                   const nsCString& aProperty,
     57                                   bool aOnCompositor, bool aCanceled) {
     58    aWriter.StringProperty("Target", aTarget);
     59    aWriter.StringProperty("property", aProperty);
     60    aWriter.BoolProperty("oncompositor", aOnCompositor);
     61    if (aCanceled) {
     62      aWriter.BoolProperty("Canceled", aCanceled);
     63    }
     64  }
     65  static MarkerSchema MarkerTypeDisplay() {
     66    using MS = MarkerSchema;
     67    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
     68    schema.AddKeyLabelFormat("property", "Animated Property",
     69                             MS::Format::String);
     70    schema.AddKeyLabelFormat("oncompositor", "Can Run on Compositor",
     71                             MS::Format::String);
     72    schema.AddKeyFormat("Canceled", MS::Format::String);
     73    schema.AddKeyFormat("Target", MS::Format::String);
     74    schema.SetChartLabel("{marker.data.property}");
     75    schema.SetTableLabel("{marker.data.property}");
     76    return schema;
     77  }
     78 };
     79 
     80 }  // namespace geckoprofiler::markers
     81 
     82 namespace mozilla {
     83 
     84 NS_IMPL_CYCLE_COLLECTION_CLASS(AnimationEventDispatcher)
     85 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AnimationEventDispatcher)
     86  tmp->ClearEventQueue();
     87 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEventDispatcher)
     89  for (auto& info : tmp->mPendingEvents) {
     90    if (OwningAnimationTarget* target = info.GetOwningAnimationTarget()) {
     91      ImplCycleCollectionTraverse(
     92          cb, target->mElement,
     93          "mozilla::AnimationEventDispatcher.mPendingEvents.mTarget");
     94    }
     95    ImplCycleCollectionTraverse(
     96        cb, info.mAnimation,
     97        "mozilla::AnimationEventDispatcher.mPendingEvents.mAnimation");
     98  }
     99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    100 
    101 void AnimationEventDispatcher::Disconnect() {
    102  ClearEventQueue();
    103  mPresContext = nullptr;
    104 }
    105 
    106 void AnimationEventDispatcher::QueueEvent(AnimationEventInfo&& aEvent) {
    107  const bool wasEmpty = mPendingEvents.IsEmpty();
    108  mPendingEvents.AppendElement(std::move(aEvent));
    109  mIsSorted = !wasEmpty;
    110  if (wasEmpty) {
    111    ScheduleDispatch();
    112  }
    113 }
    114 
    115 void AnimationEventDispatcher::QueueEvents(
    116    nsTArray<AnimationEventInfo>&& aEvents) {
    117  if (aEvents.IsEmpty()) {
    118    return;
    119  }
    120  const bool wasEmpty = mPendingEvents.IsEmpty();
    121  mPendingEvents.AppendElements(std::move(aEvents));
    122  mIsSorted = false;
    123  if (wasEmpty) {
    124    ScheduleDispatch();
    125  }
    126 }
    127 
    128 void AnimationEventDispatcher::ScheduleDispatch() {
    129  MOZ_ASSERT(mPresContext, "The pres context should be valid");
    130  mPresContext->RefreshDriver()->ScheduleRenderingPhase(
    131      RenderingPhase::UpdateAnimationsAndSendEvents);
    132 }
    133 
    134 void AnimationEventInfo::MaybeAddMarker() const {
    135  if (mData.is<CssAnimationData>()) {
    136    const auto& data = mData.as<CssAnimationData>();
    137    const EventMessage message = data.mMessage;
    138    if (message != eAnimationCancel && message != eAnimationEnd &&
    139        message != eAnimationIteration) {
    140      return;
    141    }
    142    nsAutoCString name;
    143    data.mAnimationName->ToUTF8String(name);
    144    const TimeStamp startTime = [&] {
    145      if (message == eAnimationIteration) {
    146        if (auto* effect = mAnimation->GetEffect()) {
    147          return mScheduledEventTimeStamp -
    148                 TimeDuration(effect->GetComputedTiming().mDuration);
    149        }
    150      }
    151      return mScheduledEventTimeStamp -
    152             TimeDuration::FromSeconds(data.mElapsedTime);
    153    }();
    154 
    155    AnimatedPropertyIDSet propertySet;
    156    nsAutoString target;
    157    if (dom::AnimationEffect* effect = mAnimation->GetEffect()) {
    158      if (dom::KeyframeEffect* keyFrameEffect = effect->AsKeyframeEffect()) {
    159        keyFrameEffect->GetTarget()->Describe(target, true);
    160        for (const AnimationProperty& property : keyFrameEffect->Properties()) {
    161          propertySet.AddProperty(property.mProperty);
    162        }
    163      }
    164    }
    165    nsAutoCString properties;
    166    nsAutoCString oncompositor;
    167    for (const CSSPropertyId& property : propertySet) {
    168      if (!properties.IsEmpty()) {
    169        properties.AppendLiteral(", ");
    170        oncompositor.AppendLiteral(", ");
    171      }
    172      nsAutoCString prop;
    173      property.ToString(prop);
    174      properties.Append(prop);
    175      oncompositor.Append(
    176          !property.IsCustom() &&
    177                  nsCSSProps::PropHasFlags(property.mId,
    178                                           CSSPropFlags::CanAnimateOnCompositor)
    179              ? "true"
    180              : "false");
    181    }
    182    PROFILER_MARKER(
    183        message == eAnimationIteration
    184            ? ProfilerString8View("CSS animation iteration")
    185            : ProfilerString8View("CSS animation"),
    186        DOM,
    187        MarkerOptions(
    188            MarkerTiming::Interval(startTime, mScheduledEventTimeStamp),
    189            mAnimation->GetOwnerWindow()
    190                ? MarkerInnerWindowId(mAnimation->GetOwnerWindow()->WindowID())
    191                : MarkerInnerWindowId::NoId()),
    192        CSSAnimationMarker, name, NS_ConvertUTF16toUTF8(target), properties,
    193        oncompositor);
    194    return;
    195  }
    196 
    197  if (!mData.is<CssTransitionData>()) {
    198    return;
    199  }
    200 
    201  const auto& data = mData.as<CssTransitionData>();
    202  const EventMessage message = data.mMessage;
    203  if (message != eTransitionEnd && message != eTransitionCancel) {
    204    return;
    205  }
    206 
    207  nsAutoString target;
    208  if (dom::AnimationEffect* effect = mAnimation->GetEffect()) {
    209    if (dom::KeyframeEffect* keyFrameEffect = effect->AsKeyframeEffect()) {
    210      keyFrameEffect->GetTarget()->Describe(target, true);
    211    }
    212  }
    213  nsAutoCString property;
    214  data.mProperty.ToString(property);
    215 
    216  // FIXME: This doesn't _really_ reflect whether the animation is actually run
    217  // in the compositor. The effect has that information and we should use it
    218  // probably.
    219  const bool onCompositor =
    220      !data.mProperty.IsCustom() &&
    221      nsCSSProps::PropHasFlags(data.mProperty.mId,
    222                               CSSPropFlags::CanAnimateOnCompositor);
    223  PROFILER_MARKER(
    224      "CSS transition", DOM,
    225      MarkerOptions(
    226          MarkerTiming::Interval(
    227              mScheduledEventTimeStamp -
    228                  TimeDuration::FromSeconds(data.mElapsedTime),
    229              mScheduledEventTimeStamp),
    230          mAnimation->GetOwnerWindow()
    231              ? MarkerInnerWindowId(mAnimation->GetOwnerWindow()->WindowID())
    232              : MarkerInnerWindowId::NoId()),
    233      CSSTransitionMarker, NS_ConvertUTF16toUTF8(target), property,
    234      onCompositor, message == eTransitionCancel);
    235 }
    236 
    237 }  // namespace mozilla