Bumping manifests a=b2g-bump
[gecko.git] / layout / base / ActiveLayerTracker.cpp
blob2ec484fbcc61971dbfcf80553c0a236b2b921348
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"
16 namespace mozilla {
18 /**
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.
28 class LayerActivity {
29 public:
30 explicit LayerActivity(nsIFrame* aFrame)
31 : mFrame(aFrame)
32 , mContent(nullptr)
33 , mOpacityRestyleCount(0)
34 , mTransformRestyleCount(0)
35 , mLeftRestyleCount(0)
36 , mTopRestyleCount(0)
37 , mRightRestyleCount(0)
38 , mBottomRestyleCount(0)
39 , mMarginLeftRestyleCount(0)
40 , mMarginTopRestyleCount(0)
41 , mMarginRightRestyleCount(0)
42 , mMarginBottomRestyleCount(0)
43 , mContentActive(false)
45 ~LayerActivity();
46 nsExpirationState* GetExpirationState() { return &mState; }
47 uint8_t& RestyleCountForProperty(nsCSSProperty aProperty)
49 switch (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.
68 nsIFrame* mFrame;
69 nsIContent* mContent;
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;
83 bool mContentActive;
86 class LayerActivityTracker MOZ_FINAL : public nsExpirationTracker<LayerActivity,4> {
87 public:
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() {
93 AgeAllGenerations();
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)
117 void
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");
130 if (f) {
131 // The pres context might have been detached during the delay -
132 // that's fine, just skip the paint.
133 if (f->PresContext()->GetContainerWeak()) {
134 f->SchedulePaint();
136 f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
137 f->Properties().Delete(LayerActivityProperty());
138 } else {
139 c->DeleteProperty(nsGkAtoms::LayerActivity);
143 static LayerActivity*
144 GetLayerActivity(nsIFrame* aFrame)
146 if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) {
147 return nullptr;
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()));
159 if (layerActivity) {
160 gLayerActivityTracker->MarkUsed(layerActivity);
161 } else {
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;
173 static void
174 IncrementMutationCount(uint8_t* aCount)
176 *aCount = uint8_t(std::min(0xFF, *aCount + 1));
179 /* static */ void
180 ActiveLayerTracker::TransferActivityToContent(nsIFrame* aFrame, nsIContent* aContent)
182 if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) {
183 return;
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) {
190 return;
192 layerActivity->mFrame = nullptr;
193 layerActivity->mContent = aContent;
194 aContent->SetProperty(nsGkAtoms::LayerActivity, layerActivity,
195 nsINode::DeleteProperty<LayerActivity>, true);
198 /* static */ void
199 ActiveLayerTracker::TransferActivityToFrame(nsIContent* aContent, nsIFrame* aFrame)
201 LayerActivity* layerActivity = static_cast<LayerActivity*>(
202 aContent->UnsetProperty(nsGkAtoms::LayerActivity));
203 if (!layerActivity) {
204 return;
206 layerActivity->mContent = nullptr;
207 layerActivity->mFrame = aFrame;
208 aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
209 aFrame->Properties().Set(LayerActivityProperty(), layerActivity);
212 /* static */ void
213 ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty)
215 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
216 uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
217 IncrementMutationCount(&mutationCount);
220 /* static */ void
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);
230 /* static */ void
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;
239 static bool
240 IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext)
242 if (aPresContext->RefreshDriver()->IsInRefresh()) {
243 return true;
245 // Treat timeouts/setintervals as scripted animation callbacks for our
246 // purposes.
247 nsPIDOMWindow* win = aPresContext->Document()->GetInnerWindow();
248 return win && win->IsRunningTimeout();
251 /* static */ void
252 ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame,
253 nsCSSProperty aProperty)
255 if (!IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
256 return;
258 NotifyAnimated(aFrame, aProperty);
261 /* static */ bool
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) {
267 return true;
269 if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
270 aProperty == eCSSProperty_opacity) {
271 return true;
274 LayerActivity* layerActivity = GetLayerActivity(aFrame);
275 if (layerActivity) {
276 if (layerActivity->RestyleCountForProperty(aProperty) >= 2) {
277 return true;
280 if (aProperty == eCSSProperty_transform && aFrame->Preserves3D()) {
281 return IsStyleAnimated(aFrame->GetParent(), aProperty);
283 nsIContent* content = aFrame->GetContent();
284 if (content) {
285 return nsLayoutUtils::HasAnimations(content, aProperty);
288 return false;
291 /* static */ bool
292 ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame* aFrame)
294 LayerActivity* layerActivity = GetLayerActivity(aFrame);
295 if (layerActivity) {
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) {
304 return true;
307 return false;
310 /* static */ void
311 ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame)
313 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
314 layerActivity->mContentActive = true;
317 /* static */ bool
318 ActiveLayerTracker::IsContentActive(nsIFrame* aFrame)
320 LayerActivity* layerActivity = GetLayerActivity(aFrame);
321 return layerActivity && layerActivity->mContentActive;
324 /* static */ void
325 ActiveLayerTracker::Shutdown()
327 delete gLayerActivityTracker;
328 gLayerActivityTracker = nullptr;