DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (d38398e5144e)

VCS Links

AnimationStyleRuleProcessor

CascadeLevel

EffectCompositor

RestyleType

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
/* -*- 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/. */

#ifndef mozilla_EffectCompositor_h
#define mozilla_EffectCompositor_h

#include "mozilla/EnumeratedArray.h"
#include "mozilla/Maybe.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/PseudoElementHashEntry.h"
#include "mozilla/RefPtr.h"
#include "nsCSSPropertyID.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsIStyleRuleProcessor.h"
#include "nsTArray.h"

class nsCSSPropertyIDSet;
class nsIFrame;
class nsIStyleRule;
class nsPresContext;
class nsStyleContext;

namespace mozilla {

class EffectSet;
class RestyleTracker;
class StyleAnimationValue;
class ServoAnimationRule;
struct AnimationPerformanceWarning;
struct AnimationProperty;
struct NonOwningAnimationTarget;

namespace dom {
class Animation;
class Element;
}

class EffectCompositor
{
public:
  explicit EffectCompositor(nsPresContext* aPresContext)
    : mPresContext(aPresContext)
  {
    for (size_t i = 0; i < kCascadeLevelCount; i++) {
      CascadeLevel cascadeLevel = CascadeLevel(i);
      mRuleProcessors[cascadeLevel] =
        new AnimationStyleRuleProcessor(this, cascadeLevel);
    }
  }

  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EffectCompositor)
  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EffectCompositor)

  void Disconnect() {
    mPresContext = nullptr;
  }

  // Animations can be applied at two different levels in the CSS cascade:
  enum class CascadeLevel : uint32_t {
    // The animations sheet (CSS animations, script-generated animations,
    // and CSS transitions that are no longer tied to CSS markup)
    Animations = 0,
    // The transitions sheet (CSS transitions that are tied to CSS markup)
    Transitions = 1
  };
  // We don't define this as part of CascadeLevel as then we'd have to add
  // explicit checks for the Count enum value everywhere CascadeLevel is used.
  static const size_t kCascadeLevelCount =
    static_cast<size_t>(CascadeLevel::Transitions) + 1;

  // NOTE: This can return null after Disconnect().
  nsPresContext* PresContext() const { return mPresContext; }

  enum class RestyleType {
    // Animation style has changed but the compositor is applying the same
    // change so we might be able to defer updating the main thread until it
    // becomes necessary.
    Throttled,
    // Animation style has changed and needs to be updated on the main thread.
    Standard,
    // Animation style has changed and needs to be updated on the main thread
    // as well as forcing animations on layers to be updated.
    // This is needed in cases such as when an animation becomes paused or has
    // its playback rate changed. In such cases, although the computed style
    // and refresh driver time might not change, we still need to ensure the
    // corresponding animations on layers are updated to reflect the new
    // configuration of the animation.
    Layer
  };

  // Notifies the compositor that the animation rule for the specified
  // (pseudo-)element at the specified cascade level needs to be updated.
  // The specified steps taken to update the animation rule depend on
  // |aRestyleType| whose values are described above.
  void RequestRestyle(dom::Element* aElement,
                      CSSPseudoElementType aPseudoType,
                      RestyleType aRestyleType,
                      CascadeLevel aCascadeLevel);

  // Schedule an animation restyle. This is called automatically by
  // RequestRestyle when necessary. However, it is exposed here since we also
  // need to perform this step when triggering transitions *without* also
  // invalidating the animation style rule (which RequestRestyle would do).
  void PostRestyleForAnimation(dom::Element* aElement,
                               CSSPseudoElementType aPseudoType,
                               CascadeLevel aCascadeLevel);

  // Posts an animation restyle for any elements whose animation style rule
  // is out of date but for which an animation restyle has not yet been
  // posted because updates on the main thread are throttled.
  void PostRestyleForThrottledAnimations();

  // Called when the style context on the specified (pseudo-) element might
  // have changed so that any context-sensitive values stored within
  // animation effects (e.g. em-based endpoints used in keyframe effects)
  // can be re-resolved to computed values.
  void UpdateEffectProperties(nsStyleContext* aStyleContext,
                              dom::Element* aElement,
                              CSSPseudoElementType aPseudoType);

  // Updates the animation rule stored on the EffectSet for the
  // specified (pseudo-)element for cascade level |aLevel|.
  // If the animation rule is not marked as needing an update,
  // no work is done.
  // |aStyleContext| is used for UpdateCascadingResults.
  // |aStyleContext| can be nullptr if style context, which is associated with
  // the primary frame of the specified (pseudo-)element, is the current style
  // context.
  // If we are resolving a new style context, we shoud pass the newly created
  // style context, otherwise we may use an old style context, it will result
  // unexpected cascading results.
  void MaybeUpdateAnimationRule(dom::Element* aElement,
                                CSSPseudoElementType aPseudoType,
                                CascadeLevel aCascadeLevel,
                                nsStyleContext *aStyleContext);

  // We need to pass the newly resolved style context as |aStyleContext| when
  // we call this function during resolving style context because this function
  // calls UpdateCascadingResults with a style context if necessary, at the
  // time, we end up using the previous style context if we don't pass the new
  // style context.
  // When we are not resolving style context, |aStyleContext| can be nullptr, we
  // will use a style context associated with the primary frame of the specified
  // (pseudo-)element.
  nsIStyleRule* GetAnimationRule(dom::Element* aElement,
                                 CSSPseudoElementType aPseudoType,
                                 CascadeLevel aCascadeLevel,
                                 nsStyleContext* aStyleContext);

  // Get animation rule for stylo. This is an equivalent of GetAnimationRule
  // and will be called from servo side. We need to be careful while doing any
  // modification because it may case some thread-safe issues.
  ServoAnimationRule* GetServoAnimationRule(const dom::Element* aElement,
                                            CSSPseudoElementType aPseudoType,
                                            CascadeLevel aCascadeLevel);

  // Clear mElementsToRestyle hashtable. Unlike GetAnimationRule,
  // in GetServoAnimationRule, we don't remove the entry of the composed
  // animation, so we can prevent the thread-safe issues of dom::Element.
  // Therefore, we need to call Clear mElementsToRestyle until we go back to
  // Gecko side.
  // FIXME: we shouldn't clear the animations on the compositor.
  void ClearElementsToRestyle();

  bool HasPendingStyleUpdates() const;
  bool HasThrottledStyleUpdates() const;

  // Tell the restyle tracker about all the animated styles that have
  // pending updates so that it can update the animation rule for these
  // elements.
  void AddStyleUpdatesTo(RestyleTracker& aTracker);

  nsIStyleRuleProcessor* RuleProcessor(CascadeLevel aCascadeLevel) const
  {
    return mRuleProcessors[aCascadeLevel];
  }

  static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
                                         nsCSSPropertyID aProperty);

  static nsTArray<RefPtr<dom::Animation>>
  GetAnimationsForCompositor(const nsIFrame* aFrame,
                             nsCSSPropertyID aProperty);

  static void ClearIsRunningOnCompositor(const nsIFrame* aFrame,
                                         nsCSSPropertyID aProperty);

  // Update animation cascade results for the specified (pseudo-)element
  // but only if we have marked the cascade as needing an update due a
  // the change in the set of effects or a change in one of the effects'
  // "in effect" state.
  // |aStyleContext| may be nullptr in which case we will use the
  // nsStyleContext of the primary frame of the specified (pseudo-)element.
  //
  // This method does NOT detect if other styles that apply above the
  // animation level of the cascade have changed.
  static void
  MaybeUpdateCascadeResults(dom::Element* aElement,
                            CSSPseudoElementType aPseudoType,
                            nsStyleContext* aStyleContext);

  // Update the mPropertiesWithImportantRules and
  // mPropertiesForAnimationsLevel members of the corresponding EffectSet.
  //
  // This can be expensive so we should only call it if styles that apply
  // above the animation level of the cascade might have changed. For all
  // other cases we should call MaybeUpdateCascadeResults.
  static void
  UpdateCascadeResults(dom::Element* aElement,
                       CSSPseudoElementType aPseudoType,
                       nsStyleContext* aStyleContext);

  // Helper to fetch the corresponding element and pseudo-type from a frame.
  //
  // For frames corresponding to pseudo-elements, the returned element is the
  // element on which we store the animations (i.e. the EffectSet and/or
  // AnimationCollection), *not* the generated content.
  //
  // Returns an empty result when a suitable element cannot be found including
  // when the frame represents a pseudo-element on which we do not support
  // animations.
  static Maybe<NonOwningAnimationTarget>
  GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame);

  // Associates a performance warning with effects on |aFrame| that animates
  // |aProperty|.
  static void SetPerformanceWarning(
    const nsIFrame* aFrame,
    nsCSSPropertyID aProperty,
    const AnimationPerformanceWarning& aWarning);

