Back out 0a88dee3b92b:dc5cc7d7b84d (bug 911889) for beaucoup unexpected assertions
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 28 Oct 2013 23:38:02 -0700
changeset 152604 cd94525c17a419e540590d4e7f8e00467bd91ae7
parent 152603 ccd0c7234886b01408c3eaed46c15f0ee8c4241d
child 152605 194ba1b8f191cab6f5f97105b33d1b99b13148d4
child 152657 bc3916e4f4c5b022bbb13e47e75eb94756383994
push id35590
push userphilringnalda@gmail.com
push dateTue, 29 Oct 2013 06:38:28 +0000
treeherdermozilla-inbound@cd94525c17a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs911889
milestone28.0a1
backs out0a88dee3b92b0b232e4855e1b236cad41c2d509a
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Back out 0a88dee3b92b:dc5cc7d7b84d (bug 911889) for beaucoup unexpected assertions
content/html/content/src/HTMLCanvasElement.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/plugins/test/mochitest/test_painting.html
layout/base/ActiveLayerTracker.cpp
layout/base/ActiveLayerTracker.h
layout/base/FrameLayerBuilder.cpp
layout/base/RestyleManager.cpp
layout/base/moz.build
layout/base/nsDisplayList.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
layout/build/nsLayoutStatics.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsIFrame.h
layout/style/nsAnimationManager.cpp
layout/style/nsDOMCSSAttrDeclaration.cpp
layout/style/nsDOMCSSAttrDeclaration.h
layout/style/nsTransitionManager.cpp
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -28,17 +28,16 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsITimer.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsMathUtils.h"
 #include "nsNetUtil.h"
 #include "nsStreamUtils.h"
-#include "ActiveLayerTracker.h"
 
 #ifdef MOZ_WEBGL
 #include "../canvas/src/WebGL2Context.h"
 #endif
 
 using namespace mozilla::layers;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
