layout/base/ActiveLayerTracker.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "ActiveLayerTracker.h"
michael@0 6
michael@0 7 #include "nsExpirationTracker.h"
michael@0 8 #include "nsIFrame.h"
michael@0 9 #include "nsIContent.h"
michael@0 10 #include "nsRefreshDriver.h"
michael@0 11 #include "nsPIDOMWindow.h"
michael@0 12 #include "nsIDocument.h"
michael@0 13 #include "nsAnimationManager.h"
michael@0 14 #include "nsTransitionManager.h"
michael@0 15
michael@0 16 namespace mozilla {
michael@0 17
michael@0 18 /**
michael@0 19 * This tracks the state of a frame that may need active layers due to
michael@0 20 * ongoing content changes or style changes that indicate animation.
michael@0 21 *
michael@0 22 * When no changes of *any* kind are detected after 75-100ms we remove this
michael@0 23 * object. Because we only track all kinds of activity with a single
michael@0 24 * nsExpirationTracker, it's possible a frame might remain active somewhat
michael@0 25 * spuriously if different kinds of changes kept happening, but that almost
michael@0 26 * certainly doesn't matter.
michael@0 27 */
michael@0 28 class LayerActivity {
michael@0 29 public:
michael@0 30 LayerActivity(nsIFrame* aFrame)
michael@0 31 : mFrame(aFrame)
michael@0 32 , mOpacityRestyleCount(0)
michael@0 33 , mTransformRestyleCount(0)
michael@0 34 , mLeftRestyleCount(0)
michael@0 35 , mTopRestyleCount(0)
michael@0 36 , mRightRestyleCount(0)
michael@0 37 , mBottomRestyleCount(0)
michael@0 38 , mMarginLeftRestyleCount(0)
michael@0 39 , mMarginTopRestyleCount(0)
michael@0 40 , mMarginRightRestyleCount(0)
michael@0 41 , mMarginBottomRestyleCount(0)
michael@0 42 , mContentActive(false)
michael@0 43 {}
michael@0 44 ~LayerActivity();
michael@0 45 nsExpirationState* GetExpirationState() { return &mState; }
michael@0 46 uint8_t& RestyleCountForProperty(nsCSSProperty aProperty)
michael@0 47 {
michael@0 48 switch (aProperty) {
michael@0 49 case eCSSProperty_opacity: return mOpacityRestyleCount;
michael@0 50 case eCSSProperty_transform: return mTransformRestyleCount;
michael@0 51 case eCSSProperty_left: return mLeftRestyleCount;
michael@0 52 case eCSSProperty_top: return mTopRestyleCount;
michael@0 53 case eCSSProperty_right: return mRightRestyleCount;
michael@0 54 case eCSSProperty_bottom: return mBottomRestyleCount;
michael@0 55 case eCSSProperty_margin_left: return mMarginLeftRestyleCount;
michael@0 56 case eCSSProperty_margin_top: return mMarginTopRestyleCount;
michael@0 57 case eCSSProperty_margin_right: return mMarginRightRestyleCount;
michael@0 58 case eCSSProperty_margin_bottom: return mMarginBottomRestyleCount;
michael@0 59 default: MOZ_ASSERT(false); return mOpacityRestyleCount;
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 nsIFrame* mFrame;
michael@0 64 nsExpirationState mState;
michael@0 65 // Number of restyle operations detected
michael@0 66 uint8_t mOpacityRestyleCount;
michael@0 67 uint8_t mTransformRestyleCount;
michael@0 68 uint8_t mLeftRestyleCount;
michael@0 69 uint8_t mTopRestyleCount;
michael@0 70 uint8_t mRightRestyleCount;
michael@0 71 uint8_t mBottomRestyleCount;
michael@0 72 uint8_t mMarginLeftRestyleCount;
michael@0 73 uint8_t mMarginTopRestyleCount;
michael@0 74 uint8_t mMarginRightRestyleCount;
michael@0 75 uint8_t mMarginBottomRestyleCount;
michael@0 76 bool mContentActive;
michael@0 77 };
michael@0 78
michael@0 79 class LayerActivityTracker MOZ_FINAL : public nsExpirationTracker<LayerActivity,4> {
michael@0 80 public:
michael@0 81 // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
michael@0 82 enum { GENERATION_MS = 100 };
michael@0 83 LayerActivityTracker()
michael@0 84 : nsExpirationTracker<LayerActivity,4>(GENERATION_MS) {}
michael@0 85 ~LayerActivityTracker() {
michael@0 86 AgeAllGenerations();
michael@0 87 }
michael@0 88
michael@0 89 virtual void NotifyExpired(LayerActivity* aObject);
michael@0 90 };
michael@0 91
michael@0 92 static LayerActivityTracker* gLayerActivityTracker = nullptr;
michael@0 93
michael@0 94 LayerActivity::~LayerActivity()
michael@0 95 {
michael@0 96 if (mFrame) {
michael@0 97 NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
michael@0 98 gLayerActivityTracker->RemoveObject(this);
michael@0 99 }
michael@0 100 }
michael@0 101
michael@0 102 static void DestroyLayerActivity(void* aPropertyValue)
michael@0 103 {
michael@0 104 delete static_cast<LayerActivity*>(aPropertyValue);
michael@0 105 }
michael@0 106
michael@0 107 // Frames with this property have NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY set
michael@0 108 NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DestroyLayerActivity)
michael@0 109
michael@0 110 void
michael@0 111 LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
michael@0 112 {
michael@0 113 RemoveObject(aObject);
michael@0 114
michael@0 115 nsIFrame* f = aObject->mFrame;
michael@0 116 aObject->mFrame = nullptr;
michael@0 117
michael@0 118 // The pres context might have been detached during the delay -
michael@0 119 // that's fine, just skip the paint.
michael@0 120 if (f->PresContext()->GetContainerWeak()) {
michael@0 121 f->SchedulePaint();
michael@0 122 }
michael@0 123 f->RemoveStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
michael@0 124 f->Properties().Delete(LayerActivityProperty());
michael@0 125 }
michael@0 126
michael@0 127 static LayerActivity*
michael@0 128 GetLayerActivity(nsIFrame* aFrame)
michael@0 129 {
michael@0 130 if (!aFrame->HasAnyStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY)) {
michael@0 131 return nullptr;
michael@0 132 }
michael@0 133 FrameProperties properties = aFrame->Properties();
michael@0 134 return static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
michael@0 135 }
michael@0 136
michael@0 137 static LayerActivity*
michael@0 138 GetLayerActivityForUpdate(nsIFrame* aFrame)
michael@0 139 {
michael@0 140 FrameProperties properties = aFrame->Properties();
michael@0 141 LayerActivity* layerActivity =
michael@0 142 static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
michael@0 143 if (layerActivity) {
michael@0 144 gLayerActivityTracker->MarkUsed(layerActivity);
michael@0 145 } else {
michael@0 146 if (!gLayerActivityTracker) {
michael@0 147 gLayerActivityTracker = new LayerActivityTracker();
michael@0 148 }
michael@0 149 layerActivity = new LayerActivity(aFrame);
michael@0 150 gLayerActivityTracker->AddObject(layerActivity);
michael@0 151 aFrame->AddStateBits(NS_FRAME_HAS_LAYER_ACTIVITY_PROPERTY);
michael@0 152 properties.Set(LayerActivityProperty(), layerActivity);
michael@0 153 }
michael@0 154 return layerActivity;
michael@0 155 }
michael@0 156
michael@0 157 static void
michael@0 158 IncrementMutationCount(uint8_t* aCount)
michael@0 159 {
michael@0 160 *aCount = uint8_t(std::min(0xFF, *aCount + 1));
michael@0 161 }
michael@0 162
michael@0 163 /* static */ void
michael@0 164 ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty)
michael@0 165 {
michael@0 166 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
michael@0 167 uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
michael@0 168 IncrementMutationCount(&mutationCount);
michael@0 169 }
michael@0 170
michael@0 171 /* static */ void
michael@0 172 ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame)
michael@0 173 {
michael@0 174 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
michael@0 175 IncrementMutationCount(&layerActivity->mLeftRestyleCount);
michael@0 176 IncrementMutationCount(&layerActivity->mTopRestyleCount);
michael@0 177 IncrementMutationCount(&layerActivity->mRightRestyleCount);
michael@0 178 IncrementMutationCount(&layerActivity->mBottomRestyleCount);
michael@0 179 }
michael@0 180
michael@0 181 /* static */ void
michael@0 182 ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
michael@0 183 {
michael@0 184 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
michael@0 185 uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
michael@0 186 // We know this is animated, so just hack the mutation count.
michael@0 187 mutationCount = 0xFF;
michael@0 188 }
michael@0 189
michael@0 190 static bool
michael@0 191 IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext)
michael@0 192 {
michael@0 193 if (aPresContext->RefreshDriver()->IsInRefresh()) {
michael@0 194 return true;
michael@0 195 }
michael@0 196 // Treat timeouts/setintervals as scripted animation callbacks for our
michael@0 197 // purposes.
michael@0 198 nsPIDOMWindow* win = aPresContext->Document()->GetInnerWindow();
michael@0 199 return win && win->IsRunningTimeout();
michael@0 200 }
michael@0 201
michael@0 202 /* static */ void
michael@0 203 ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame,
michael@0 204 nsCSSProperty aProperty)
michael@0 205 {
michael@0 206 if (!IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
michael@0 207 return;
michael@0 208 }
michael@0 209 NotifyAnimated(aFrame, aProperty);
michael@0 210 }
michael@0 211
michael@0 212 /* static */ bool
michael@0 213 ActiveLayerTracker::IsStyleAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
michael@0 214 {
michael@0 215 // TODO: Add some abuse restrictions
michael@0 216 if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_TRANSFORM) &&
michael@0 217 aProperty == eCSSProperty_transform) {
michael@0 218 return true;
michael@0 219 }
michael@0 220 if ((aFrame->StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
michael@0 221 aProperty == eCSSProperty_opacity) {
michael@0 222 return true;
michael@0 223 }
michael@0 224
michael@0 225 LayerActivity* layerActivity = GetLayerActivity(aFrame);
michael@0 226 if (layerActivity) {
michael@0 227 if (layerActivity->RestyleCountForProperty(aProperty) >= 2) {
michael@0 228 return true;
michael@0 229 }
michael@0 230 }
michael@0 231 if (aProperty == eCSSProperty_transform && aFrame->Preserves3D()) {
michael@0 232 return IsStyleAnimated(aFrame->GetParent(), aProperty);
michael@0 233 }
michael@0 234 nsIContent* content = aFrame->GetContent();
michael@0 235 if (content) {
michael@0 236 if (mozilla::HasAnimationOrTransition<ElementAnimations>(
michael@0 237 content, nsGkAtoms::animationsProperty, aProperty)) {
michael@0 238 return true;
michael@0 239 }
michael@0 240 if (mozilla::HasAnimationOrTransition<ElementTransitions>(
michael@0 241 content, nsGkAtoms::transitionsProperty, aProperty)) {
michael@0 242 return true;
michael@0 243 }
michael@0 244 }
michael@0 245
michael@0 246 return false;
michael@0 247 }
michael@0 248
michael@0 249 /* static */ bool
michael@0 250 ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame* aFrame)
michael@0 251 {
michael@0 252 LayerActivity* layerActivity = GetLayerActivity(aFrame);
michael@0 253 if (layerActivity) {
michael@0 254 if (layerActivity->mLeftRestyleCount >= 2 ||
michael@0 255 layerActivity->mTopRestyleCount >= 2 ||
michael@0 256 layerActivity->mRightRestyleCount >= 2 ||
michael@0 257 layerActivity->mBottomRestyleCount >= 2 ||
michael@0 258 layerActivity->mMarginLeftRestyleCount >= 2 ||
michael@0 259 layerActivity->mMarginTopRestyleCount >= 2 ||
michael@0 260 layerActivity->mMarginRightRestyleCount >= 2 ||
michael@0 261 layerActivity->mMarginBottomRestyleCount >= 2) {
michael@0 262 return true;
michael@0 263 }
michael@0 264 }
michael@0 265 return false;
michael@0 266 }
michael@0 267
michael@0 268 /* static */ void
michael@0 269 ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame)
michael@0 270 {
michael@0 271 LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
michael@0 272 layerActivity->mContentActive = true;
michael@0 273 }
michael@0 274
michael@0 275 /* static */ bool
michael@0 276 ActiveLayerTracker::IsContentActive(nsIFrame* aFrame)
michael@0 277 {
michael@0 278 LayerActivity* layerActivity = GetLayerActivity(aFrame);
michael@0 279 return layerActivity && layerActivity->mContentActive;
michael@0 280 }
michael@0 281
michael@0 282 /* static */ void
michael@0 283 ActiveLayerTracker::Shutdown()
michael@0 284 {
michael@0 285 delete gLayerActivityTracker;
michael@0 286 gLayerActivityTracker = nullptr;
michael@0 287 }
michael@0 288
michael@0 289 }

mercurial