private:
  ~EffectCompositor() = default;

  // Rebuilds the animation rule corresponding to |aCascadeLevel| on the
  // EffectSet associated with the specified (pseudo-)element.
  static void ComposeAnimationRule(dom::Element* aElement,
                                   CSSPseudoElementType aPseudoType,
                                   CascadeLevel aCascadeLevel);

  static dom::Element* GetElementToRestyle(dom::Element* aElement,
                                           CSSPseudoElementType
                                             aPseudoType);

  // Get the properties in |aEffectSet| that we are able to animate on the
  // compositor but which are also specified at a higher level in the cascade
  // than the animations level in |aStyleContext|.
  static void
  GetOverriddenProperties(nsStyleContext* aStyleContext,
                          EffectSet& aEffectSet,
                          nsCSSPropertyIDSet& aPropertiesOverridden);

  static void
  UpdateCascadeResults(EffectSet& aEffectSet,
                       dom::Element* aElement,
                       CSSPseudoElementType aPseudoType,
                       nsStyleContext* aStyleContext);

  static nsPresContext* GetPresContext(dom::Element* aElement);

  nsPresContext* mPresContext;

  // Elements with a pending animation restyle. The associated bool value is
  // true if a pending animation restyle has also been dispatched. For
  // animations that can be throttled, we will add an entry to the hashtable to
  // indicate that the style rule on the element is out of date but without
  // posting a restyle to update it.
  EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount),
                  nsDataHashtable<PseudoElementHashEntry, bool>>
                    mElementsToRestyle;

  class AnimationStyleRuleProcessor final : public nsIStyleRuleProcessor
  {
  public:
    AnimationStyleRuleProcessor(EffectCompositor* aCompositor,
                                CascadeLevel aCascadeLevel)
      : mCompositor(aCompositor)
      , mCascadeLevel(aCascadeLevel)
    {
      MOZ_ASSERT(aCompositor);
    }

    NS_DECL_ISUPPORTS

    // nsIStyleRuleProcessor (parts)
    nsRestyleHint HasStateDependentStyle(
                        StateRuleProcessorData* aData) override;
    nsRestyleHint HasStateDependentStyle(
                        PseudoElementStateRuleProcessorData* aData) override;
    bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override;
    nsRestyleHint HasAttributeDependentStyle(
                        AttributeRuleProcessorData* aData,
                        RestyleHintData& aRestyleHintDataResult) override;
    bool MediumFeaturesChanged(nsPresContext* aPresContext) override;
    void RulesMatching(ElementRuleProcessorData* aData) override;
    void RulesMatching(PseudoElementRuleProcessorData* aData) override;
    void RulesMatching(AnonBoxRuleProcessorData* aData) override;
#ifdef MOZ_XUL
    void RulesMatching(XULTreeRuleProcessorData* aData) override;
#endif
    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
      const MOZ_MUST_OVERRIDE override;
    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
      const MOZ_MUST_OVERRIDE override;

  private:
    ~AnimationStyleRuleProcessor() = default;

    EffectCompositor* mCompositor;
    CascadeLevel      mCascadeLevel;
  };

  EnumeratedArray<CascadeLevel, CascadeLevel(kCascadeLevelCount),
                  OwningNonNull<AnimationStyleRuleProcessor>>
                    mRuleProcessors;
};

} // namespace mozilla

#endif // mozilla_EffectCompositor_h