@@ -831,17 +830,17 @@ void
 HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect)
 {
   // We don't need to flush anything here; if there's no frame or if
   // we plan to reframe we don't need to invalidate it anyway.
   nsIFrame *frame = GetPrimaryFrame();
   if (!frame)
     return;
 
-  ActiveLayerTracker::NotifyContentChange(frame);
+  frame->MarkLayersActive(nsChangeHint(0));
 
   Layer* layer = nullptr;
   if (damageRect) {
     nsIntSize size = GetWidthHeight();
     if (size.width != 0 && size.height != 0) {
 
       gfx::Rect realRect(*damageRect);
       realRect.RoundOut();
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -429,17 +429,16 @@ public:
                                            bool aFreezeChildren = true);
   virtual NS_HIDDEN_(nsresult) ResumeTimeouts(bool aThawChildren = true);
   virtual NS_HIDDEN_(uint32_t) TimeoutSuspendCount();
   virtual NS_HIDDEN_(nsresult) FireDelayedDOMEvents();
   virtual NS_HIDDEN_(bool) IsFrozen() const
   {
     return mIsFrozen;
   }
-  virtual NS_HIDDEN_(bool) IsRunningTimeout() { return mTimeoutFiringDepth > 0; }
 
   virtual NS_HIDDEN_(bool) WouldReuseInnerWindow(nsIDocument *aNewDocument);
 
   virtual NS_HIDDEN_(void) SetDocShell(nsIDocShell* aDocShell);
   virtual void DetachFromDocShell();
   virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
                                               nsISupports *aState,
                                               bool aForceReuseInnerWindow);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -184,18 +184,16 @@ public:
   nsIDocument* GetDoc()
   {
     if (!mDoc) {
       MaybeCreateDoc();
     }
     return mDoc;
   }
 
-  virtual NS_HIDDEN_(bool) IsRunningTimeout() = 0;
-
 protected:
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
 public:
   // Internal getter/setter for the frame element, this version of the
   // getter crosses chrome boundaries whereas the public scriptable
--- a/dom/plugins/test/mochitest/test_painting.html
+++ b/dom/plugins/test/mochitest/test_painting.html
@@ -92,17 +92,17 @@ function waitForPaint(func) {
 
 function waitForPaintHelper(func) {
   if (paint_waiter.getPaintCount() != paint_waiter.last_paint_count) {
     // hide the paint waiter
     paint_waiter.style.height = "0px";
     setTimeout(func, 0);
     return;
   }
-  setTimeout(function() { waitForPaintHelper(func); }, 1000);
+  setTimeout(function() { waitForPaintHelper(func); }, 100);
 }
 
 </script>
 
 <p id="display"></p>
 <div id="container">
   <embed id="paint-waiter" type="application/x-test"/>
   <div id="clip">
deleted file mode 100644
--- a/layout/base/ActiveLayerTracker.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ActiveLayerTracker.h"
-
-#include "nsExpirationTracker.h"
-#include "nsIFrame.h"
-#include "nsIContent.h"
-#include "nsRefreshDriver.h"
-#include "nsPIDOMWindow.h"
-#include "nsIDocument.h"
-
-namespace mozilla {
-
-/**
- * This tracks the state of a frame that may need active layers due to
- * ongoing content changes or style changes that indicate animation.
- *
- * When no changes of *any* kind are detected after 75-100ms we remove this
- * object. Because we only track all kinds of activity with a single
- * nsExpirationTracker, it's possible a frame might remain active somewhat
- * spuriously if different kinds of changes kept happening, but that almost
- * certainly doesn't matter.
- */
-class LayerActivity {
-public:
-  LayerActivity(nsIFrame* aFrame)
-    : mFrame(aFrame)
-    , mOpacityRestyleCount(0)
-    , mTransformRestyleCount(0)
-    , mContentActive(false)
-  {}
-  ~LayerActivity();
-  nsExpirationState* GetExpirationState() { return &mState; }
-  uint8_t& RestyleCountForProperty(nsCSSProperty aProperty)
-  {
-    switch (aProperty) {
-    case eCSSProperty_opacity: return mOpacityRestyleCount;
-    case eCSSProperty_transform: return mTransformRestyleCount;
-    default: MOZ_ASSERT(false); return mOpacityRestyleCount;
-    }
-  }
-
-  nsIFrame* mFrame;
-  nsExpirationState mState;
-  // Number of restyle operations detected
-  uint8_t mOpacityRestyleCount;
-  uint8_t mTransformRestyleCount;
-  bool mContentActive;
-};
-
-class LayerActivityTracker MOZ_FINAL : public nsExpirationTracker<LayerActivity,4> {
-public:
-  // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
-  enum { GENERATION_MS = 100 };
-  LayerActivityTracker()
-    : nsExpirationTracker<LayerActivity,4>(GENERATION_MS) {}
-  ~LayerActivityTracker() {
-    AgeAllGenerations();
-  }
-
-  virtual void NotifyExpired(LayerActivity* aObject);
-};
-
-static LayerActivityTracker* gLayerActivityTracker = nullptr;
-
-LayerActivity::~LayerActivity()
-{
-  if (mFrame) {
-    NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
-    gLayerActivityTracker->RemoveObject(this);
-  }
-}
-
-static void DestroyLayerActivity(void* aPropertyValue)
-{
-  delete static_cast<LayerActivity*>(aPropertyValue);
-}
-
-NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DestroyLayerActivity)
-
-void
-LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
-{
-  RemoveObject(aObject);
-
-  nsIFrame* f = aObject->mFrame;
-  aObject->mFrame = nullptr;
-
-  f->SchedulePaint();
-  f->Properties().Delete(LayerActivityProperty());
-}
-
-static LayerActivity*
-GetLayerActivity(nsIFrame* aFrame)
-{
-  FrameProperties properties = aFrame->Properties();
-  return static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
-}
-
-static LayerActivity*
-GetLayerActivityForUpdate(nsIFrame* aFrame)
-{
-  FrameProperties properties = aFrame->Properties();
-  LayerActivity* layerActivity =
-    static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
-  if (layerActivity) {
-    gLayerActivityTracker->MarkUsed(layerActivity);
-  } else {
-    if (!gLayerActivityTracker) {
-      gLayerActivityTracker = new LayerActivityTracker();
-    }
-    layerActivity = new LayerActivity(aFrame);
-    gLayerActivityTracker->AddObject(layerActivity);
-    properties.Set(LayerActivityProperty(), layerActivity);
-  }
-  return layerActivity;
-}
-
-/* static */ void
-ActiveLayerTracker::NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty)
-{
-  LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
-  uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
-  mutationCount = uint8_t(std::min(0xFF, mutationCount + 1));
-}
-
-/* static */ void
-ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
-{
-  LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
-  uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty);
-  // We know this is animated, so just hack the mutation count.
-  mutationCount = 0xFF;
-}
-
-static bool
-IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext)
-{
-  if (aPresContext->RefreshDriver()->IsInRefresh()) {
-    return true;
-  }
-  // Treat timeouts/setintervals as scripted animation callbacks for our
-  // purposes.
-  nsPIDOMWindow* win = aPresContext->Document()->GetInnerWindow();
-  return win && win->IsRunningTimeout();
-}
-
-/* static */ void
-ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame,
-                                                  nsCSSProperty aProperty)
-{
-  if (!IsPresContextInScriptAnimationCallback(aFrame->PresContext())) {
-    return;
-  }
-  NotifyAnimated(aFrame, aProperty);
-}
-
-/* static */ bool
-ActiveLayerTracker::IsStyleAnimated(nsIFrame* aFrame, nsCSSProperty aProperty)
-{
-  LayerActivity* layerActivity = GetLayerActivity(aFrame);
-  if (layerActivity) {
-    if (layerActivity->RestyleCountForProperty(aProperty) >= 2) {
-      return true;
-    }
-  }
-  if (aProperty == eCSSProperty_transform && aFrame->Preserves3D()) {
-    return IsStyleAnimated(aFrame->GetParent(), aProperty);
-  }
-  return false;
-}
-
-/* static */ void
-ActiveLayerTracker::NotifyContentChange(nsIFrame* aFrame)
-{
-  LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame);
-  layerActivity->mContentActive = true;
-}
-
-/* static */ bool
-ActiveLayerTracker::IsContentActive(nsIFrame* aFrame)
-{
-  LayerActivity* layerActivity = GetLayerActivity(aFrame);
-  return layerActivity && layerActivity->mContentActive;
-}
-
-/* static */ void
-ActiveLayerTracker::Shutdown()
-{
-  delete gLayerActivityTracker;
-  gLayerActivityTracker = nullptr;
-}
-
-}
deleted file mode 100644
--- a/layout/base/ActiveLayerTracker.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef ACTIVELAYERTRACKER_H_
-#define ACTIVELAYERTRACKER_H_
-
-#include "nsCSSProperty.h"
-
-class nsIFrame;
-
-namespace mozilla {
-
-/**
- * This class receives various notifications about style changes and content
- * changes that affect layerization decisions, and implements the heuristics
- * that drive those decisions. It manages per-frame state to support those
- * heuristics.
- */
-class ActiveLayerTracker {
-public:
-  static void Shutdown();
-
-  /*
-   * We track eCSSProperty_transform and eCSSProperty_opacity style changes
-   * and use that information to guess whether style changes are animated.
-   */
-
-  /**
-   * Notify aFrame's style property as having changed due to a restyle,
-   * and therefore possibly wanting an active layer to render that style.
-   * Any such marking will time out after a short period.
-   * @param aProperty the property that has changed
-   */
-  static void NotifyRestyle(nsIFrame* aFrame, nsCSSProperty aProperty);
-  /**
-   * Mark aFrame as being known to have an animation of aProperty.
-   * Any such marking will time out after a short period.
-   */
-  static void NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty);
-  /**
-   * Notify that a property in the inline style rule of aFrame's element
-   * has been modified.
-   * This notification is incomplete --- not all modifications to inline
-   * style will trigger this.
-   */
-  static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSProperty aProperty);
-  /**
-   * Return true if aFrame's aProperty style should be considered as being animated
-   * for constructing active layers.
-   */
-  static bool IsStyleAnimated(nsIFrame* aFrame, nsCSSProperty aProperty);
-
-  /*
-   * We track modifications to the content of certain frames (i.e. canvas frames)
-   * and use that to make layering decisions.
-   */
-
-  /**
-   * Mark aFrame's content as being active. This marking will time out after
-   * a short period.
-   */
-  static void NotifyContentChange(nsIFrame* aFrame);
-  /**
-   * Return true if this frame's content is still marked as active.
-   */
-  static bool IsContentActive(nsIFrame* aFrame);
-};
-
-}
-
-#endif /* ACTIVELAYERTRACKER_H_ */
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -15,17 +15,16 @@
 #include "gfxUtils.h"
 #include "nsRenderingContext.h"
 #include "MaskLayerImageCache.h"
 #include "nsIScrollableFrame.h"
 #include "nsPrintfCString.h"
 #include "LayerTreeInvalidation.h"
 #include "nsSVGIntegrationUtils.h"
 #include "ImageContainer.h"
