1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ActiveLayerTracker.h"
7 #include "nsExpirationTracker.h"
8 #include "nsContainerFrame.h"
9 #include "nsIContent.h"
10 #include "nsRefreshDriver.h"
11 #include "nsPIDOMWindow.h"
12 #include "nsIDocument.h"
13 #include "nsAnimationManager.h"
14 #include "nsTransitionManager.h"
19 * This tracks the state of a frame that may need active layers due to
20 * ongoing content changes or style changes that indicate animation.
22 * When no changes of *any* kind are detected after 75-100ms we remove this
23 * object. Because we only track all kinds of activity with a single
24 * nsExpirationTracker, it's possible a frame might remain active somewhat
25 * spuriously if different kinds of changes kept happening, but that almost
26 * certainly doesn't matter.
30 explicit LayerActivity(nsIFrame
* aFrame
)
33 , mOpacityRestyleCount(0)
34 , mTransformRestyleCount(0)
35 , mLeftRestyleCount(0)
37 , mRightRestyleCount(0)
38 , mBottomRestyleCount(0)
39 , mMarginLeftRestyleCount(0)
40 , mMarginTopRestyleCount(0)
41 , mMarginRightRestyleCount(0)
42 , mMarginBottomRestyleCount(0)
43 , mContentActive(false)
46 nsExpirationState
* GetExpirationState() { return &mState
; }
47 uint8_t& RestyleCountForProperty(nsCSSProperty aProperty
)
50 case eCSSProperty_opacity
: return mOpacityRestyleCount
;
51 case eCSSProperty_transform
: return mTransformRestyleCount
;
52 case eCSSProperty_left
: return mLeftRestyleCount
;
53 case eCSSProperty_top
: return mTopRestyleCount
;
54 case eCSSProperty_right
: return mRightRestyleCount
;
55 case eCSSProperty_bottom
: return mBottomRestyleCount
;
56 case eCSSProperty_margin_left
: return mMarginLeftRestyleCount
;
57 case eCSSProperty_margin_top
: return mMarginTopRestyleCount
;
58 case eCSSProperty_margin_right
: return mMarginRightRestyleCount
;
59 case eCSSProperty_margin_bottom
: return mMarginBottomRestyleCount
;
60 default: MOZ_ASSERT(false); return mOpacityRestyleCount
;
64 // While tracked, exactly one of mFrame or mContent is non-null, depending
65 // on whether this property is stored on a frame or on a content node.
66 // When this property is expired by the layer activity tracker, both mFrame
67 // and mContent are nulled-out and the property is deleted.
71 nsExpirationState mState
;
72 // Number of restyle operations detected
73 uint8_t mOpacityRestyleCount
;
74 uint8_t mTransformRestyleCount
;
75 uint8_t mLeftRestyleCount
;
76 uint8_t mTopRestyleCount
;
77 uint8_t mRightRestyleCount
;
78 uint8_t mBottomRestyleCount
;
79 uint8_t mMarginLeftRestyleCount
;
80 uint8_t mMarginTopRestyleCount
;
81 uint8_t mMarginRightRestyleCount
;
82 uint8_t mMarginBottomRestyleCount
;
86 class LayerActivityTracker MOZ_FINAL
: public nsExpirationTracker
<LayerActivity
,4> {
88 // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
89 enum { GENERATION_MS
= 100 };
90 LayerActivityTracker()
91 : nsExpirationTracker
<LayerActivity
,4>(GENERATION_MS
) {}
92 ~LayerActivityTracker() {
96 virtual void NotifyExpired(LayerActivity
* aObject
);
99 static LayerActivityTracker
* gLayerActivityTracker
= nullptr;
101 LayerActivity::~LayerActivity()
103 if (mFrame
|| mContent
) {
104 NS_ASSERTION(gLayerActivityTracker
, "Should still have a tracker");
105 gLayerActivityTracker
->RemoveObject(this);
109 static void DestroyLayerActivity(void* aPropertyValue
)
111 delete static_cast<LayerActivity
*>(aPropertyValue
);
114 // Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set
115 NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty
, DestroyLayerActivity
)
118 LayerActivityTracker::NotifyExpired(LayerActivity
* aObject
)
120 RemoveObject(aObject
);
122 nsIFrame
* f
= aObject
->mFrame
;
123 nsIContent
* c
= aObject
->mContent
;
124 aObject
->mFrame
= nullptr;
125 aObject
->mContent
= nullptr;
127 MOZ_ASSERT((f
== nullptr) != (c
== nullptr),
128 "A LayerActivity object should always have a reference to either its frame or its content");
131 // The pres context might have been detached during the delay -
132 // that's fine, just skip the paint.
133 if (f
->PresContext()->GetContainerWeak()) {
136 f
->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY
);
137 f
->Properties().Delete(LayerActivityProperty());
139 c
->DeleteProperty(nsGkAtoms::LayerActivity
);
143 static LayerActivity
*
144 GetLayerActivity(nsIFrame
* aFrame
)
146 if (!aFrame
->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY
)) {
149 FrameProperties properties
= aFrame
->Properties();
150 return static_cast<LayerActivity
*>(properties
.Get(LayerActivityProperty()));
153 static LayerActivity
*
154 GetLayerActivityForUpdate(nsIFrame
* aFrame
)
156 FrameProperties properties
= aFrame
->Properties();
157 LayerActivity
* layerActivity
=
158 static_cast<LayerActivity
*>(properties
.Get(LayerActivityProperty()));
160 gLayerActivityTracker
->MarkUsed(layerActivity
);
162 if (!gLayerActivityTracker
) {
163 gLayerActivityTracker
= new LayerActivityTracker();
165 layerActivity
= new LayerActivity(aFrame
);
166 gLayerActivityTracker
->AddObject(layerActivity
);
167 aFrame
->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY
);
168 properties
.Set(LayerActivityProperty(), layerActivity
);
170 return layerActivity
;
174 IncrementMutationCount(uint8_t* aCount
)
176 *aCount
= uint8_t(std::min(0xFF, *aCount
+ 1));
180 ActiveLayerTracker::TransferActivityToContent(nsIFrame
* aFrame
, nsIContent
* aContent
)
182 if (!aFrame
->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY
)) {
185 FrameProperties properties
= aFrame
->Properties();
186 LayerActivity
* layerActivity
=
187 static_cast<LayerActivity
*>(properties
.Remove(LayerActivityProperty()));
188 aFrame
->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY
);
189 if (!layerActivity
) {
192 layerActivity
->mFrame
= nullptr;
193 layerActivity
->mContent
= aContent
;
194 aContent
->SetProperty(nsGkAtoms::LayerActivity
, layerActivity
,
195 nsINode::DeleteProperty
<LayerActivity
>, true);
199 ActiveLayerTracker::TransferActivityToFrame(nsIContent
* aContent
, nsIFrame
* aFrame
)
201 LayerActivity
* layerActivity
= static_cast<LayerActivity
*>(
202 aContent
->UnsetProperty(nsGkAtoms::LayerActivity
));
203 if (!layerActivity
) {
206 layerActivity
->mContent
= nullptr;
207 layerActivity
->mFrame
= aFrame
;
208 aFrame
->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY
);
209 aFrame
->Properties().Set(LayerActivityProperty(), layerActivity
);
213 ActiveLayerTracker::NotifyRestyle(nsIFrame
* aFrame
, nsCSSProperty aProperty
)
215 LayerActivity
* layerActivity
= GetLayerActivityForUpdate(aFrame
);
216 uint8_t& mutationCount
= layerActivity
->RestyleCountForProperty(aProperty
);
217 IncrementMutationCount(&mutationCount
);
221 ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame
* aFrame
)
223 LayerActivity
* layerActivity
= GetLayerActivityForUpdate(aFrame
);
224 IncrementMutationCount(&layerActivity
->mLeftRestyleCount
);
225 IncrementMutationCount(&layerActivity
->mTopRestyleCount
);
226 IncrementMutationCount(&layerActivity
->mRightRestyleCount
);
227 IncrementMutationCount(&layerActivity
->mBottomRestyleCount
);
231 ActiveLayerTracker::NotifyAnimated(nsIFrame
* aFrame
, nsCSSProperty aProperty
)
233 LayerActivity
* layerActivity
= GetLayerActivityForUpdate(aFrame
);
234 uint8_t& mutationCount
= layerActivity
->RestyleCountForProperty(aProperty
);
235 // We know this is animated, so just hack the mutation count.
236 mutationCount
= 0xFF;
240 IsPresContextInScriptAnimationCallback(nsPresContext
* aPresContext
)
242 if (aPresContext
->RefreshDriver()->IsInRefresh()) {
245 // Treat timeouts/setintervals as scripted animation callbacks for our
247 nsPIDOMWindow
* win
= aPresContext
->Document()->GetInnerWindow();
248 return win
&& win
->IsRunningTimeout();
252 ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame
* aFrame
,
253 nsCSSProperty aProperty
)
255 if (!IsPresContextInScriptAnimationCallback(aFrame
->PresContext())) {
258 NotifyAnimated(aFrame
, aProperty
);
262 ActiveLayerTracker::IsStyleAnimated(nsIFrame
* aFrame
, nsCSSProperty aProperty
)
264 // TODO: Add some abuse restrictions
265 if ((aFrame
->StyleDisplay()->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_TRANSFORM
) &&
266 aProperty
== eCSSProperty_transform
) {
269 if ((aFrame
->StyleDisplay()->mWillChangeBitField
& NS_STYLE_WILL_CHANGE_OPACITY
) &&
270 aProperty
== eCSSProperty_opacity
) {
274 LayerActivity
* layerActivity
= GetLayerActivity(aFrame
);
276 if (layerActivity
->RestyleCountForProperty(aProperty
) >= 2) {
280 if (aProperty
== eCSSProperty_transform
&& aFrame
->Preserves3D()) {
281 return IsStyleAnimated(aFrame
->GetParent(), aProperty
);
283 nsIContent
* content
= aFrame
->GetContent();
285 return nsLayoutUtils::HasAnimations(content
, aProperty
);
292 ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame
* aFrame
)
294 LayerActivity
* layerActivity
= GetLayerActivity(aFrame
);
296 if (layerActivity
->mLeftRestyleCount
>= 2 ||
297 layerActivity
->mTopRestyleCount
>= 2 ||
298 layerActivity
->mRightRestyleCount
>= 2 ||
299 layerActivity
->mBottomRestyleCount
>= 2 ||
300 layerActivity
->mMarginLeftRestyleCount
>= 2 ||
301 layerActivity
->mMarginTopRestyleCount
>= 2 ||
302 layerActivity
->mMarginRightRestyleCount
>= 2 ||
303 layerActivity
->mMarginBottomRestyleCount
>= 2) {
311 ActiveLayerTracker::NotifyContentChange(nsIFrame
* aFrame
)
313 LayerActivity
* layerActivity
= GetLayerActivityForUpdate(aFrame
);
314 layerActivity
->mContentActive
= true;
318 ActiveLayerTracker::IsContentActive(nsIFrame
* aFrame
)
320 LayerActivity
* layerActivity
= GetLayerActivity(aFrame
);
321 return layerActivity
&& layerActivity
->mContentActive
;
325 ActiveLayerTracker::Shutdown()
327 delete gLayerActivityTracker
;
328 gLayerActivityTracker
= nullptr;