layout/base/ActiveLayerTracker.cpp

changeset 0
6474c204b198
     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 +}

mercurial