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

     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 }

mercurial