1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/ActiveLayerTracker.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,289 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "ActiveLayerTracker.h" 1.9 + 1.10 +#include "nsExpirationTracker.h" 1.11 +#include "nsIFrame.h" 1.12 +#include "nsIContent.h" 1.13 +#include "nsRefreshDriver.h" 1.14 +#include "nsPIDOMWindow.h" 1.15 +#include "nsIDocument.h" 1.16 +#include "nsAnimationManager.h" 1.17 +#include "nsTransitionManager.h" 1.18 + 1.19 +namespace mozilla { 1.20 + 1.21 +/** 1.22 + * This tracks the state of a frame that may need active layers due to 1.23 + * ongoing content changes or style changes that indicate animation. 1.24 + * 1.25 + * When no changes of *any* kind are detected after 75-100ms we remove this 1.26 + * object. Because we only track all kinds of activity with a single 1.27 + * nsExpirationTracker, it's possible a frame might remain active somewhat 1.28 + * spuriously if different kinds of changes kept happening, but that almost 1.29 + * certainly doesn't matter. 1.30 + */ 1.31 +class LayerActivity { 1.32 +public: 1.33 + LayerActivity(nsIFrame* aFrame) 1.34 + : mFrame(aFrame) 1.35 + , mOpacityRestyleCount(0) 1.36 + , mTransformRestyleCount(0) 1.37 + , mLeftRestyleCount(0) 1.38 + , mTopRestyleCount(0) 1.39 + , mRightRestyleCount(0) 1.40 + , mBottomRestyleCount(0) 1.41 + , mMarginLeftRestyleCount(0) 1.42 + , mMarginTopRestyleCount(0) 1.43 + , mMarginRightRestyleCount(0) 1.44 + , mMarginBottomRestyleCount(0) 1.45 + , mContentActive(false) 1.46 + {} 1.47 + ~LayerActivity(); 1.48 + nsExpirationState* GetExpirationState() { return &mState; } 1.49 + uint8_t& RestyleCountForProperty(nsCSSProperty aProperty) 1.50 + { 1.51 + switch (aProperty) { 1.52 + case eCSSProperty_opacity: return mOpacityRestyleCount; 1.53 + case eCSSProperty_transform: return mTransformRestyleCount; 1.54 + case eCSSProperty_left: return mLeftRestyleCount; 1.55 + case eCSSProperty_top: return mTopRestyleCount; 1.56 + case eCSSProperty_right: return mRightRestyleCount; 1.57 + case eCSSProperty_bottom: return mBottomRestyleCount; 1.58 + case eCSSProperty_margin_left: return mMarginLeftRestyleCount; 1.59 + case eCSSProperty_margin_top: return mMarginTopRestyleCount; 1.60 + case eCSSProperty_margin_right: return mMarginRightRestyleCount; 1.61 + case eCSSProperty_margin_bottom: return mMarginBottomRestyleCount; 1.62 + default: MOZ_ASSERT(false); return mOpacityRestyleCount; 1.63 + } 1.64 + } 1.65 + 1.66 + nsIFrame* mFrame; 1.67 + nsExpirationState mState; 1.68 + // Number of restyle operations detected 1.69 + uint8_t mOpacityRestyleCount; 1.70 + uint8_t mTransformRestyleCount; 1.71 + uint8_t mLeftRestyleCount; 1.72 + uint8_t mTopRestyleCount; 1.73 + uint8_t mRightRestyleCount; 1.74 + uint8_t mBottomRestyleCount; 1.75 + uint8_t mMarginLeftRestyleCount; 1.76 + uint8_t mMarginTopRestyleCount; 1.77 + uint8_t mMarginRightRestyleCount; 1.78 + uint8_t mMarginBottomRestyleCount; 1.79 + bool mContentActive; 1.80 +}; 1.81 + 1.82 +class LayerActivityTracker MOZ_FINAL : public nsExpirationTracker<LayerActivity,4> { 1.83 +public: 1.84 + // 75-100ms is a good timeout period. We use 4 generations of 25ms each. 1.85 + enum { GENERATION_MS = 100 }; 1.86 + LayerActivityTracker() 1.87 + : nsExpirationTracker<LayerActivity,4>(GENERATION_MS) {} 1.88 + ~LayerActivityTracker() { 1.89 + AgeAllGenerations(); 1.90 + } 1.91 + 1.92 + virtual void NotifyExpired(LayerActivity* aObject); 1.93 +}; 1.94 + 1.95 +static LayerActivityTracker* gLayerActivityTracker = nullptr; 1.96 + 1.97 +LayerActivity::~LayerActivity() 1.98 +{ 1.99 + if (mFrame) { 1.100 + NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker"); 1.101 + gLayerActivityTracker->RemoveObject(this); 1.102 + } 1.103 +} 1.104 + 1.105 +static void DestroyLayerActivity(void* aPropertyValue) 1.106 +{ 1.107 + delete static_cast<LayerActivity*>(aPropertyValue); 1.108 +} 1.109 + 1.110 +// Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set 1.111 +NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DestroyLayerActivity) 1.112 + 1.113 +void 1.114 +LayerActivityTracker::NotifyExpired(LayerActivity* aObject) 1.115 +{ 1.116 + RemoveObject(aObject); 1.117 + 1.118 + nsIFrame* f = aObject->mFrame; 1.119 + aObject->mFrame = nullptr; 1.120 + 1.121 + // The pres context might have been detached during the delay - 1.122 + // that's fine, just skip the paint. 1.123 + if (f->PresContext()->GetContainerWeak()) { 1.124 + f->SchedulePaint(); 1.125 + } 1.126 + f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); 1.127 + f->Properties().Delete(LayerActivityProperty()); 1.128 +} 1.129 + 1.130 +static LayerActivity* 1.131 +GetLayerActivity(nsIFrame* aFrame) 1.132 +{ 1.133 + if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) { 1.134 + return nullptr; 1.135 + } 1.136 + FrameProperties properties = aFrame->Properties(); 1.137 + return static_cast<LayerActivity*>(properties.Get(LayerActivityProperty())); 1.138 +} 1.139 + 1.140 +static LayerActivity* 1.141 +GetLayerActivityForUpdate(nsIFrame* aFrame) 1.142 +{ 1.143 + FrameProperties properties = aFrame->Properties(); 1.144 + LayerActivity* layerActivity = 1.145 + static_cast<LayerActivity*>(properties.Get(LayerActivityProperty())); 1.146 + if (layerActivity) { 1.147 + gLayerActivityTracker->MarkUsed(layerActivity); 1.148 + } else { 1.149 + if (!gLayerActivityTracker) { 1.150 + gLayerActivityTracker = new LayerActivityTracker(); 1.151 + } 1.152 + layerActivity = new LayerActivity(aFrame); 1.153 + gLayerActivityTracker->AddObject(layerActivity); 1.154 + aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY); 1.155 + properties.Set(LayerActivityProperty(), layerActivity); 1.156 + } 1.157 + return layerActivity; 1.158 +} 1.159 + 1.160 +static void 1.161 +IncrementMutationCount(uint8_t* aCount) 1.162 +{ 1.163 + *aCount = uint8_t(std::min(0xFF, *aCount + 1)); 1.164 +} 1.165 + 1.166 +/* static */ void 1.167 +ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty) 1.168 +{ 1.169 + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); 1.170 + uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); 1.171 + IncrementMutationCount(&mutationCount); 1.172 +} 1.173 + 1.174 +/* static */ void 1.175 +ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame) 1.176 +{ 1.177 + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); 1.178 + IncrementMutationCount(&layerActivity->mLeftRestyleCount); 1.179 + IncrementMutationCount(&layerActivity->mTopRestyleCount); 1.180 + IncrementMutationCount(&layerActivity->mRightRestyleCount); 1.181 + IncrementMutationCount(&layerActivity->mBottomRestyleCount); 1.182 +} 1.183 + 1.184 +/* static */ void 1.185 +ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty) 1.186 +{ 1.187 + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); 1.188 + uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); 1.189 + // We know this is animated, so just hack the mutation count. 1.190 + mutationCount = 0xFF; 1.191 +} 1.192 + 1.193 +static bool 1.194 +IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext) 1.195 +{ 1.196 + if (aPresContext->RefreshDriver()->IsInRefresh()) { 1.197 + return true; 1.198 + } 1.199 + // Treat timeouts/setintervals as scripted animation callbacks for our 1.200 + // purposes. 1.201 + nsPIDOMWindow* win = aPresContext->Document()->GetInnerWindow(); 1.202 + return win && win->IsRunningTimeout(); 1.203 +} 1.204 + 1.205 +/* static */ void 1.206 +ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame, 1.207 + nsCSSProperty aProperty) 1.208 +{ 1.209 + if (!IsPresContextInScriptAnimationCallback(aFrame->PresContext())) { 1.210 + return; 1.211 + } 1.212 + NotifyAnimated(aFrame, aProperty); 1.213 +} 1.214 + 1.215 +/* static */ bool 1.216 +ActiveLayerTracker::IsStyleAnimated(nsIFrame* aFrame, nsCSSProperty aProperty) 1.217 +{ 1.218 + // TODO: Add some abuse restrictions 1.219 + if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) && 1.220 + aProperty == eCSSProperty_transform) { 1.221 + return true; 1.222 + } 1.223 + if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && 1.224 + aProperty == eCSSProperty_opacity) { 1.225 + return true; 1.226 + } 1.227 + 1.228 + LayerActivity* layerActivity = GetLayerActivity(aFrame); 1.229 + if (layerActivity) { 1.230 + if (layerActivity->RestyleCountForProperty(aProperty) >= 2) { 1.231 + return true; 1.232 + } 1.233 + } 1.234 + if (aProperty == eCSSProperty_transform && aFrame->Preserves3D()) { 1.235 + return IsStyleAnimated(aFrame->GetParent(), aProperty); 1.236 + } 1.237 + nsIContent* content = aFrame->GetContent(); 1.238 + if (content) { 1.239 + if (mozilla::HasAnimationOrTransition<ElementAnimations>( 1.240 + content, nsGkAtoms::animationsProperty, aProperty)) { 1.241 + return true; 1.242 + } 1.243 + if (mozilla::HasAnimationOrTransition<ElementTransitions>( 1.244 + content, nsGkAtoms::transitionsProperty, aProperty)) { 1.245 + return true; 1.246 + } 1.247 + } 1.248 + 1.249 + return false; 1.250 +} 1.251 + 1.252 +/* static */ bool 1.253 +ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame* aFrame) 1.254 +{ 1.255 + LayerActivity* layerActivity = GetLayerActivity(aFrame); 1.256 + if (layerActivity) { 1.257 + if (layerActivity->mLeftRestyleCount >= 2 || 1.258 + layerActivity->mTopRestyleCount >= 2 || 1.259 + layerActivity->mRightRestyleCount >= 2 || 1.260 + layerActivity->mBottomRestyleCount >= 2 || 1.261 + layerActivity->mMarginLeftRestyleCount >= 2 || 1.262 + layerActivity->mMarginTopRestyleCount >= 2 || 1.263 + layerActivity->mMarginRightRestyleCount >= 2 || 1.264 + layerActivity->mMarginBottomRestyleCount >= 2) { 1.265 + return true; 1.266 + } 1.267 + } 1.268 + return false; 1.269 +} 1.270 + 1.271 +/* static */ void 1.272 +ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame) 1.273 +{ 1.274 + LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); 1.275 + layerActivity->mContentActive = true; 1.276 +} 1.277 + 1.278 +/* static */ bool 1.279 +ActiveLayerTracker::IsContentActive(nsIFrame* aFrame) 1.280 +{ 1.281 + LayerActivity* layerActivity = GetLayerActivity(aFrame); 1.282 + return layerActivity && layerActivity->mContentActive; 1.283 +} 1.284 + 1.285 +/* static */ void 1.286 +ActiveLayerTracker::Shutdown() 1.287 +{ 1.288 + delete gLayerActivityTracker; 1.289 + gLayerActivityTracker = nullptr; 1.290 +} 1.291 + 1.292 +}