Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsIFrame.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.
21 *
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.
27 */
28 class LayerActivity {
29 public:
30 LayerActivity(nsIFrame* aFrame)
31 : mFrame(aFrame)
32 , mOpacityRestyleCount(0)
33 , mTransformRestyleCount(0)
34 , mLeftRestyleCount(0)
35 , mTopRestyleCount(0)
36 , mRightRestyleCount(0)
37 , mBottomRestyleCount(0)
38 , mMarginLeftRestyleCount(0)
39 , mMarginTopRestyleCount(0)
40 , mMarginRightRestyleCount(0)
41 , mMarginBottomRestyleCount(0)
42 , mContentActive(false)
43 {}
44 ~LayerActivity();
45 nsExpirationState* GetExpirationState() { return &mState; }
46 uint8_t& RestyleCountForProperty(nsCSSProperty aProperty)
47 {
48 switch (aProperty) {
49 case eCSSProperty_opacity: return mOpacityRestyleCount;
50 case eCSSProperty_transform: return mTransformRestyleCount;
51 case eCSSProperty_left: return mLeftRestyleCount;
52 case eCSSProperty_top: return mTopRestyleCount;
53 case eCSSProperty_right: return mRightRestyleCount;
54 case eCSSProperty_bottom: return mBottomRestyleCount;
55 case eCSSProperty_margin_left: return mMarginLeftRestyleCount;
56 case eCSSProperty_margin_top: return mMarginTopRestyleCount;
57 case eCSSProperty_margin_right: return mMarginRightRestyleCount;
58 case eCSSProperty_margin_bottom: return mMarginBottomRestyleCount;
59 default: MOZ_ASSERT(false); return mOpacityRestyleCount;
60 }
61 }
63 nsIFrame* mFrame;
64 nsExpirationState mState;
65 // Number of restyle operations detected
66 uint8_t mOpacityRestyleCount;
67 uint8_t mTransformRestyleCount;
68 uint8_t mLeftRestyleCount;
69 uint8_t mTopRestyleCount;
70 uint8_t mRightRestyleCount;
71 uint8_t mBottomRestyleCount;
72 uint8_t mMarginLeftRestyleCount;
73 uint8_t mMarginTopRestyleCount;
74 uint8_t mMarginRightRestyleCount;
75 uint8_t mMarginBottomRestyleCount;
76 bool mContentActive;
77 };
79 class LayerActivityTracker MOZ_FINAL : public nsExpirationTracker<LayerActivity,4> {
80 public:
81 // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
82 enum { GENERATION_MS = 100 };
83 LayerActivityTracker()
84 : nsExpirationTracker<LayerActivity,4>(GENERATION_MS) {}
85 ~LayerActivityTracker() {
86 AgeAllGenerations();
87 }
89 virtual void NotifyExpired(LayerActivity* aObject);
90 };
92 static LayerActivityTracker* gLayerActivityTracker = nullptr;
94 LayerActivity::~LayerActivity()
95 {
96 if (mFrame) {
97 NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
98 gLayerActivityTracker->RemoveObject(this);
99 }
100 }
102 static void DestroyLayerActivity(void* aPropertyValue)
103 {
104 delete static_cast<LayerActivity*>(aPropertyValue);
105 }
107 // Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set
108 NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DestroyLayerActivity)
110 void
111 LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
112 {
113 RemoveObject(aObject);
115 nsIFrame* f = aObject->mFrame;
116 aObject->mFrame = nullptr;
118 // The pres context might have been detached during the delay -
119 // that's fine, just skip the paint.
120 if (f->PresContext()->GetContainerWeak()) {
121 f->SchedulePaint();
122 }
123 f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
124 f->Properties().Delete(LayerActivityProperty());
125 }
127 static LayerActivity*
128 GetLayerActivity(nsIFrame* aFrame)
129 {
130 if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) {
131 return nullptr;
132 }
133 FrameProperties properties = aFrame->Properties();
134 return static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
135 }
137 static LayerActivity*
138 GetLayerActivityForUpdate(nsIFrame* aFrame)
139 {
140 FrameProperties properties = aFrame->Properties();
141 LayerActivity* layerActivity =
142 static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
143 if (layerActivity) {
144 gLayerActivityTracker->MarkUsed(layerActivity);
145 } else {
146 if (!gLayerActivityTracker) {
147 gLayerActivityTracker = new LayerActivityTracker();
148 }
149 layerActivity = new LayerActivity(aFrame);
150 gLayerActivityTracker->AddObject(layerActivity);
151 aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
152 properties.Set(LayerActivityProperty(), layerActivity);
153 }
154 return layerActivity;
155 }
157 static void
158 IncrementMutationCount(uint8_t* aCount)
159 {
160 *aCount = uint8_t(std::min(0xFF, *aCount + 1));
161 }
163 /* static */ void
164 ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty)
165 {
166 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
167 uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
168 IncrementMutationCount(&mutationCount);
169 }
171 /* static */ void
172 ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame)
173 {
174 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
175 IncrementMutationCount(&layerActivity->mLeftRestyleCount);
176 IncrementMutationCount(&layerActivity->mTopRestyleCount);
177 IncrementMutationCount(&layerActivity->mRightRestyleCount);
178 IncrementMutationCount(&layerActivity->mBottomRestyleCount);
179 }
181 /* static */ void
182 ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
183 {
184 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
185 uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
186 // We know this is animated, so just hack the mutation count.
187 mutationCount = 0xFF;
188 }
190 static bool
191 IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext)
192 {
193 if (aPresContext->RefreshDriver()->IsInRefresh()) {
194 return true;
195 }
196 // Treat timeouts/setintervals as scripted animation callbacks for our
197 // purposes.
198 nsPIDOMWindow* win = aPresContext->Document()->GetInnerWindow();
199 return win && win->IsRunningTimeout();
200 }
202 /* static */ void
203 ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame,
204 nsCSSProperty aProperty)
205 {
206 if (!IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
207 return;
208 }
209 NotifyAnimated(aFrame, aProperty);
210 }
212 /* static */ bool
213 ActiveLayerTracker::IsStyleAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
214 {
215 // TODO: Add some abuse restrictions
216 if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) &&
217 aProperty == eCSSProperty_transform) {
218 return true;
219 }
220 if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
221 aProperty == eCSSProperty_opacity) {
222 return true;
223 }
225 LayerActivity* layerActivity = GetLayerActivity(aFrame);
226 if (layerActivity) {
227 if (layerActivity->RestyleCountForProperty(aProperty) >= 2) {
228 return true;
229 }
230 }
231 if (aProperty == eCSSProperty_transform && aFrame->Preserves3D()) {
232 return IsStyleAnimated(aFrame->GetParent(), aProperty);
233 }
234 nsIContent* content = aFrame->GetContent();
235 if (content) {
236 if (mozilla::HasAnimationOrTransition<ElementAnimations>(
237 content, nsGkAtoms::animationsProperty, aProperty)) {
238 return true;
239 }
240 if (mozilla::HasAnimationOrTransition<ElementTransitions>(
241 content, nsGkAtoms::transitionsProperty, aProperty)) {
242 return true;
243 }
244 }
246 return false;
247 }
249 /* static */ bool
250 ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame* aFrame)
251 {
252 LayerActivity* layerActivity = GetLayerActivity(aFrame);
253 if (layerActivity) {
254 if (layerActivity->mLeftRestyleCount >= 2 ||
255 layerActivity->mTopRestyleCount >= 2 ||
256 layerActivity->mRightRestyleCount >= 2 ||
257 layerActivity->mBottomRestyleCount >= 2 ||
258 layerActivity->mMarginLeftRestyleCount >= 2 ||
259 layerActivity->mMarginTopRestyleCount >= 2 ||
260 layerActivity->mMarginRightRestyleCount >= 2 ||
261 layerActivity->mMarginBottomRestyleCount >= 2) {
262 return true;
263 }
264 }
265 return false;
266 }
268 /* static */ void
269 ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame)
270 {
271 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
272 layerActivity->mContentActive = true;
273 }
275 /* static */ bool
276 ActiveLayerTracker::IsContentActive(nsIFrame* aFrame)
277 {
278 LayerActivity* layerActivity = GetLayerActivity(aFrame);
279 return layerActivity && layerActivity->mContentActive;
280 }
282 /* static */ void
283 ActiveLayerTracker::Shutdown()
284 {
285 delete gLayerActivityTracker;
286 gLayerActivityTracker = nullptr;
287 }
289 }