From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- layout/painting/ActiveLayerTracker.cpp | 614 +++++++++++++++++++++++++++++++++ 1 file changed, 614 insertions(+) create mode 100644 layout/painting/ActiveLayerTracker.cpp (limited to 'layout/painting/ActiveLayerTracker.cpp') diff --git a/layout/painting/ActiveLayerTracker.cpp b/layout/painting/ActiveLayerTracker.cpp new file mode 100644 index 0000000000..4000ee9191 --- /dev/null +++ b/layout/painting/ActiveLayerTracker.cpp @@ -0,0 +1,614 @@ +/* -*- 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 "ActiveLayerTracker.h" + +#include "mozilla/AnimationUtils.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/EffectSet.h" +#include "mozilla/MotionPathUtils.h" +#include "mozilla/PodOperations.h" +#include "gfx2DGlue.h" +#include "nsExpirationTracker.h" +#include "nsContainerFrame.h" +#include "nsIContent.h" +#include "nsIScrollableFrame.h" +#include "nsRefreshDriver.h" +#include "nsPIDOMWindow.h" +#include "mozilla/dom/Document.h" +#include "nsAnimationManager.h" +#include "nsStyleTransformMatrix.h" +#include "nsTransitionManager.h" +#include "nsDisplayList.h" +#include "nsDOMCSSDeclaration.h" +#include "nsLayoutUtils.h" + +namespace mozilla { + +using namespace gfx; + +/** + * This tracks the state of a frame that may need active layers due to + * ongoing content changes or style changes that indicate animation. + * + * When no changes of *any* kind are detected after 75-100ms we remove this + * object. Because we only track all kinds of activity with a single + * nsExpirationTracker, it's possible a frame might remain active somewhat + * spuriously if different kinds of changes kept happening, but that almost + * certainly doesn't matter. + */ +class LayerActivity { + public: + enum ActivityIndex { + ACTIVITY_OPACITY, + ACTIVITY_TRANSFORM, + ACTIVITY_LEFT, + ACTIVITY_TOP, + ACTIVITY_RIGHT, + ACTIVITY_BOTTOM, + ACTIVITY_BACKGROUND_POSITION, + + ACTIVITY_SCALE, + ACTIVITY_TRIGGERED_REPAINT, + + // keep as last item + ACTIVITY_COUNT + }; + + explicit LayerActivity(nsIFrame* aFrame) + : mFrame(aFrame), mContent(nullptr), mContentActive(false) { + PodArrayZero(mRestyleCounts); + } + ~LayerActivity(); + nsExpirationState* GetExpirationState() { return &mState; } + uint8_t& RestyleCountForProperty(nsCSSPropertyID aProperty) { + return mRestyleCounts[GetActivityIndexForProperty(aProperty)]; + } + + static ActivityIndex GetActivityIndexForProperty(nsCSSPropertyID aProperty) { + switch (aProperty) { + case eCSSProperty_opacity: + return ACTIVITY_OPACITY; + case eCSSProperty_transform: + case eCSSProperty_translate: + case eCSSProperty_rotate: + case eCSSProperty_scale: + case eCSSProperty_offset_path: + case eCSSProperty_offset_distance: + case eCSSProperty_offset_rotate: + case eCSSProperty_offset_anchor: + // TODO: Bug 1559232: Add offset-position. + return ACTIVITY_TRANSFORM; + case eCSSProperty_left: + return ACTIVITY_LEFT; + case eCSSProperty_top: + return ACTIVITY_TOP; + case eCSSProperty_right: + return ACTIVITY_RIGHT; + case eCSSProperty_bottom: + return ACTIVITY_BOTTOM; + case eCSSProperty_background_position: + return ACTIVITY_BACKGROUND_POSITION; + case eCSSProperty_background_position_x: + return ACTIVITY_BACKGROUND_POSITION; + case eCSSProperty_background_position_y: + return ACTIVITY_BACKGROUND_POSITION; + default: + MOZ_ASSERT(false); + return ACTIVITY_OPACITY; + } + } + + static ActivityIndex GetActivityIndexForPropertySet( + const nsCSSPropertyIDSet& aPropertySet) { + if (aPropertySet.IsSubsetOf( + nsCSSPropertyIDSet::TransformLikeProperties())) { + return ACTIVITY_TRANSFORM; + } + MOZ_ASSERT( + aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())); + return ACTIVITY_OPACITY; + } + + // While tracked, exactly one of mFrame or mContent is non-null, depending + // on whether this property is stored on a frame or on a content node. + // When this property is expired by the layer activity tracker, both mFrame + // and mContent are nulled-out and the property is deleted. + nsIFrame* mFrame; + nsIContent* mContent; + + nsExpirationState mState; + + // Previous scale due to the CSS transform property. + Maybe mPreviousTransformScale; + + // The scroll frame during for which we most recently received a call to + // NotifyAnimatedFromScrollHandler. + WeakFrame mAnimatingScrollHandlerFrame; + // The set of activities that were triggered during + // mAnimatingScrollHandlerFrame's scroll event handler. + EnumSet mScrollHandlerInducedActivity; + + // Number of restyle operations detected + uint8_t mRestyleCounts[ACTIVITY_COUNT]; + bool mContentActive; +}; + +class LayerActivityTracker final + : public nsExpirationTracker { + public: + // 75-100ms is a good timeout period. We use 4 generations of 25ms each. + enum { GENERATION_MS = 100 }; + + explicit LayerActivityTracker(nsIEventTarget* aEventTarget) + : nsExpirationTracker( + GENERATION_MS, "LayerActivityTracker", aEventTarget), + mDestroying(false) {} + ~LayerActivityTracker() override { + mDestroying = true; + AgeAllGenerations(); + } + + void NotifyExpired(LayerActivity* aObject) override; + + public: + WeakFrame mCurrentScrollHandlerFrame; + + private: + bool mDestroying; +}; + +static LayerActivityTracker* gLayerActivityTracker = nullptr; + +LayerActivity::~LayerActivity() { + if (mFrame || mContent) { + NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker"); + gLayerActivityTracker->RemoveObject(this); + } +} + +// Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set +NS_DECLARE_FRAME_PROPERTY_DELETABLE(LayerActivityProperty, LayerActivity) + +void LayerActivityTracker::NotifyExpired(LayerActivity* aObject) { + if (!mDestroying && aObject->mAnimatingScrollHandlerFrame.IsAlive()) { + // Reset the restyle counts, but let the layer activity survive. + PodArrayZero(aObject->mRestyleCounts); + MarkUsed(aObject); + return; + } + + RemoveObject(aObject); + + nsIFrame* f = aObject->mFrame; + nsIContent* c = aObject->mContent; + aObject->mFrame = nullptr; + aObject->mContent = nullptr; + + MOZ_ASSERT((f == nullptr) != (c == nullptr), + "A LayerActivity object should always have a reference to either " + "its frame or its content"); + + if (f) { + // The pres context might have been detached during the delay - + // that's fine, just skip the paint. + if (f->PresContext()->GetContainerWeak() && !gfxVars::UseWebRender()) { + f->SchedulePaint(nsIFrame::PAINT_DEFAULT, false); + } + f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); + f->RemoveProperty(LayerActivityProperty()); + } else { + c->RemoveProperty(nsGkAtoms::LayerActivity); + } +} + +static LayerActivity* GetLayerActivity(nsIFrame* aFrame) { + if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) { + return nullptr; + } + return aFrame->GetProperty(LayerActivityProperty()); +} + +static LayerActivity* GetLayerActivityForUpdate(nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivity(aFrame); + if (layerActivity) { + gLayerActivityTracker->MarkUsed(layerActivity); + } else { + if (!gLayerActivityTracker) { + gLayerActivityTracker = + new LayerActivityTracker(GetMainThreadSerialEventTarget()); + } + layerActivity = new LayerActivity(aFrame); + gLayerActivityTracker->AddObject(layerActivity); + aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); + aFrame->SetProperty(LayerActivityProperty(), layerActivity); + } + return layerActivity; +} + +static void IncrementMutationCount(uint8_t* aCount) { + *aCount = uint8_t(std::min(0xFF, *aCount + 1)); +} + +/* static */ +void ActiveLayerTracker::TransferActivityToContent(nsIFrame* aFrame, + nsIContent* aContent) { + if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) { + return; + } + LayerActivity* layerActivity = aFrame->TakeProperty(LayerActivityProperty()); + aFrame->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); + if (!layerActivity) { + return; + } + layerActivity->mFrame = nullptr; + layerActivity->mContent = aContent; + aContent->SetProperty(nsGkAtoms::LayerActivity, layerActivity, + nsINode::DeleteProperty, true); +} + +/* static */ +void ActiveLayerTracker::TransferActivityToFrame(nsIContent* aContent, + nsIFrame* aFrame) { + auto* layerActivity = static_cast( + aContent->TakeProperty(nsGkAtoms::LayerActivity)); + if (!layerActivity) { + return; + } + layerActivity->mContent = nullptr; + layerActivity->mFrame = aFrame; + aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); + aFrame->SetProperty(LayerActivityProperty(), layerActivity); +} + +static void IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, + LayerActivity* aActivity) { + const nsStyleDisplay* display = aFrame->StyleDisplay(); + if (!display->HasTransformProperty() && !display->HasIndividualTransform() && + display->mOffsetPath.IsNone()) { + // The transform was removed. + aActivity->mPreviousTransformScale = Nothing(); + IncrementMutationCount( + &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); + return; + } + + // Compute the new scale due to the CSS transform property. + // Note: Motion path doesn't contribute to scale factor. (It only has 2d + // translate and 2d rotate, so we use Nothing() for it.) + nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame); + Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms( + display->mTranslate, display->mRotate, display->mScale, Nothing(), + display->mTransform, refBox, AppUnitsPerCSSPixel()); + Matrix transform2D; + if (!transform.Is2D(&transform2D)) { + // We don't attempt to handle 3D transforms; just assume the scale changed. + aActivity->mPreviousTransformScale = Nothing(); + IncrementMutationCount( + &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); + return; + } + + Size scale = transform2D.ScaleFactors(); + if (aActivity->mPreviousTransformScale == Some(scale)) { + return; // Nothing changed. + } + + aActivity->mPreviousTransformScale = Some(scale); + IncrementMutationCount( + &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); +} + +/* static */ +void ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, + nsCSSPropertyID aProperty) { + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); + uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); + IncrementMutationCount(&mutationCount); + + if (nsCSSPropertyIDSet::TransformLikeProperties().HasProperty(aProperty)) { + IncrementScaleRestyleCountIfNeeded(aFrame, layerActivity); + } +} + +/* static */ +void ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); + IncrementMutationCount( + &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT]); + IncrementMutationCount( + &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP]); + IncrementMutationCount( + &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT]); + IncrementMutationCount( + &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_BOTTOM]); +} + +/* static */ +void ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, + nsCSSPropertyID aProperty, + const nsACString& aNewValue, + nsDOMCSSDeclaration* aDOMCSSDecl) { + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); + uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); + if (mutationCount != 0xFF) { + nsAutoCString oldValue; + aDOMCSSDecl->GetPropertyValue(aProperty, oldValue); + if (oldValue != aNewValue) { + // We know this is animated, so just hack the mutation count. + mutationCount = 0xFF; + } + } +} + +/* static */ +void ActiveLayerTracker::NotifyAnimatedFromScrollHandler( + nsIFrame* aFrame, nsCSSPropertyID aProperty, nsIFrame* aScrollFrame) { + if (aFrame->PresContext() != aScrollFrame->PresContext()) { + // Don't allow cross-document dependencies. + return; + } + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); + LayerActivity::ActivityIndex activityIndex = + LayerActivity::GetActivityIndexForProperty(aProperty); + + if (layerActivity->mAnimatingScrollHandlerFrame.GetFrame() != aScrollFrame) { + // Discard any activity of a different scroll frame. We only track the + // most recent scroll handler induced activity. + layerActivity->mScrollHandlerInducedActivity.clear(); + layerActivity->mAnimatingScrollHandlerFrame = aScrollFrame; + } + + layerActivity->mScrollHandlerInducedActivity += activityIndex; +} + +static bool IsPresContextInScriptAnimationCallback( + nsPresContext* aPresContext) { + if (aPresContext->RefreshDriver()->IsInRefresh()) { + return true; + } + // Treat timeouts/setintervals as scripted animation callbacks for our + // purposes. + nsPIDOMWindowInner* win = aPresContext->Document()->GetInnerWindow(); + return win && win->IsRunningTimeout(); +} + +/* static */ +void ActiveLayerTracker::NotifyInlineStyleRuleModified( + nsIFrame* aFrame, nsCSSPropertyID aProperty, const nsACString& aNewValue, + nsDOMCSSDeclaration* aDOMCSSDecl) { + if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) { + NotifyAnimated(aFrame, aProperty, aNewValue, aDOMCSSDecl); + } + if (gLayerActivityTracker && + gLayerActivityTracker->mCurrentScrollHandlerFrame.IsAlive()) { + NotifyAnimatedFromScrollHandler( + aFrame, aProperty, + gLayerActivityTracker->mCurrentScrollHandlerFrame.GetFrame()); + } +} + +/* static */ +void ActiveLayerTracker::NotifyNeedsRepaint(nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); + if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) { + // This is mirroring NotifyInlineStyleRuleModified's NotifyAnimated logic. + // Just max out the restyle count if we're in an animation callback. + layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] = + 0xFF; + } else { + IncrementMutationCount( + &layerActivity + ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT]); + } +} + +static bool CheckScrollInducedActivity( + LayerActivity* aLayerActivity, LayerActivity::ActivityIndex aActivityIndex, + nsDisplayListBuilder* aBuilder) { + if (!aLayerActivity->mScrollHandlerInducedActivity.contains(aActivityIndex) || + !aLayerActivity->mAnimatingScrollHandlerFrame.IsAlive()) { + return false; + } + + nsIScrollableFrame* scrollFrame = + do_QueryFrame(aLayerActivity->mAnimatingScrollHandlerFrame.GetFrame()); + if (scrollFrame && (!aBuilder || scrollFrame->IsScrollingActive(aBuilder))) { + return true; + } + + // The scroll frame has been destroyed or has become inactive. Clear it from + // the layer activity so that it can expire. + aLayerActivity->mAnimatingScrollHandlerFrame = nullptr; + aLayerActivity->mScrollHandlerInducedActivity.clear(); + return false; +} + +/* static */ +bool ActiveLayerTracker::IsBackgroundPositionAnimated( + nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivity(aFrame); + if (layerActivity) { + LayerActivity::ActivityIndex activityIndex = + LayerActivity::ActivityIndex::ACTIVITY_BACKGROUND_POSITION; + if (layerActivity->mRestyleCounts[activityIndex] >= 2) { + // If the frame needs to be repainted frequently, we probably don't get + // much from treating the property as animated, *unless* this frame's + // 'scale' (which includes the bounds changes of a rotation) is changing. + // Marking a scaling transform as animating allows us to avoid resizing + // the texture, even if we have to repaint the contents of that texture. + if (layerActivity + ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] < 2) { + return true; + } + } + if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) { + return true; + } + } + return nsLayoutUtils::HasEffectiveAnimation( + aFrame, nsCSSPropertyIDSet({eCSSProperty_background_position_x, + eCSSProperty_background_position_y})); +} + +static bool IsMotionPathAnimated(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame) { + return ActiveLayerTracker::IsStyleAnimated( + aBuilder, aFrame, nsCSSPropertyIDSet{eCSSProperty_offset_path}) || + (!aFrame->StyleDisplay()->mOffsetPath.IsNone() && + ActiveLayerTracker::IsStyleAnimated( + aBuilder, aFrame, + nsCSSPropertyIDSet{eCSSProperty_offset_distance, + eCSSProperty_offset_rotate, + eCSSProperty_offset_anchor})); +} + +/* static */ +bool ActiveLayerTracker::IsTransformAnimated(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame) { + return IsStyleAnimated(aBuilder, aFrame, + nsCSSPropertyIDSet::CSSTransformProperties()) || + IsMotionPathAnimated(aBuilder, aFrame); +} + +/* static */ +bool ActiveLayerTracker::IsTransformMaybeAnimated(nsIFrame* aFrame) { + return IsStyleAnimated(nullptr, aFrame, + nsCSSPropertyIDSet::CSSTransformProperties()) || + IsMotionPathAnimated(nullptr, aFrame); +} + +/* static */ +bool ActiveLayerTracker::IsStyleAnimated( + nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + const nsCSSPropertyIDSet& aPropertySet) { + MOZ_ASSERT( + aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties()) || + aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties()), + "Only subset of opacity or transform-like properties set calls this"); + + // For display:table content, transforms are applied to the table wrapper + // (primary frame) but their will-change style will be specified on the style + // frame and, unlike other transform properties, not inherited. + // As a result, for transform properties only we need to be careful to look up + // the will-change style on the _style_ frame. + const nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aFrame); + const nsCSSPropertyIDSet transformSet = + nsCSSPropertyIDSet::TransformLikeProperties(); + if ((styleFrame && (styleFrame->StyleDisplay()->mWillChange.bits & + StyleWillChangeBits::TRANSFORM)) && + aPropertySet.Intersects(transformSet) && + (!aBuilder || + aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) { + return true; + } + if ((aFrame->StyleDisplay()->mWillChange.bits & + StyleWillChangeBits::OPACITY) && + aPropertySet.Intersects(nsCSSPropertyIDSet::OpacityProperties()) && + (!aBuilder || + aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) { + return true; + } + + LayerActivity* layerActivity = GetLayerActivity(aFrame); + if (layerActivity) { + LayerActivity::ActivityIndex activityIndex = + LayerActivity::GetActivityIndexForPropertySet(aPropertySet); + if (layerActivity->mRestyleCounts[activityIndex] >= 2) { + // If the frame needs to be repainted frequently, we probably don't get + // much from treating the property as animated, *unless* this frame's + // 'scale' (which includes the bounds changes of a rotation) is changing. + // Marking a scaling transform as animating allows us to avoid resizing + // the texture, even if we have to repaint the contents of that texture. + if (layerActivity + ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] < + 2 || + (aPropertySet.Intersects(transformSet) && + IsScaleSubjectToAnimation(aFrame))) { + return true; + } + } + if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) { + return true; + } + } + + if (nsLayoutUtils::HasEffectiveAnimation(aFrame, aPropertySet)) { + return true; + } + + if (!aPropertySet.Intersects(transformSet) || + !aFrame->Combines3DTransformWithAncestors()) { + return false; + } + + // For preserve-3d, we check if there is any transform animation on its parent + // frames in the 3d rendering context. If there is one, this function will + // return true. + return IsStyleAnimated(aBuilder, aFrame->GetParent(), aPropertySet); +} + +/* static */ +bool ActiveLayerTracker::IsOffsetStyleAnimated(nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivity(aFrame); + if (layerActivity) { + if (layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT] >= 2 || + layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP] >= 2 || + layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT] >= 2 || + layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_BOTTOM] >= 2) { + return true; + } + } + // We should also check for running CSS animations of these properties once + // bug 1009693 is fixed. Until that happens, layerization isn't useful for + // animations of these properties because we'll invalidate the layer contents + // on every change anyway. + // See bug 1151346 for a patch that adds a check for CSS animations. + return false; +} + +/* static */ +bool ActiveLayerTracker::IsScaleSubjectToAnimation(nsIFrame* aFrame) { + // Check whether JavaScript is animating this frame's scale. + LayerActivity* layerActivity = GetLayerActivity(aFrame); + if (layerActivity && + layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE] >= 2) { + return true; + } + + return AnimationUtils::FrameHasAnimatedScale(aFrame); +} + +/* static */ +void ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); + layerActivity->mContentActive = true; +} + +/* static */ +bool ActiveLayerTracker::IsContentActive(nsIFrame* aFrame) { + LayerActivity* layerActivity = GetLayerActivity(aFrame); + return layerActivity && layerActivity->mContentActive; +} + +/* static */ +void ActiveLayerTracker::SetCurrentScrollHandlerFrame(nsIFrame* aFrame) { + if (!gLayerActivityTracker) { + gLayerActivityTracker = + new LayerActivityTracker(GetMainThreadSerialEventTarget()); + } + gLayerActivityTracker->mCurrentScrollHandlerFrame = aFrame; +} + +/* static */ +void ActiveLayerTracker::Shutdown() { + delete gLayerActivityTracker; + gLayerActivityTracker = nullptr; +} + +} // namespace mozilla -- cgit v1.2.3