-#include "ActiveLayerTracker.h"
 
 #include "GeckoProfiler.h"
 #include "mozilla/gfx/Tools.h"
 
 #include <algorithm>
 
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
@@ -2694,26 +2693,26 @@ ChooseScaleAndSetTransform(FrameLayerBui
   if (canDraw2D && isRetained) {
     // If the container's transform is animated off main thread, then use the
     // maximum scale.
     if (aContainerFrame->GetContent() &&
         nsLayoutUtils::HasAnimationsForCompositor(
           aContainerFrame->GetContent(), eCSSProperty_transform)) {
       scale = nsLayoutUtils::GetMaximumAnimatedScale(aContainerFrame->GetContent());
     } else {
-      // Scale factors are normalized to a power of 2 to reduce the number of resolution changes
+      //Scale factors are normalized to a power of 2 to reduce the number of resolution changes
       scale = RoundToFloatPrecision(transform2d.ScaleFactors(true));
       // For frames with a changing transform that's not just a translation,
       // round scale factors up to nearest power-of-2 boundary so that we don't
       // keep having to redraw the content as it scales up and down. Rounding up to nearest
       // power-of-2 boundary ensures we never scale up, only down --- avoiding
       // jaggies. It also ensures we never scale down by more than a factor of 2,
       // avoiding bad downscaling quality.
       gfxMatrix frameTransform;
-      if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform) &&
+      if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
           aTransform &&
           (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) {
         // Don't clamp the scale factor when the new desired scale factor matches the old one
         // or it was previously unscaled.
         bool clamp = true;
         gfxMatrix oldFrameTransform2d;
         if (aLayer->GetBaseTransform().Is2D(&oldFrameTransform2d)) {
           gfxSize oldScale = RoundToFloatPrecision(oldFrameTransform2d.ScaleFactors(true));
@@ -2744,17 +2743,17 @@ ChooseScaleAndSetTransform(FrameLayerBui
                       1.0f/float(scale.height));
   aLayer->SetInheritedScale(aIncomingScale.mXScale,
                             aIncomingScale.mYScale);
 
   aOutgoingScale = 
     FrameLayerBuilder::ContainerParameters(scale.width, scale.height, -offset, aIncomingScale);
   if (aTransform) {
     aOutgoingScale.mInTransformedSubtree = true;
-    if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform)) {
+    if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer)) {
       aOutgoingScale.mInActiveTransformedSubtree = true;
     }
   }
   if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) {
     aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true;
   }
   return true;
 }
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -29,17 +29,16 @@
 #include "nsViewportFrame.h"
 #include "nsSVGTextFrame2.h"
 #include "nsSVGTextPathFrame.h"
 #include "StickyScrollContainer.h"
 #include "nsIRootBox.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsContentUtils.h"
 #include "nsIFrameInlines.h"
-#include "ActiveLayerTracker.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 namespace mozilla {
 
 RestyleManager::RestyleManager(nsPresContext* aPresContext)
@@ -220,41 +219,40 @@ DoApplyRenderingChangeToTree(nsIFrame* a
         NS_ABORT_IF_FALSE(false, "unexpected frame got "
                                  "nsChangeHint_UpdateTextPath");
       }
     }
     if (aChange & nsChangeHint_UpdateOpacityLayer) {
       // FIXME/bug 796697: we can get away with empty transactions for
       // opacity updates in many cases.
       needInvalidatingPaint = true;
-
-      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
+      aFrame->MarkLayersActive(nsChangeHint_UpdateOpacityLayer);
       if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
         // SVG effects paints the opacity without using
         // nsDisplayOpacity. We need to invalidate manually.
         aFrame->InvalidateFrameSubtree();
       }
     }
     if ((aChange & nsChangeHint_UpdateTransformLayer) &&
         aFrame->IsTransformed()) {
-      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
+      aFrame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
       // If we're not already going to do an invalidating paint, see
       // if we can get away with only updating the transform on a
       // layer for this frame, and not scheduling an invalidating
       // paint.
       if (!needInvalidatingPaint) {
         needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly();
       }
     }
     if (aChange & nsChangeHint_ChildrenOnlyTransform) {
       needInvalidatingPaint = true;
       nsIFrame* childFrame =
         GetFrameForChildrenOnlyTransformHint(aFrame)->GetFirstPrincipalChild();
       for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
-        ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
+        childFrame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
       }
     }
     aFrame->SchedulePaint(needInvalidatingPaint ?
                           nsIFrame::PAINT_DEFAULT :
                           nsIFrame::PAINT_COMPOSITE_ONLY);
   }
 }
 
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -21,17 +21,16 @@ if CONFIG['MOZ_DEBUG']:
         'nsAutoLayoutPhase.cpp',
     ]
 
 XPIDL_MODULE = 'layout_base'
 
 MODULE = 'layout'
 
 EXPORTS += [
-    'ActiveLayerTracker.h',
     'DisplayItemClip.h',
     'DisplayListClipState.h',
     'FrameLayerBuilder.h',
     'FramePropertyTable.h',
     'nsArenaMemoryStats.h',
     'nsBidi.h',
     'nsBidiPresUtils.h',
     'nsCaret.h',
@@ -62,17 +61,16 @@ EXPORTS += [
     'Units.h',
 ]
 
 EXPORTS.mozilla += [
     'PaintTracker.h',
 ]
 
 SOURCES += [
-    'ActiveLayerTracker.cpp',
     'DisplayItemClip.cpp',
     'DisplayListClipState.cpp',
     'FrameLayerBuilder.cpp',
     'FramePropertyTable.cpp',
     'MaskLayerImageCache.cpp',
     'nsCaret.cpp',
     'nsCounterManager.cpp',
     'nsCSSColorUtils.cpp',
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -42,17 +42,16 @@
 #include "nsTransitionManager.h"
 #include "nsViewManager.h"
 #include "ImageLayers.h"
 #include "ImageContainer.h"
 #include "nsCanvasFrame.h"
 #include "StickyScrollContainer.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
-#include "ActiveLayerTracker.h"
 
 #include <stdint.h>
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
@@ -3052,17 +3051,17 @@ nsRegion nsDisplayOpacity::GetOpaqueRegi
 }
 
 // nsDisplayOpacity uses layers for rendering
 already_AddRefed<Layer>
 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager,
                              const ContainerParameters& aContainerParameters) {
   if (mFrame->StyleDisplay()->mOpacity == 0 && mFrame->GetContent() &&
-      !nsLayoutUtils::HasAnimations(mFrame->GetContent(), eCSSProperty_opacity)) {
+      !nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(), eCSSProperty_opacity)) {
     return nullptr;
   }
   nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
                            aContainerParameters, nullptr);
   if (!container)
     return nullptr;
 
