/src/mozilla-central/layout/painting/ActiveLayerTracker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "ActiveLayerTracker.h" |
8 | | |
9 | | #include "mozilla/AnimationUtils.h" |
10 | | #include "mozilla/ArrayUtils.h" |
11 | | #include "mozilla/gfx/Matrix.h" |
12 | | #include "mozilla/EffectSet.h" |
13 | | #include "mozilla/PodOperations.h" |
14 | | #include "gfx2DGlue.h" |
15 | | #include "nsExpirationTracker.h" |
16 | | #include "nsContainerFrame.h" |
17 | | #include "nsIContent.h" |
18 | | #include "nsRefreshDriver.h" |
19 | | #include "nsPIDOMWindow.h" |
20 | | #include "nsIDocument.h" |
21 | | #include "nsAnimationManager.h" |
22 | | #include "nsStyleTransformMatrix.h" |
23 | | #include "nsTransitionManager.h" |
24 | | #include "nsDisplayList.h" |
25 | | #include "nsDOMCSSDeclaration.h" |
26 | | |
27 | | namespace mozilla { |
28 | | |
29 | | using namespace gfx; |
30 | | |
31 | | /** |
32 | | * This tracks the state of a frame that may need active layers due to |
33 | | * ongoing content changes or style changes that indicate animation. |
34 | | * |
35 | | * When no changes of *any* kind are detected after 75-100ms we remove this |
36 | | * object. Because we only track all kinds of activity with a single |
37 | | * nsExpirationTracker, it's possible a frame might remain active somewhat |
38 | | * spuriously if different kinds of changes kept happening, but that almost |
39 | | * certainly doesn't matter. |
40 | | */ |
41 | | class LayerActivity |
42 | | { |
43 | | public: |
44 | | enum ActivityIndex |
45 | | { |
46 | | ACTIVITY_OPACITY, |
47 | | ACTIVITY_TRANSFORM, |
48 | | ACTIVITY_LEFT, |
49 | | ACTIVITY_TOP, |
50 | | ACTIVITY_RIGHT, |
51 | | ACTIVITY_BOTTOM, |
52 | | ACTIVITY_BACKGROUND_POSITION, |
53 | | |
54 | | ACTIVITY_SCALE, |
55 | | ACTIVITY_TRIGGERED_REPAINT, |
56 | | |
57 | | // keep as last item |
58 | | ACTIVITY_COUNT |
59 | | }; |
60 | | |
61 | | explicit LayerActivity(nsIFrame* aFrame) |
62 | | : mFrame(aFrame) |
63 | | , mContent(nullptr) |
64 | | , mContentActive(false) |
65 | 0 | { |
66 | 0 | PodArrayZero(mRestyleCounts); |
67 | 0 | } |
68 | | ~LayerActivity(); |
69 | 0 | nsExpirationState* GetExpirationState() { return &mState; } |
70 | | uint8_t& RestyleCountForProperty(nsCSSPropertyID aProperty) |
71 | 0 | { |
72 | 0 | return mRestyleCounts[GetActivityIndexForProperty(aProperty)]; |
73 | 0 | } |
74 | | |
75 | | static ActivityIndex GetActivityIndexForProperty(nsCSSPropertyID aProperty) |
76 | 0 | { |
77 | 0 | switch (aProperty) { |
78 | 0 | case eCSSProperty_opacity: |
79 | 0 | return ACTIVITY_OPACITY; |
80 | 0 | case eCSSProperty_transform: |
81 | 0 | return ACTIVITY_TRANSFORM; |
82 | 0 | case eCSSProperty_left: |
83 | 0 | return ACTIVITY_LEFT; |
84 | 0 | case eCSSProperty_top: |
85 | 0 | return ACTIVITY_TOP; |
86 | 0 | case eCSSProperty_right: |
87 | 0 | return ACTIVITY_RIGHT; |
88 | 0 | case eCSSProperty_bottom: |
89 | 0 | return ACTIVITY_BOTTOM; |
90 | 0 | case eCSSProperty_background_position: |
91 | 0 | return ACTIVITY_BACKGROUND_POSITION; |
92 | 0 | case eCSSProperty_background_position_x: |
93 | 0 | return ACTIVITY_BACKGROUND_POSITION; |
94 | 0 | case eCSSProperty_background_position_y: |
95 | 0 | return ACTIVITY_BACKGROUND_POSITION; |
96 | 0 | default: |
97 | 0 | MOZ_ASSERT(false); |
98 | 0 | return ACTIVITY_OPACITY; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | // While tracked, exactly one of mFrame or mContent is non-null, depending |
103 | | // on whether this property is stored on a frame or on a content node. |
104 | | // When this property is expired by the layer activity tracker, both mFrame |
105 | | // and mContent are nulled-out and the property is deleted. |
106 | | nsIFrame* mFrame; |
107 | | nsIContent* mContent; |
108 | | |
109 | | nsExpirationState mState; |
110 | | |
111 | | // Previous scale due to the CSS transform property. |
112 | | Maybe<Size> mPreviousTransformScale; |
113 | | |
114 | | // The scroll frame during for which we most recently received a call to |
115 | | // NotifyAnimatedFromScrollHandler. |
116 | | WeakFrame mAnimatingScrollHandlerFrame; |
117 | | // The set of activities that were triggered during |
118 | | // mAnimatingScrollHandlerFrame's scroll event handler. |
119 | | EnumSet<ActivityIndex> mScrollHandlerInducedActivity; |
120 | | |
121 | | // Number of restyle operations detected |
122 | | uint8_t mRestyleCounts[ACTIVITY_COUNT]; |
123 | | bool mContentActive; |
124 | | }; |
125 | | |
126 | | class LayerActivityTracker final : public nsExpirationTracker<LayerActivity, 4> |
127 | | { |
128 | | public: |
129 | | // 75-100ms is a good timeout period. We use 4 generations of 25ms each. |
130 | | enum |
131 | | { |
132 | | GENERATION_MS = 100 |
133 | | }; |
134 | | |
135 | | explicit LayerActivityTracker(nsIEventTarget* aEventTarget) |
136 | | : nsExpirationTracker<LayerActivity, 4>(GENERATION_MS, |
137 | | "LayerActivityTracker", |
138 | | aEventTarget) |
139 | | , mDestroying(false) |
140 | 0 | { |
141 | 0 | } |
142 | | ~LayerActivityTracker() override |
143 | 0 | { |
144 | 0 | mDestroying = true; |
145 | 0 | AgeAllGenerations(); |
146 | 0 | } |
147 | | |
148 | | void NotifyExpired(LayerActivity* aObject) override; |
149 | | |
150 | | public: |
151 | | WeakFrame mCurrentScrollHandlerFrame; |
152 | | |
153 | | private: |
154 | | bool mDestroying; |
155 | | }; |
156 | | |
157 | | static LayerActivityTracker* gLayerActivityTracker = nullptr; |
158 | | |
159 | | LayerActivity::~LayerActivity() |
160 | 0 | { |
161 | 0 | if (mFrame || mContent) { |
162 | 0 | NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker"); |
163 | 0 | gLayerActivityTracker->RemoveObject(this); |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | // Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set |
168 | | NS_DECLARE_FRAME_PROPERTY_DELETABLE(LayerActivityProperty, LayerActivity) |
169 | | |
170 | | void |
171 | | LayerActivityTracker::NotifyExpired(LayerActivity* aObject) |
172 | 0 | { |
173 | 0 | if (!mDestroying && aObject->mAnimatingScrollHandlerFrame.IsAlive()) { |
174 | 0 | // Reset the restyle counts, but let the layer activity survive. |
175 | 0 | PodArrayZero(aObject->mRestyleCounts); |
176 | 0 | MarkUsed(aObject); |
177 | 0 | return; |
178 | 0 | } |
179 | 0 | |
180 | 0 | RemoveObject(aObject); |
181 | 0 |
|
182 | 0 | nsIFrame* f = aObject->mFrame; |
183 | 0 | nsIContent* c = aObject->mContent; |
184 | 0 | aObject->mFrame = nullptr; |
185 | 0 | aObject->mContent = nullptr; |
186 | 0 |
|
187 | 0 | MOZ_ASSERT((f == nullptr) != (c == nullptr), |
188 | 0 | "A LayerActivity object should always have a reference to either " |
189 | 0 | "its frame or its content"); |
190 | 0 |
|
191 | 0 | if (f) { |
192 | 0 | // The pres context might have been detached during the delay - |
193 | 0 | // that's fine, just skip the paint. |
194 | 0 | if (f->PresContext()->GetContainerWeak()) { |
195 | 0 | f->SchedulePaint(); |
196 | 0 | } |
197 | 0 | f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); |
198 | 0 | f->DeleteProperty(LayerActivityProperty()); |
199 | 0 | } else { |
200 | 0 | c->DeleteProperty(nsGkAtoms::LayerActivity); |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | static LayerActivity* |
205 | | GetLayerActivity(nsIFrame* aFrame) |
206 | 0 | { |
207 | 0 | if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) { |
208 | 0 | return nullptr; |
209 | 0 | } |
210 | 0 | return aFrame->GetProperty(LayerActivityProperty()); |
211 | 0 | } |
212 | | |
213 | | static LayerActivity* |
214 | | GetLayerActivityForUpdate(nsIFrame* aFrame) |
215 | 0 | { |
216 | 0 | LayerActivity* layerActivity = GetLayerActivity(aFrame); |
217 | 0 | if (layerActivity) { |
218 | 0 | gLayerActivityTracker->MarkUsed(layerActivity); |
219 | 0 | } else { |
220 | 0 | if (!gLayerActivityTracker) { |
221 | 0 | gLayerActivityTracker = new LayerActivityTracker( |
222 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other)); |
223 | 0 | } |
224 | 0 | layerActivity = new LayerActivity(aFrame); |
225 | 0 | gLayerActivityTracker->AddObject(layerActivity); |
226 | 0 | aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); |
227 | 0 | aFrame->SetProperty(LayerActivityProperty(), layerActivity); |
228 | 0 | } |
229 | 0 | return layerActivity; |
230 | 0 | } |
231 | | |
232 | | static void |
233 | | IncrementMutationCount(uint8_t* aCount) |
234 | 0 | { |
235 | 0 | *aCount = uint8_t(std::min(0xFF, *aCount + 1)); |
236 | 0 | } |
237 | | |
238 | | /* static */ void |
239 | | ActiveLayerTracker::TransferActivityToContent(nsIFrame* aFrame, |
240 | | nsIContent* aContent) |
241 | 0 | { |
242 | 0 | if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) { |
243 | 0 | return; |
244 | 0 | } |
245 | 0 | LayerActivity* layerActivity = |
246 | 0 | aFrame->RemoveProperty(LayerActivityProperty()); |
247 | 0 | aFrame->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); |
248 | 0 | if (!layerActivity) { |
249 | 0 | return; |
250 | 0 | } |
251 | 0 | layerActivity->mFrame = nullptr; |
252 | 0 | layerActivity->mContent = aContent; |
253 | 0 | aContent->SetProperty(nsGkAtoms::LayerActivity, |
254 | 0 | layerActivity, |
255 | 0 | nsINode::DeleteProperty<LayerActivity>, |
256 | 0 | true); |
257 | 0 | } |
258 | | |
259 | | /* static */ void |
260 | | ActiveLayerTracker::TransferActivityToFrame(nsIContent* aContent, |
261 | | nsIFrame* aFrame) |
262 | 0 | { |
263 | 0 | LayerActivity* layerActivity = static_cast<LayerActivity*>( |
264 | 0 | aContent->UnsetProperty(nsGkAtoms::LayerActivity)); |
265 | 0 | if (!layerActivity) { |
266 | 0 | return; |
267 | 0 | } |
268 | 0 | layerActivity->mContent = nullptr; |
269 | 0 | layerActivity->mFrame = aFrame; |
270 | 0 | aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); |
271 | 0 | aFrame->SetProperty(LayerActivityProperty(), layerActivity); |
272 | 0 | } |
273 | | |
274 | | static void |
275 | | IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, LayerActivity* aActivity) |
276 | 0 | { |
277 | 0 | const nsStyleDisplay* display = aFrame->StyleDisplay(); |
278 | 0 | if (!display->mSpecifiedTransform && !display->HasIndividualTransform() && |
279 | 0 | !(display->mMotion && display->mMotion->HasPath())) { |
280 | 0 | // The transform was removed. |
281 | 0 | aActivity->mPreviousTransformScale = Nothing(); |
282 | 0 | IncrementMutationCount( |
283 | 0 | &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); |
284 | 0 | return; |
285 | 0 | } |
286 | 0 | |
287 | 0 | // Compute the new scale due to the CSS transform property. |
288 | 0 | bool dummyBool; |
289 | 0 | nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame); |
290 | 0 | Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms( |
291 | 0 | display->mIndividualTransform ? display->mIndividualTransform->mHead |
292 | 0 | : nullptr, |
293 | 0 | nsLayoutUtils::ResolveMotionPath(aFrame), |
294 | 0 | display->mSpecifiedTransform ? display->mSpecifiedTransform->mHead |
295 | 0 | : nullptr, |
296 | 0 | refBox, |
297 | 0 | AppUnitsPerCSSPixel(), |
298 | 0 | &dummyBool); |
299 | 0 | Matrix transform2D; |
300 | 0 | if (!transform.Is2D(&transform2D)) { |
301 | 0 | // We don't attempt to handle 3D transforms; just assume the scale changed. |
302 | 0 | aActivity->mPreviousTransformScale = Nothing(); |
303 | 0 | IncrementMutationCount( |
304 | 0 | &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); |
305 | 0 | return; |
306 | 0 | } |
307 | 0 | |
308 | 0 | Size scale = transform2D.ScaleFactors(true); |
309 | 0 | if (aActivity->mPreviousTransformScale == Some(scale)) { |
310 | 0 | return; // Nothing changed. |
311 | 0 | } |
312 | 0 | |
313 | 0 | aActivity->mPreviousTransformScale = Some(scale); |
314 | 0 | IncrementMutationCount( |
315 | 0 | &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); |
316 | 0 | } |
317 | | |
318 | | /* static */ void |
319 | | ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSPropertyID aProperty) |
320 | 0 | { |
321 | 0 | LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); |
322 | 0 | uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); |
323 | 0 | IncrementMutationCount(&mutationCount); |
324 | 0 |
|
325 | 0 | if (aProperty == eCSSProperty_transform) { |
326 | 0 | IncrementScaleRestyleCountIfNeeded(aFrame, layerActivity); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | /* static */ void |
331 | | ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame) |
332 | 0 | { |
333 | 0 | LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); |
334 | 0 | IncrementMutationCount( |
335 | 0 | &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT]); |
336 | 0 | IncrementMutationCount( |
337 | 0 | &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP]); |
338 | 0 | IncrementMutationCount( |
339 | 0 | &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT]); |
340 | 0 | IncrementMutationCount( |
341 | 0 | &layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_BOTTOM]); |
342 | 0 | } |
343 | | |
344 | | /* static */ void |
345 | | ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, |
346 | | nsCSSPropertyID aProperty, |
347 | | const nsAString& aNewValue, |
348 | | nsDOMCSSDeclaration* aDOMCSSDecl) |
349 | 0 | { |
350 | 0 | LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); |
351 | 0 | uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); |
352 | 0 | if (mutationCount != 0xFF) { |
353 | 0 | nsAutoString oldValue; |
354 | 0 | aDOMCSSDecl->GetPropertyValue(aProperty, oldValue); |
355 | 0 | if (aNewValue != oldValue) { |
356 | 0 | // We know this is animated, so just hack the mutation count. |
357 | 0 | mutationCount = 0xFF; |
358 | 0 | } |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | /* static */ void |
363 | | ActiveLayerTracker::NotifyAnimatedFromScrollHandler(nsIFrame* aFrame, |
364 | | nsCSSPropertyID aProperty, |
365 | | nsIFrame* aScrollFrame) |
366 | 0 | { |
367 | 0 | if (aFrame->PresContext() != aScrollFrame->PresContext()) { |
368 | 0 | // Don't allow cross-document dependencies. |
369 | 0 | return; |
370 | 0 | } |
371 | 0 | LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); |
372 | 0 | LayerActivity::ActivityIndex activityIndex = |
373 | 0 | LayerActivity::GetActivityIndexForProperty(aProperty); |
374 | 0 |
|
375 | 0 | if (layerActivity->mAnimatingScrollHandlerFrame.GetFrame() != aScrollFrame) { |
376 | 0 | // Discard any activity of a different scroll frame. We only track the |
377 | 0 | // most recent scroll handler induced activity. |
378 | 0 | layerActivity->mScrollHandlerInducedActivity.clear(); |
379 | 0 | layerActivity->mAnimatingScrollHandlerFrame = aScrollFrame; |
380 | 0 | } |
381 | 0 |
|
382 | 0 | layerActivity->mScrollHandlerInducedActivity += activityIndex; |
383 | 0 | } |
384 | | |
385 | | static bool |
386 | | IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext) |
387 | 0 | { |
388 | 0 | if (aPresContext->RefreshDriver()->IsInRefresh()) { |
389 | 0 | return true; |
390 | 0 | } |
391 | 0 | // Treat timeouts/setintervals as scripted animation callbacks for our |
392 | 0 | // purposes. |
393 | 0 | nsPIDOMWindowInner* win = aPresContext->Document()->GetInnerWindow(); |
394 | 0 | return win && win->IsRunningTimeout(); |
395 | 0 | } |
396 | | |
397 | | /* static */ void |
398 | | ActiveLayerTracker::NotifyInlineStyleRuleModified( |
399 | | nsIFrame* aFrame, |
400 | | nsCSSPropertyID aProperty, |
401 | | const nsAString& aNewValue, |
402 | | nsDOMCSSDeclaration* aDOMCSSDecl) |
403 | 0 | { |
404 | 0 | if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) { |
405 | 0 | NotifyAnimated(aFrame, aProperty, aNewValue, aDOMCSSDecl); |
406 | 0 | } |
407 | 0 | if (gLayerActivityTracker && |
408 | 0 | gLayerActivityTracker->mCurrentScrollHandlerFrame.IsAlive()) { |
409 | 0 | NotifyAnimatedFromScrollHandler( |
410 | 0 | aFrame, |
411 | 0 | aProperty, |
412 | 0 | gLayerActivityTracker->mCurrentScrollHandlerFrame.GetFrame()); |
413 | 0 | } |
414 | 0 | } |
415 | | |
416 | | /* static */ void |
417 | | ActiveLayerTracker::NotifyNeedsRepaint(nsIFrame* aFrame) |
418 | 0 | { |
419 | 0 | LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); |
420 | 0 | if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) { |
421 | 0 | // This is mirroring NotifyInlineStyleRuleModified's NotifyAnimated logic. |
422 | 0 | // Just max out the restyle count if we're in an animation callback. |
423 | 0 | layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] = |
424 | 0 | 0xFF; |
425 | 0 | } else { |
426 | 0 | IncrementMutationCount( |
427 | 0 | &layerActivity |
428 | 0 | ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT]); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | /* static */ bool |
433 | | ActiveLayerTracker::IsStyleMaybeAnimated(nsIFrame* aFrame, |
434 | | nsCSSPropertyID aProperty) |
435 | 0 | { |
436 | 0 | return IsStyleAnimated(nullptr, aFrame, aProperty); |
437 | 0 | } |
438 | | |
439 | | /* static */ bool |
440 | | ActiveLayerTracker::IsBackgroundPositionAnimated(nsDisplayListBuilder* aBuilder, |
441 | | nsIFrame* aFrame) |
442 | 0 | { |
443 | 0 | return IsStyleAnimated( |
444 | 0 | aBuilder, aFrame, eCSSProperty_background_position_x) || |
445 | 0 | IsStyleAnimated(aBuilder, aFrame, eCSSProperty_background_position_y); |
446 | 0 | } |
447 | | |
448 | | static bool |
449 | | CheckScrollInducedActivity(LayerActivity* aLayerActivity, |
450 | | LayerActivity::ActivityIndex aActivityIndex, |
451 | | nsDisplayListBuilder* aBuilder) |
452 | 0 | { |
453 | 0 | if (!aLayerActivity->mScrollHandlerInducedActivity.contains(aActivityIndex) || |
454 | 0 | !aLayerActivity->mAnimatingScrollHandlerFrame.IsAlive()) { |
455 | 0 | return false; |
456 | 0 | } |
457 | 0 | |
458 | 0 | nsIScrollableFrame* scrollFrame = |
459 | 0 | do_QueryFrame(aLayerActivity->mAnimatingScrollHandlerFrame.GetFrame()); |
460 | 0 | if (scrollFrame && (!aBuilder || scrollFrame->IsScrollingActive(aBuilder))) { |
461 | 0 | return true; |
462 | 0 | } |
463 | 0 | |
464 | 0 | // The scroll frame has been destroyed or has become inactive. Clear it from |
465 | 0 | // the layer activity so that it can expire. |
466 | 0 | aLayerActivity->mAnimatingScrollHandlerFrame = nullptr; |
467 | 0 | aLayerActivity->mScrollHandlerInducedActivity.clear(); |
468 | 0 | return false; |
469 | 0 | } |
470 | | |
471 | | /* static */ bool |
472 | | ActiveLayerTracker::IsStyleAnimated(nsDisplayListBuilder* aBuilder, |
473 | | nsIFrame* aFrame, |
474 | | nsCSSPropertyID aProperty) |
475 | 0 | { |
476 | 0 | // TODO: Add some abuse restrictions |
477 | 0 | if ((aFrame->StyleDisplay()->mWillChangeBitField & |
478 | 0 | NS_STYLE_WILL_CHANGE_TRANSFORM) && |
479 | 0 | aProperty == eCSSProperty_transform && |
480 | 0 | (!aBuilder || |
481 | 0 | aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) { |
482 | 0 | return true; |
483 | 0 | } |
484 | 0 | if ((aFrame->StyleDisplay()->mWillChangeBitField & |
485 | 0 | NS_STYLE_WILL_CHANGE_OPACITY) && |
486 | 0 | aProperty == eCSSProperty_opacity && |
487 | 0 | (!aBuilder || |
488 | 0 | aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) { |
489 | 0 | return true; |
490 | 0 | } |
491 | 0 | |
492 | 0 | LayerActivity* layerActivity = GetLayerActivity(aFrame); |
493 | 0 | if (layerActivity) { |
494 | 0 | LayerActivity::ActivityIndex activityIndex = |
495 | 0 | LayerActivity::GetActivityIndexForProperty(aProperty); |
496 | 0 | if (layerActivity->mRestyleCounts[activityIndex] >= 2) { |
497 | 0 | // If the frame needs to be repainted frequently, we probably don't get |
498 | 0 | // much from treating the property as animated, *unless* this frame's |
499 | 0 | // 'scale' (which includes the bounds changes of a rotation) is changing. |
500 | 0 | // Marking a scaling transform as animating allows us to avoid resizing |
501 | 0 | // the texture, even if we have to repaint the contents of that texture. |
502 | 0 | if (layerActivity |
503 | 0 | ->mRestyleCounts[LayerActivity::ACTIVITY_TRIGGERED_REPAINT] < 2 || |
504 | 0 | (aProperty == eCSSProperty_transform && |
505 | 0 | IsScaleSubjectToAnimation(aFrame))) { |
506 | 0 | return true; |
507 | 0 | } |
508 | 0 | } |
509 | 0 | if (CheckScrollInducedActivity(layerActivity, activityIndex, aBuilder)) { |
510 | 0 | return true; |
511 | 0 | } |
512 | 0 | } |
513 | 0 | if (aProperty == eCSSProperty_transform && |
514 | 0 | aFrame->Combines3DTransformWithAncestors()) { |
515 | 0 | return IsStyleAnimated(aBuilder, aFrame->GetParent(), aProperty); |
516 | 0 | } |
517 | 0 | return nsLayoutUtils::HasEffectiveAnimation(aFrame, aProperty); |
518 | 0 | } |
519 | | |
520 | | /* static */ bool |
521 | | ActiveLayerTracker::IsOffsetStyleAnimated(nsIFrame* aFrame) |
522 | 0 | { |
523 | 0 | LayerActivity* layerActivity = GetLayerActivity(aFrame); |
524 | 0 | if (layerActivity) { |
525 | 0 | if (layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_LEFT] >= 2 || |
526 | 0 | layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_TOP] >= 2 || |
527 | 0 | layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_RIGHT] >= 2 || |
528 | 0 | layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_BOTTOM] >= 2) { |
529 | 0 | return true; |
530 | 0 | } |
531 | 0 | } |
532 | 0 | // We should also check for running CSS animations of these properties once |
533 | 0 | // bug 1009693 is fixed. Until that happens, layerization isn't useful for |
534 | 0 | // animations of these properties because we'll invalidate the layer contents |
535 | 0 | // on every change anyway. |
536 | 0 | // See bug 1151346 for a patch that adds a check for CSS animations. |
537 | 0 | return false; |
538 | 0 | } |
539 | | |
540 | | /* static */ bool |
541 | | ActiveLayerTracker::IsScaleSubjectToAnimation(nsIFrame* aFrame) |
542 | 0 | { |
543 | 0 | // Check whether JavaScript is animating this frame's scale. |
544 | 0 | LayerActivity* layerActivity = GetLayerActivity(aFrame); |
545 | 0 | if (layerActivity && |
546 | 0 | layerActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE] >= 2) { |
547 | 0 | return true; |
548 | 0 | } |
549 | 0 | |
550 | 0 | // Check if any animations, transitions, etc. associated with this frame may |
551 | 0 | // animate its scale. |
552 | 0 | EffectSet* effects = EffectSet::GetEffectSet(aFrame); |
553 | 0 | if (effects && |
554 | 0 | AnimationUtils::EffectSetContainsAnimatedScale(*effects, aFrame)) { |
555 | 0 | return true; |
556 | 0 | } |
557 | 0 | |
558 | 0 | return false; |
559 | 0 | } |
560 | | |
561 | | /* static */ void |
562 | | ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame) |
563 | 0 | { |
564 | 0 | LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); |
565 | 0 | layerActivity->mContentActive = true; |
566 | 0 | } |
567 | | |
568 | | /* static */ bool |
569 | | ActiveLayerTracker::IsContentActive(nsIFrame* aFrame) |
570 | 0 | { |
571 | 0 | LayerActivity* layerActivity = GetLayerActivity(aFrame); |
572 | 0 | return layerActivity && layerActivity->mContentActive; |
573 | 0 | } |
574 | | |
575 | | /* static */ void |
576 | | ActiveLayerTracker::SetCurrentScrollHandlerFrame(nsIFrame* aFrame) |
577 | 0 | { |
578 | 0 | if (!gLayerActivityTracker) { |
579 | 0 | gLayerActivityTracker = new LayerActivityTracker( |
580 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other)); |
581 | 0 | } |
582 | 0 | gLayerActivityTracker->mCurrentScrollHandlerFrame = aFrame; |
583 | 0 | } |
584 | | |
585 | | /* static */ void |
586 | | ActiveLayerTracker::Shutdown() |
587 | 0 | { |
588 | 0 | delete gLayerActivityTracker; |
589 | 0 | gLayerActivityTracker = nullptr; |
590 | 0 | } |
591 | | |
592 | | } // namespace mozilla |