@@ -3086,17 +3085,17 @@ IsItemTooSmallForActiveLayer(nsDisplayIt
   return visibleDevPixels.Size() <
     nsIntSize(MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS, MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS);
 }
 
 nsDisplayItem::LayerState
 nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
                                 LayerManager* aManager,
                                 const ContainerParameters& aParameters) {
-  if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity) &&
+  if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateOpacityLayer) &&
       !IsItemTooSmallForActiveLayer(this))
     return LAYER_ACTIVE;
   if (mFrame->GetContent()) {
     if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
                                                   eCSSProperty_opacity)) {
       return LAYER_ACTIVE;
     }
   }
@@ -4180,17 +4179,17 @@ nsDisplayTransform::GetResultingTransfor
 
   return nsLayoutUtils::ChangeMatrixBasis
     (rounded + aProperties.mToTransformOrigin, result);
 }
 
 bool
 nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
 {
-  if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_opacity)) {
+  if (Frame()->AreLayersMarkedActive(nsChangeHint_UpdateOpacityLayer)) {
     return true;
   }
 
   if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
     nsCString message;
     message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for opacity animation");
     CommonElementAnimationData::LogAsyncAnimationFailure(message,
                                                          Frame()->GetContent());
@@ -4210,17 +4209,17 @@ nsDisplayTransform::CanUseAsyncAnimation
 nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
                                                       nsIFrame* aFrame,
                                                       bool aLogAnimations)
 {
   // Elements whose transform has been modified recently, or which
   // have a compositor-animated transform, can be prerendered. An element
   // might have only just had its transform animated in which case
   // nsChangeHint_UpdateTransformLayer will not be present yet.
-  if (!ActiveLayerTracker::IsStyleAnimated(aFrame, eCSSProperty_transform) &&
+  if (!aFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
       (!aFrame->GetContent() ||
        !nsLayoutUtils::HasAnimationsForCompositor(aFrame->GetContent(),
                                                   eCSSProperty_transform))) {
     if (aLogAnimations) {
       nsCString message;
       message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation");
       CommonElementAnimationData::LogAsyncAnimationFailure(message,
                                                            aFrame->GetContent());
@@ -4360,17 +4359,17 @@ nsDisplayTransform::GetLayerState(nsDisp
   // If the transform is 3d, or the layer takes part in preserve-3d sorting
   // then we *always* want this to be an active layer.
   if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || 
       mFrame->Preserves3D()) {
     return LAYER_ACTIVE_FORCE;
   }
   // Here we check if the *post-transform* bounds of this item are big enough
   // to justify an active layer.
-  if (ActiveLayerTracker::IsStyleAnimated(mFrame, eCSSProperty_transform) &&
+  if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
       !IsItemTooSmallForActiveLayer(this))
     return LAYER_ACTIVE;
   if (mFrame->GetContent()) {
     if (nsLayoutUtils::HasAnimationsForCompositor(mFrame->GetContent(),
                                                   eCSSProperty_transform)) {
       return LAYER_ACTIVE;
     }
   }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -246,19 +246,19 @@ TextAlignTrueEnabledPrefChangeCallback(c
   nsCSSProps::kTextAlignLastKTable[sIndexOfTrueInTextAlignLastTable] =
     isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN;
 
   return 0;
 }
 
 template <class AnimationsOrTransitions>
 static AnimationsOrTransitions*
-HasAnimationOrTransitionForCompositor(nsIContent* aContent,
-                                      nsIAtom* aAnimationProperty,
-                                      nsCSSProperty aProperty)
+HasAnimationOrTransition(nsIContent* aContent,
+                         nsIAtom* aAnimationProperty,
+                         nsCSSProperty aProperty)
 {
   AnimationsOrTransitions* animations =
     static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty));
   if (animations) {
     bool propertyMatches = animations->HasAnimationOfProperty(aProperty);
     if (propertyMatches &&
         animations->CanPerformOnCompositorThread(
           CommonElementAnimationData::CanAnimate_AllowPartial)) {
@@ -270,50 +270,22 @@ HasAnimationOrTransitionForCompositor(ns
 }
 
 bool
 nsLayoutUtils::HasAnimationsForCompositor(nsIContent* aContent,
                                           nsCSSProperty aProperty)
 {
   if (!aContent->MayHaveAnimations())
     return false;
-  return HasAnimationOrTransitionForCompositor<ElementAnimations>
-            (aContent, nsGkAtoms::animationsProperty, aProperty) ||
-         HasAnimationOrTransitionForCompositor<ElementTransitions>
-            (aContent, nsGkAtoms::transitionsProperty, aProperty);
-}
-
-template <class AnimationsOrTransitions>
-static AnimationsOrTransitions*
-HasAnimationOrTransition(nsIContent* aContent,
-                         nsIAtom* aAnimationProperty,
-                         nsCSSProperty aProperty)
-{
-  AnimationsOrTransitions* animations =
-    static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty));
-  if (animations) {
-    bool propertyMatches = animations->HasAnimationOfProperty(aProperty);
-    if (propertyMatches) {
-      return animations;
-    }
-  }
-
-  return nullptr;
-}
-
-bool
-nsLayoutUtils::HasAnimations(nsIContent* aContent,
-                             nsCSSProperty aProperty)
-{
-  if (!aContent->MayHaveAnimations())
-    return false;
-  return HasAnimationOrTransition<ElementAnimations>
-            (aContent, nsGkAtoms::animationsProperty, aProperty) ||
-         HasAnimationOrTransition<ElementTransitions>
-            (aContent, nsGkAtoms::transitionsProperty, aProperty);
+  if (HasAnimationOrTransition<ElementAnimations>
+        (aContent, nsGkAtoms::animationsProperty, aProperty)) {
+    return true;
+  }
+  return HasAnimationOrTransition<ElementTransitions>
+    (aContent, nsGkAtoms::transitionsProperty, aProperty);
 }
 
 static gfxSize
 GetScaleForValue(const nsStyleAnimation::Value& aValue,
                  nsIFrame* aFrame)
 {
   if (!aFrame) {
     NS_WARNING("No frame.");
@@ -346,17 +318,17 @@ GetScaleForValue(const nsStyleAnimation:
 
   return transform2d.ScaleFactors(true);
 }
 
 gfxSize
 nsLayoutUtils::GetMaximumAnimatedScale(nsIContent* aContent)
 {
   gfxSize result;
-  ElementAnimations* animations = HasAnimationOrTransitionForCompositor<ElementAnimations>
+  ElementAnimations* animations = HasAnimationOrTransition<ElementAnimations>
     (aContent, nsGkAtoms::animationsProperty, eCSSProperty_transform);
   if (animations) {
     for (uint32_t animIdx = animations->mAnimations.Length(); animIdx-- != 0; ) {
       ElementAnimation& anim = animations->mAnimations[animIdx];
       for (uint32_t propIdx = anim.mProperties.Length(); propIdx-- != 0; ) {
         AnimationProperty& prop = anim.mProperties[propIdx];
         if (prop.mProperty == eCSSProperty_transform) {
           for (uint32_t segIdx = prop.mSegments.Length(); segIdx-- != 0; ) {
@@ -369,17 +341,17 @@ nsLayoutUtils::GetMaximumAnimatedScale(n
                                           aContent->GetPrimaryFrame());
             result.width = std::max<float>(result.width, to.width);
             result.height = std::max<float>(result.height, to.height);
           }
         }
       }
     }
   }
-  ElementTransitions* transitions = HasAnimationOrTransitionForCompositor<ElementTransitions>
+  ElementTransitions* transitions = HasAnimationOrTransition<ElementTransitions>
     (aContent, nsGkAtoms::transitionsProperty, eCSSProperty_transform);
   if (transitions) {
     for (uint32_t i = 0, i_end = transitions->mPropertyTransitions.Length();
          i < i_end; ++i)
     {
       ElementPropertyTransition &pt = transitions->mPropertyTransitions[i];
       if (pt.IsRemovedSentinel()) {
         continue;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1628,22 +1628,16 @@ public:
   /**
    * Returns true if the content node has animations or transitions that can be
    * performed on the compositor.
    */
   static bool HasAnimationsForCompositor(nsIContent* aContent,
                                          nsCSSProperty aProperty);
 
   /**
-   * Returns true if the content node has animations or transitions for the
-   * property.
-   */
-  static bool HasAnimations(nsIContent* aContent, nsCSSProperty aProperty);
-
-  /**
    * Checks if off-main-thread animations are enabled.
    */
   static bool AreAsyncAnimationsEnabled();
 
   /**
    * Checks if we should warn about animations that can't be async
    */
   static bool IsAnimationLoggingEnabled();
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -21,17 +21,17 @@
 #include <windows.h>
 // mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
 // to manually include it
 #include <mmsystem.h>
 #include "WinUtils.h"
 #endif
 
 #include "mozilla/Util.h"
-#include "mozilla/AutoRestore.h"
+
 #include "nsRefreshDriver.h"
 #include "nsITimer.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsComponentManagerUtils.h"
 #include "prlog.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
@@ -680,18 +680,17 @@ nsRefreshDriver::ChooseTimer() const
 
 nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
   : mActiveTimer(nullptr),
     mPresContext(aPresContext),
     mFrozen(false),
     mThrottled(false),
     mTestControllingRefreshes(false),
     mViewManagerFlushIsPending(false),
-    mRequestedHighPrecision(false),
-    mInRefresh(false)
+    mRequestedHighPrecision(false)
 {
   mMostRecentRefreshEpochTime = JS_Now();
   mMostRecentRefresh = TimeStamp::Now();
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   NS_ABORT_IF_FALSE(ObserverCount() == 0,
@@ -1057,20 +1056,16 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
     // wait until we get a Notify() call when we have no observers
     // before stopping the timer.
     StopTimer();
     return;
   }
 
   profiler_tracing("Paint", "RD", TRACING_INTERVAL_START);
 
-  AutoRestore<bool> restoreInRefresh(mInRefresh);
-  NS_ASSERTION(!mInRefresh, "Not yet in refresh");
-  mInRefresh = true;
-
   /*
    * The timer holds a reference to |this| while calling |Notify|.
    * However, implementations of |WillRefresh| are permitted to destroy
    * the pres context, which will cause our |mPresContext| to become
    * null.  If this happens, we must stop notifying observers.
    */
   for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
     ObserverArray::EndLimitedIterator etor(mObservers[i]);
@@ -1220,18 +1215,16 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
     }
 #endif
   }
 
   for (uint32_t i = 0; i < mPostRefreshObservers.Length(); ++i) {
     mPostRefreshObservers[i]->DidRefresh();
   }
   profiler_tracing("Paint", "RD", TRACING_INTERVAL_END);
-
-  NS_ASSERTION(mInRefresh, "Still in refresh");
 }
 
 /* static */ PLDHashOperator
 nsRefreshDriver::ImageRequestEnumerator(nsISupportsHashKey* aEntry,
                                         void* aUserArg)
 {
   nsCOMArray<imgIContainer>* imagesToRefresh =
     static_cast<nsCOMArray<imgIContainer>*> (aUserArg);
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -244,18 +244,16 @@ public:
 			   mozFlushType aFlushType);
 #endif
 
   /**
    * Default interval the refresh driver uses, in ms.
    */
   static int32_t DefaultInterval();
 
-  bool IsInRefresh() { return mInRefresh; }
-
 private:
   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
   typedef nsTHashtable<nsISupportsHashKey> RequestTable;
   struct ImageStartData {
     ImageStartData()
     {
     }
 
@@ -299,17 +297,16 @@ private:
   nsPresContext *mPresContext; // weak; pres context passed in constructor
                                // and unset in Disconnect
 
   bool mFrozen;
   bool mThrottled;
   bool mTestControllingRefreshes;
   bool mViewManagerFlushIsPending;
   bool mRequestedHighPrecision;
-  bool mInRefresh;
 
   int64_t mMostRecentRefreshEpochTime;
   mozilla::TimeStamp mMostRecentRefresh;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
   RequestTable mRequests;
   ImageStartTable mStartTable;
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -56,17 +56,16 @@
 #include "SVGElementFactory.h"
 #include "nsSVGUtils.h"
 #include "nsMathMLAtoms.h"
 #include "nsMathMLOperators.h"
 #include "Navigator.h"
 #include "DOMStorageObserver.h"
 #include "CacheObserver.h"
 #include "DisplayItemClip.h"
-#include "ActiveLayerTracker.h"
 
 #include "AudioChannelService.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
@@ -307,17 +306,17 @@ nsLayoutStatics::Shutdown()
   nsCSSRuleProcessor::Shutdown();
   nsTextFrameTextRunCache::Shutdown();
   nsHTMLDNSPrefetch::Shutdown();
   nsCSSRendering::Shutdown();
 #ifdef DEBUG
   nsFrame::DisplayReflowShutdown();
 #endif
   nsCellMap::Shutdown();
-  ActiveLayerTracker::Shutdown();
+  nsFrame::ShutdownLayerActivityTimer();
 
   // Release all of our atoms
   nsColorNames::ReleaseTable();
   nsCSSProps::ReleaseTable();
   nsCSSKeywords::ReleaseTable();
   nsRepeatService::Shutdown();
   nsStackLayout::Shutdown();
   nsBox::Shutdown();
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -57,16 +57,17 @@
 // For triple-click pref
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsError.h"
 #include "nsContainerFrame.h"
 #include "nsBoxLayoutState.h"
 #include "nsBlockFrame.h"
 #include "nsDisplayList.h"
+#include "nsExpirationTracker.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGEffects.h"
 #include "nsChangeHint.h"
 #include "nsDeckFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "nsSVGTextFrame2.h"
 
 #include "gfxContext.h"
@@ -1789,17 +1790,18 @@ nsIFrame::BuildDisplayListForStackingCon
 
   const nsStyleDisplay* disp = StyleDisplay();
   // We can stop right away if this is a zero-opacity stacking context and
   // we're painting, and we're not animating opacity. Don't do this
   // if we're going to compute plugin geometry, since opacity-0 plugins
   // need to have display items built for them.
   if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() &&
       !aBuilder->WillComputePluginGeometry() &&
-      !nsLayoutUtils::HasAnimations(mContent, eCSSProperty_opacity)) {
+      !nsLayoutUtils::HasAnimationsForCompositor(mContent,
+                                                 eCSSProperty_opacity)) {
     return;
   }
 
   nsRect dirtyRect = aDirtyRect;
 
   bool inTransform = aBuilder->IsInTransform();
   bool isTransformed = IsTransformed();
   // reset blend mode so we can keep track if this stacking context needs have
@@ -4555,16 +4557,140 @@ nsFrame::GetType() const
 }
 
 bool
 nsIFrame::IsLeaf() const
 {
   return true;
 }
 
+class LayerActivity {
+public:
+  LayerActivity(nsIFrame* aFrame)
+    : mFrame(aFrame)
+    , mChangeHint(nsChangeHint(0))
+    , mMutationCount(0)
+  {}
+  ~LayerActivity();
+  nsExpirationState* GetExpirationState() { return &mState; }
+  uint32_t GetMutationCount() { return mMutationCount; }
+
+  nsIFrame* mFrame;
+  nsExpirationState mState;
+  // mChangeHint can be some combination of nsChangeHint_UpdateOpacityLayer and
+  // nsChangeHint_UpdateTransformLayer (or neither)
+  // The presence of those bits indicates whether opacity or transform
+  // changes have been detected.
+  nsChangeHint mChangeHint;
+  uint32_t mMutationCount;
+};
+
+class LayerActivityTracker MOZ_FINAL : public nsExpirationTracker<LayerActivity,4> {
+public:
+  // 75-100ms is a good timeout period. We use 4 generations of 25ms each.
+  enum { GENERATION_MS = 100 };
+  LayerActivityTracker()
+    : nsExpirationTracker<LayerActivity,4>(GENERATION_MS) {}
+  ~LayerActivityTracker() {
+    AgeAllGenerations();
+  }
+
+  virtual void NotifyExpired(LayerActivity* aObject);
+};
+
+static LayerActivityTracker* gLayerActivityTracker = nullptr;
+
+LayerActivity::~LayerActivity()
+{
+  if (mFrame) {
+    NS_ASSERTION(gLayerActivityTracker, "Should still have a tracker");
+    gLayerActivityTracker->RemoveObject(this);
+  }
+}
+
+static void DestroyLayerActivity(void* aPropertyValue)
+{
+  delete static_cast<LayerActivity*>(aPropertyValue);
+}
+
+NS_DECLARE_FRAME_PROPERTY(LayerActivityProperty, DestroyLayerActivity)
+
+void
+LayerActivityTracker::NotifyExpired(LayerActivity* aObject)
+{
+  RemoveObject(aObject);
+
+  nsIFrame* f = aObject->mFrame;
+  aObject->mFrame = nullptr;
+
+  // if there are hints other than transform/opacity, invalidate, since we don't know what else to do.
+  if (aObject->mChangeHint & ~(nsChangeHint_UpdateOpacityLayer|nsChangeHint_UpdateTransformLayer)) {
+    f->InvalidateFrameSubtree();
+  } else {
+    if (aObject->mChangeHint & nsChangeHint_UpdateOpacityLayer) {
+      f->InvalidateFrameSubtree(nsDisplayItem::TYPE_OPACITY);
+    } 
+    if (aObject->mChangeHint & nsChangeHint_UpdateTransformLayer) {
+      f->InvalidateFrameSubtree(nsDisplayItem::TYPE_TRANSFORM);
+    }
+  } 
+  f->Properties().Delete(LayerActivityProperty());
+}
+
+void
+nsIFrame::MarkLayersActive(nsChangeHint aChangeHint)
+{
+  FrameProperties properties = Properties();
+  LayerActivity* layerActivity =
+    static_cast<LayerActivity*>(properties.Get(LayerActivityProperty()));
+  if (layerActivity) {
+    gLayerActivityTracker->MarkUsed(layerActivity);
+  } else {
+    if (!gLayerActivityTracker) {
+      gLayerActivityTracker = new LayerActivityTracker();
+    }
+    layerActivity = new LayerActivity(this);
+    gLayerActivityTracker->AddObject(layerActivity);
+    properties.Set(LayerActivityProperty(), layerActivity);
+  }
+  layerActivity->mMutationCount++;
+  NS_UpdateHint(layerActivity->mChangeHint, aChangeHint);
+}
+
+bool
+nsIFrame::AreLayersMarkedActive()
+{
+  return Properties().Get(LayerActivityProperty()) != nullptr;
+}
+
+bool
+nsIFrame::AreLayersMarkedActive(nsChangeHint aChangeHint)
+{
+  LayerActivity* layerActivity =
+    static_cast<LayerActivity*>(Properties().Get(LayerActivityProperty()));
+  if (layerActivity && (layerActivity->mChangeHint & aChangeHint)) {
+    if (aChangeHint & nsChangeHint_UpdateOpacityLayer) {
+      return layerActivity->GetMutationCount() > 1;
+    }
+    return true;
+  }
+  if (aChangeHint & nsChangeHint_UpdateTransformLayer &&
+      Preserves3D()) {
+    return GetParent()->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer);
+  }
+  return false;
+}
+
+/* static */ void
+nsFrame::ShutdownLayerActivityTimer()
+{
+  delete gLayerActivityTracker;
+  gLayerActivityTracker = nullptr;
+}
+
 gfx3DMatrix
 nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
                              nsIFrame** aOutAncestor)
 {
   NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
 
   /* If we're transformed, we want to hand back the combination
    * transform/translate matrix that will apply our current transform, then
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -479,16 +479,18 @@ public:
                                         const char* aType,
                                         nsSize aResult,
                                         void* aFrameTreeNode);
 
   static void DisplayReflowStartup();
   static void DisplayReflowShutdown();
 #endif
 
+  static void ShutdownLayerActivityTimer();
+
   /**
    * Adds display items for standard CSS background if necessary.
    * Does not check IsVisibleForPainting.
    * @param aForceBackground draw the background even if the frame
    * background style appears to have no background --- this is useful
    * for frames that might receive a propagated background via
    * nsCSSRendering::FindBackground
    * @return whether a themed background item was created.
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -7,17 +7,16 @@
 
 #include "nsHTMLCanvasFrame.h"
 
 #include "nsGkAtoms.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h"
 #include "Layers.h"
-#include "ActiveLayerTracker.h"
 
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 class nsDisplayCanvas : public nsDisplayItem {
@@ -64,21 +63,20 @@ public:
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const FrameLayerBuilder::ContainerParameters& aParameters)
   {
     if (HTMLCanvasElement::FromContent(mFrame->GetContent())->ShouldForceInactiveLayer(aManager))
       return LAYER_INACTIVE;
 
     // If compositing is cheap, just do that
-    if (aManager->IsCompositingCheap() ||
-        ActiveLayerTracker::IsContentActive(mFrame))
+    if (aManager->IsCompositingCheap())
       return mozilla::LAYER_ACTIVE;
 
-    return LAYER_INACTIVE;
+    return mFrame->AreLayersMarkedActive() ? LAYER_ACTIVE : LAYER_INACTIVE;
   }
 };
 
 
 nsIFrame*
 NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsHTMLCanvasFrame(aContext);
@@ -93,19 +91,19 @@ NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasF
 void
 nsHTMLCanvasFrame::Init(nsIContent* aContent,
                         nsIFrame*   aParent,
                         nsIFrame*   aPrevInFlow)
 {
   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
 
   // We can fill in the canvas before the canvas frame is created, in
-  // which case we never get around to marking the content as active. Therefore,
+  // which case we never get around to marking the layer active. Therefore,
   // we mark it active here when we create the frame.
-  ActiveLayerTracker::NotifyContentChange(this);
+  MarkLayersActive(nsChangeHint(0));
 }
 
 nsHTMLCanvasFrame::~nsHTMLCanvasFrame()
 {
 }
 
 nsIntSize
 nsHTMLCanvasFrame::GetCanvasSize()
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2172,16 +2172,40 @@ public:
 
   /**
    * Is this a flex item? (Is the parent a flex container frame?)
    */
   bool IsFlexItem() const
   { return mParent && mParent->GetType() == nsGkAtoms::flexContainerFrame; }
 
   /**
+   * Mark this frame as using active layers. This marking will time out
+   * after a short period. This call does no immediate invalidation,
+   * but when the mark times out, we'll invalidate the frame's overflow
+   * area.
+   * @param aChangeHint nsChangeHint_UpdateTransformLayer or
+   * nsChangeHint_UpdateOpacityLayer or 0, depending on whether the change
+   * triggering the activity is a changing transform, changing opacity, or
+   * something else.
+   */
+  void MarkLayersActive(nsChangeHint aHint);
+  /**
+   * Return true if this frame is marked as needing active layers.
+   */
+  bool AreLayersMarkedActive();
+  /**
+   * Return true if this frame is marked as needing active layers.
+   * @param aChangeHint nsChangeHint_UpdateTransformLayer or
+   * nsChangeHint_UpdateOpacityLayer. We return true only if
+   * a change in the transform or opacity has been recorded while layers have
+   * been marked active for this frame.
+   */
+  bool AreLayersMarkedActive(nsChangeHint aChangeHint);
+
+  /**
    * Marks all display items created by this frame as needing a repaint,
    * and calls SchedulePaint() if requested and one is not already pending.
    *
    * This includes all display items created by this frame, including
    * container types.
    *
    * @param aDisplayItemKey If specified, only issues an invalidate
    * if this frame painted a display item of that type during the 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -11,17 +11,16 @@
 #include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsCSSRules.h"
 #include "nsStyleAnimation.h"
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
-#include "ActiveLayerTracker.h"
 #include <math.h>
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 ElementAnimations::ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty,
                                      nsAnimationManager *aAnimationManager)
   : CommonElementAnimationData(aElement, aElementProperty,
@@ -412,20 +411,20 @@ ElementAnimations::CanPerformOnComposito
       } else if (prop.mProperty == eCSSProperty_transform) {
         hasTransform = true;
       }
     }
   }
   // This animation can be done on the compositor.  Mark the frame as active, in
   // case we are able to throttle this animation.
   if (hasOpacity) {
-    ActiveLayerTracker::NotifyAnimated(frame, eCSSProperty_opacity);
+    frame->MarkLayersActive(nsChangeHint_UpdateOpacityLayer);
   }
   if (hasTransform) {
-    ActiveLayerTracker::NotifyAnimated(frame, eCSSProperty_transform);
+    frame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
   }
   return true;
 }
 
 ElementAnimations*
 nsAnimationManager::GetElementAnimations(dom::Element *aElement,
                                          nsCSSPseudoElements::Type aPseudoType,
                                          bool aCreateIfNeeded)
--- a/layout/style/nsDOMCSSAttrDeclaration.cpp
+++ b/layout/style/nsDOMCSSAttrDeclaration.cpp
@@ -10,20 +10,19 @@
 #include "mozilla/css/Declaration.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIURI.h"
 #include "nsNodeUtils.h"
 #include "nsWrapperCacheInlines.h"
-#include "nsIFrame.h"
-#include "ActiveLayerTracker.h"
 
-using namespace mozilla;
+namespace css = mozilla::css;
+namespace dom = mozilla::dom;
 
 nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(dom::Element* aElement,
                                                            bool aIsSMILOverride)
   : mElement(aElement)
   , mIsSMILOverride(aIsSMILOverride)
 {
   MOZ_COUNT_CTOR(nsDOMCSSAttributeDeclaration);
 
@@ -165,24 +164,8 @@ nsDOMCSSAttributeDeclaration::GetParentR
   return NS_OK;
 }
 
 /* virtual */ nsINode*
 nsDOMCSSAttributeDeclaration::GetParentObject()
 {
   return mElement;
 }
-
-NS_IMETHODIMP
-nsDOMCSSAttributeDeclaration::SetPropertyValue(const nsCSSProperty aPropID,
-                                               const nsAString& aValue)
-{
-  // Scripted modifications to style.opacity or style.transform
-  // could immediately force us into the animated state if heuristics suggest
-  // this is scripted animation.
-  if (aPropID == eCSSProperty_opacity || aPropID == eCSSProperty_transform) {
-    nsIFrame* frame = mElement->GetPrimaryFrame();
-    if (frame) {
-      ActiveLayerTracker::NotifyInlineStyleRuleModified(frame, aPropID);
-    }
-  }
-  return nsDOMCSSDeclaration::SetPropertyValue(aPropID, aValue);
-}
--- a/layout/style/nsDOMCSSAttrDeclaration.h
+++ b/layout/style/nsDOMCSSAttrDeclaration.h
@@ -33,19 +33,16 @@ public:
   // If GetCSSDeclaration returns non-null, then the decl it returns
   // is owned by our current style rule.
   virtual mozilla::css::Declaration* GetCSSDeclaration(bool aAllocate);
   virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) MOZ_OVERRIDE;
   NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) MOZ_OVERRIDE;
 
   virtual nsINode* GetParentObject() MOZ_OVERRIDE;
 
-  NS_IMETHOD SetPropertyValue(const nsCSSProperty aPropID,
-                              const nsAString& aValue);
-
 protected:
   virtual nsresult SetCSSDeclaration(mozilla::css::Declaration* aDecl) MOZ_OVERRIDE;
   virtual nsIDocument* DocToUpdate() MOZ_OVERRIDE;
 
   nsRefPtr<Element> mElement;
 
   /* If true, this indicates that this nsDOMCSSAttributeDeclaration
    * should interact with mContent's SMIL override style rule (rather
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -23,17 +23,16 @@
 #include "mozilla/dom/Element.h"
 #include "nsIFrame.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsDisplayList.h"
 #include "nsStyleChangeList.h"
 #include "nsStyleSet.h"
 #include "RestyleManager.h"
-#include "ActiveLayerTracker.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::css;
 
@@ -193,20 +192,20 @@ ElementTransitions::CanPerformOnComposit
   // No properties to animate
   if (!existsProperty) {
     return false;
   }
 
   // This transition can be done on the compositor.  Mark the frame as active, in
   // case we are able to throttle this transition.
   if (hasOpacity) {
-    ActiveLayerTracker::NotifyAnimated(frame, eCSSProperty_opacity);
+    frame->MarkLayersActive(nsChangeHint_UpdateOpacityLayer);
   }
   if (hasTransform) {
-    ActiveLayerTracker::NotifyAnimated(frame, eCSSProperty_transform);
+    frame->MarkLayersActive(nsChangeHint_UpdateTransformLayer);
   }
   return true;
 }
 
 /*****************************************************************************
  * nsTransitionManager                                                       *
  *****************************************************************************/