merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 12 Sep 2016 13:07:54 +0200
changeset 339248 1851b78b5a9673ee422f189b92e5f1e86b82a01c
parent 339230 3d667f260feb3f7f30fca84f608b92e330aa349c (current diff)
parent 339247 f56deec592bd3cff3e7f9eacb4786a3e50626fca (diff)
child 339250 a18a3e221752e57641ed74c438fa20dc7f50cd1e
child 339271 e8b53c6a566ec9f6d9abb194498044a6003c2b86
child 339277 5d8afbdb3620c7da25955d0c567cc4a4f890adf5
push id10033
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:50:26 +0000
treeherdermozilla-aurora@5dddbefdf759 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
merge mozilla-inbound to mozilla-central a=merge
layout/generic/VisibilityIPC.h
--- a/dom/base/nsIImageLoadingContent.idl
+++ b/dom/base/nsIImageLoadingContent.idl
@@ -175,21 +175,19 @@ interface nsIImageLoadingContent : imgIN
    */
   readonly attribute unsigned long    naturalWidth;
   readonly attribute unsigned long    naturalHeight;
 
   /**
    * Called by layout to announce when the frame associated with this content
    * has changed its visibility state.
    *
-   * @param aOldVisibility    The previous visibility state.
    * @param aNewVisibility    The new visibility state.
    * @param aNonvisibleAction A requested action if the frame has become
    *                          nonvisible. If Nothing(), no action is
    *                          requested. If DISCARD_IMAGES is specified, the
    *                          frame is requested to ask any images it's
    *                          associated with to discard their surfaces if
    *                          possible.
    */
-  [noscript, notxpcom] void onVisibilityChange(in Visibility aOldVisibility,
-                                               in Visibility aNewVisibility,
+  [noscript, notxpcom] void onVisibilityChange(in Visibility aNewVisibility,
                                                in MaybeOnNonvisible aNonvisibleAction);
 };
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -278,31 +278,32 @@ nsImageLoadingContent::OnUnlockedDraw()
     return;
   }
 
   nsIFrame* frame = GetOurPrimaryFrame();
   if (!frame) {
     return;
   }
 
-  if (frame->IsVisibleOrMayBecomeVisibleSoon()) {
-    return;  // Nothing to do.
+  if (frame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE) {
+    // This frame is already marked visible; there's nothing to do.
+    return;
   }
 
   nsPresContext* presContext = frame->PresContext();
   if (!presContext) {
     return;
   }
 
   nsIPresShell* presShell = presContext->PresShell();
   if (!presShell) {
     return;
   }
 
-  presShell->MarkFrameVisible(frame, VisibilityCounter::IN_DISPLAYPORT);
+  presShell->EnsureFrameInApproximatelyVisibleList(frame);
 }
 
 nsresult
 nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
 {
   bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
   if (requestFlag) {
     nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
@@ -519,17 +520,17 @@ nsImageLoadingContent::FrameDestroyed(ns
                                           &mPendingRequestRegistered);
   }
 
   UntrackImage(mCurrentRequest);
   UntrackImage(mPendingRequest);
 
   nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nullptr;
   if (presShell) {
-    presShell->MarkFrameNonvisible(aFrame);
+    presShell->RemoveFrameFromApproximatelyVisibleList(aFrame);
   }
 }
 
 /* static */
 nsContentPolicyType
 nsImageLoadingContent::PolicyTypeForLoad(ImageLoadType aImageLoadType)
 {
   if (aImageLoadType == eImageLoadType_Imageset) {
@@ -1427,30 +1428,26 @@ nsImageLoadingContent::UnbindFromTree(bo
   UntrackImage(mCurrentRequest);
   UntrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     doc->UnblockOnload(false);
 }
 
 void
-nsImageLoadingContent::OnVisibilityChange(Visibility aOldVisibility,
-                                          Visibility aNewVisibility,
+nsImageLoadingContent::OnVisibilityChange(Visibility aNewVisibility,
                                           const Maybe<OnNonvisible>& aNonvisibleAction)
 {
   switch (aNewVisibility) {
-    case Visibility::MAY_BECOME_VISIBLE:
-    case Visibility::IN_DISPLAYPORT:
-      if (aOldVisibility == Visibility::NONVISIBLE) {
-        TrackImage(mCurrentRequest);
-        TrackImage(mPendingRequest);
-      }
+    case Visibility::APPROXIMATELY_VISIBLE:
+      TrackImage(mCurrentRequest);
+      TrackImage(mPendingRequest);
       break;
 
-    case Visibility::NONVISIBLE:
+    case Visibility::APPROXIMATELY_NONVISIBLE:
       UntrackImage(mCurrentRequest, aNonvisibleAction);
       UntrackImage(mPendingRequest, aNonvisibleAction);
       break;
 
     case Visibility::UNTRACKED:
       MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
       break;
   }
@@ -1466,21 +1463,21 @@ nsImageLoadingContent::TrackImage(imgIRe
              "Why haven't we heard of this request?");
 
   nsIDocument* doc = GetOurCurrentDoc();
   if (!doc) {
     return;
   }
 
   // We only want to track this request if we're visible. Ordinarily we check
-  // whether our frame considers itself visible, but in cases where
+  // the visible count, but that requires a frame; in cases where
   // GetOurPrimaryFrame() cannot obtain a frame (e.g. <feImage>), we assume
   // we're visible if FrameCreated() was called.
   nsIFrame* frame = GetOurPrimaryFrame();
-  if ((frame && !frame->IsVisibleOrMayBecomeVisibleSoon()) ||
+  if ((frame && frame->GetVisibility() == Visibility::APPROXIMATELY_NONVISIBLE) ||
       (!frame && !mFrameCreateCalled)) {
     return;
   }
 
   if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
     mCurrentRequestFlags |= REQUEST_IS_TRACKED;
     doc->AddImage(mCurrentRequest);
   }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -6012,16 +6012,66 @@ HTMLMediaElement::IsAllowedToPlay()
   if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
       mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
     return false;
   }
 
   return true;
 }
 
+static const char* VisibilityString(Visibility aVisibility) {
+  switch(aVisibility) {
+    case Visibility::UNTRACKED: {
+      return "UNTRACKED";
+    }
+    case Visibility::APPROXIMATELY_NONVISIBLE: {
+      return "APPROXIMATELY_NONVISIBLE";
+    }
+    case Visibility::APPROXIMATELY_VISIBLE: {
+      return "APPROXIMATELY_VISIBLE";
+    }
+  }
+
+  return "NAN";
+}
+
+void
+HTMLMediaElement::OnVisibilityChange(Visibility aNewVisibility)
+{
+  LOG(LogLevel::Debug, ("OnVisibilityChange(): %s\n",
+      VisibilityString(aNewVisibility)));
+
+  if (!mDecoder) {
+    return;
+  }
+
+  switch (aNewVisibility) {
+    case Visibility::UNTRACKED: {
+        MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
+        break;
+    }
+    case Visibility::APPROXIMATELY_NONVISIBLE: {
+      if (mPlayTime.IsStarted()) {
+        // Not visible, play time is running -> Start hidden play time if needed.
+        HiddenVideoStart();
+      }
+
+      mDecoder->NotifyOwnerActivityChanged(false);
+      break;
+    }
+    case Visibility::APPROXIMATELY_VISIBLE: {
+      // Visible -> Just pause hidden play time (no-op if already paused).
+      HiddenVideoStop();
+
+      mDecoder->NotifyOwnerActivityChanged(true);
+      break;
+    }
+  }
+
+}
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
 bool
@@ -6451,94 +6501,16 @@ HTMLMediaElement::OpenUnsupportedMediaWi
   }
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(), static_cast<nsIContent*>(this),
                                        NS_LITERAL_STRING("OpenMediaWithExternalApp"),
                                        true,
                                        true);
 }
 
-static const char* VisibilityString(Visibility aVisibility) {
-  switch(aVisibility) {
-    case Visibility::UNTRACKED: {
-      return "UNTRACKED";
-    }
-    case Visibility::NONVISIBLE: {
-      return "NONVISIBLE";
-    }
-    case Visibility::MAY_BECOME_VISIBLE: {
-      return "MAY_BECOME_VISIBLE";
-    }
-    case Visibility::IN_DISPLAYPORT: {
-      return "IN_DISPLAYPORT";
-    }
-  }
-
-  return "NAN";
-}
-
-// The visibility enumeration contains three states:
-// {NONVISIBLE, MAY_BECOME_VISIBLE, IN_DISPLAYPORT}.
-// Here, I implement a conservative mechanism:
-// (1) {MAY_BECOME_VISIBLE, IN_DISPLAYPORT} -> NONVISIBLE:
-//     notify the decoder to suspend.
-// (2) {NONVISIBLE, MAY_BECOME_VISIBLE} -> IN_DISPLAYPORT:
-//     notify the decoder to resume.
-// (3) IN_DISPLAYPORT -> MAY_BECOME_VISIBLE:
-//     do nothing here because users might scroll back immediately.
-// (4) NONVISIBLE -> MAY_BECOME_VISIBLE:
-//     notify the decoder to resume because users might continue their scroll
-//     direction and the video might be IN_DISPLAYPORT soon.
-void
-HTMLMediaElement::OnVisibilityChange(Visibility aOldVisibility,
-                                     Visibility aNewVisibility)
-{
-  LOG(LogLevel::Debug, ("OnVisibilityChange(): %s -> %s\n",
-      VisibilityString(aOldVisibility),VisibilityString(aNewVisibility)));
-
-  if (!mDecoder) {
-    return;
-  }
-
-  switch (aNewVisibility) {
-    case Visibility::UNTRACKED: {
-        MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
-        break;
-    }
-    case Visibility::NONVISIBLE: {
-      if (mPlayTime.IsStarted()) {
-        // Not visible, play time is running -> Start hidden play time if needed.
-        HiddenVideoStart();
-      }
-
-      mDecoder->NotifyOwnerActivityChanged(false);
-      break;
-    }
-    case Visibility::MAY_BECOME_VISIBLE: {
-      if (aOldVisibility == Visibility::NONVISIBLE) {
-        // Visible -> Just pause hidden play time (no-op if already paused).
-        HiddenVideoStop();
-
-        mDecoder->NotifyOwnerActivityChanged(true);
-      } else if (aOldVisibility == Visibility::IN_DISPLAYPORT) {
-        // Do nothing.
-      }
-      break;
-    }
-    case Visibility::IN_DISPLAYPORT: {
-      // Visible -> Just pause hidden play time (no-op if already paused).
-      HiddenVideoStop();
-
-      mDecoder->NotifyOwnerActivityChanged(true);
-      break;
-    }
-  }
-
-}
-
 bool
 HTMLMediaElement::ShouldElementBePaused()
 {
   // The media in the non-visited page would be blocked.
   if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
     return true;
   }
 
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -135,11 +135,10 @@ interface nsIDOMHTMLMediaElement : nsISu
 
   /**
    * Called by layout to announce when the frame associated with this content
    * has changed its visibility state.
    *
    * @param aOldVisibility    The previous visibility state.
    * @param aNewVisibility    The new visibility state.
    */
-  [noscript, notxpcom] void onVisibilityChange(in Visibility aOldVisibility,
-                                               in Visibility aNewVisibility);
+  [noscript, notxpcom] void onVisibilityChange(in Visibility aNewVisibility);
 };
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -210,32 +210,16 @@ ContainerPrepare(ContainerT* aContainer,
       aContainer->mPrepared->mNeedsSurfaceCopy = true;
       aContainer->mLastIntermediateSurface = nullptr;
     }
   } else {
     aContainer->mLastIntermediateSurface = nullptr;
   }
 }
 
-template <typename RectPainter> void
-DrawRegion(CSSIntRegion* aRegion,
-           gfx::Color aColor,
-           const RectPainter& aRectPainter)
-{
-  MOZ_ASSERT(aRegion);
-
-  // Iterate through and draw the rects in the region using the provided lambda.
-  for (CSSIntRegion::RectIterator iterator = aRegion->RectIter();
-       !iterator.Done();
-       iterator.Next())
-  {
-    aRectPainter(iterator.Get(), aColor);
-  }
-}
-
 template<class ContainerT> void
 RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
                    const RenderTargetIntRect& aClipRect, Layer* aLayer)
 {
   Compositor* compositor = aManager->GetCompositor();
 
   if (aLayer->GetScrollMetadataCount() < 1) {
     return;
@@ -253,18 +237,17 @@ RenderMinimap(ContainerT* aContainer, La
   const int horizontalPadding = 5;
   gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
   gfx::Color tileActiveColor(1, 1, 1, 0.4f);
   gfx::Color tileBorderColor(0, 0, 0, 0.1f);
   gfx::Color pageBorderColor(0, 0, 0);
   gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
   gfx::Color displayPortColor(0, 1.f, 0);
   gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
-  gfx::Color approxVisibilityColor(1.f, 0, 0);
-  gfx::Color inDisplayPortVisibilityColor(1.f, 1.f, 0);
+  gfx::Color visibilityColor(1.f, 0, 0);
 
   // Rects
   const FrameMetrics& fm = aLayer->GetFrameMetrics(0);
   ParentLayerRect compositionBounds = fm.GetCompositionBounds();
   LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
   LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
   LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
   Maybe<LayerRect> cdp;
@@ -311,31 +294,33 @@ RenderMinimap(ContainerT* aContainer, La
   if (gfxPrefs::APZMinimapVisibilityEnabled()) {
     // Retrieve the APZC scrollable layer guid, which we'll use to get the
     // appropriate visibility information from the layer manager.
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
     MOZ_ASSERT(controller);
 
     ScrollableLayerGuid guid = controller->GetGuid();
 
-    auto rectPainter = [&](const CSSIntRect& aRect, const gfx::Color& aColor) {
-      LayerRect scaledRect = aRect * fm.LayersPixelsPerCSSPixel();
-      Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
-      compositor->FillRect(r, aColor, clipRect, aContainer->GetEffectiveTransform());
-    };
+    // Get the approximately visible region.
+    static CSSIntRegion emptyRegion;
+    CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid);
+    if (!visibleRegion) {
+      visibleRegion = &emptyRegion;
+    }
 
-    // Draw the approximately visible region.
-    CSSIntRegion* approxVisibleRegion =
-      aManager->GetVisibleRegion(VisibilityCounter::MAY_BECOME_VISIBLE, guid);
-    DrawRegion(approxVisibleRegion, approxVisibilityColor, rectPainter);
-
-    // Draw the in-displayport visible region.
-    CSSIntRegion* inDisplayPortVisibleRegion =
-      aManager->GetVisibleRegion(VisibilityCounter::IN_DISPLAYPORT, guid);
-    DrawRegion(inDisplayPortVisibleRegion, inDisplayPortVisibilityColor, rectPainter);
+    // Iterate through and draw the rects in the region.
+    for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter();
+         !iterator.Done();
+         iterator.Next())
+    {
+      CSSIntRect rect = iterator.Get();
+      LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
+      Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
+      compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform());
+    }
   }
 
   // Render the displayport.
   Rect r = transform.TransformBounds(dp.ToUnknownRect());
   compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
   compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
 
   // Render the critical displayport if there is one
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -28,17 +28,16 @@
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nscore.h"                     // for nsAString, etc
 #include "LayerTreeInvalidation.h"
-#include "Visibility.h"
 
 class gfxContext;
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 namespace mozilla {
@@ -207,63 +206,39 @@ public:
 
   static void PlatformSyncBeforeReplyUpdate();
 
   void AddInvalidRegion(const nsIntRegion& aRegion)
   {
     mInvalidRegion.Or(mInvalidRegion, aRegion);
   }
 
-  void ClearVisibleRegions(uint64_t aLayersId,
-                           const Maybe<uint32_t>& aPresShellId)
+  void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
+                                        const Maybe<uint32_t>& aPresShellId)
   {
-    for (auto iter = mApproximatelyVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
-      if (iter.Key().mLayersId == aLayersId &&
-          (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
-        iter.Remove();
-      }
-    }
-
-    for (auto iter = mInDisplayPortVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
+    for (auto iter = mVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
       if (iter.Key().mLayersId == aLayersId &&
           (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
         iter.Remove();
       }
     }
   }
 
-  void UpdateVisibleRegion(VisibilityCounter aCounter,
-                           const ScrollableLayerGuid& aGuid,
-                           const CSSIntRegion& aRegion)
+  void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                        const CSSIntRegion& aRegion)
   {
-    VisibleRegions& regions = aCounter == VisibilityCounter::MAY_BECOME_VISIBLE
-                            ? mApproximatelyVisibleRegions
-                            : mInDisplayPortVisibleRegions;
-
-    CSSIntRegion* regionForScrollFrame = regions.LookupOrAdd(aGuid);
+    CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid);
     MOZ_ASSERT(regionForScrollFrame);
 
     *regionForScrollFrame = aRegion;
   }
 
-  CSSIntRegion* GetVisibleRegion(VisibilityCounter aCounter,
-                                 const ScrollableLayerGuid& aGuid)
+  CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid)
   {
-    static CSSIntRegion emptyRegion;
-
-    VisibleRegions& regions = aCounter == VisibilityCounter::MAY_BECOME_VISIBLE
-                            ? mApproximatelyVisibleRegions
-                            : mInDisplayPortVisibleRegions;
-
-    CSSIntRegion* region = regions.Get(aGuid);
-    if (!region) {
-      region = &emptyRegion;
-    }
-
-    return region;
+    return mVisibleRegions.Get(aGuid);
   }
 
   Compositor* GetCompositor() const
   {
     return mCompositor;
   }
 
   // Called by CompositorBridgeParent when a new compositor has been created due
@@ -394,18 +369,17 @@ private:
    */
   RefPtr<gfx::DrawTarget> mTarget;
   gfx::IntRect mTargetBounds;
 
   nsIntRegion mInvalidRegion;
 
   typedef nsClassHashtable<nsGenericHashKey<ScrollableLayerGuid>,
                            CSSIntRegion> VisibleRegions;
-  VisibleRegions mApproximatelyVisibleRegions;
-  VisibleRegions mInDisplayPortVisibleRegions;
+  VisibleRegions mVisibleRegions;
 
   UniquePtr<FPSState> mFPS;
 
   bool mInTransaction;
   bool mIsCompositorReady;
   bool mDebugOverlayWantsNextFrame;
 
   RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -797,36 +797,36 @@ CompositorBridgeChild::SendRequestNotify
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
     return true;
   }
   return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
 }
 
 bool
-CompositorBridgeChild::SendClearVisibleRegions(uint64_t aLayersId,
-                                               uint32_t aPresShellId)
+CompositorBridgeChild::SendClearApproximatelyVisibleRegions(uint64_t aLayersId,
+                                                            uint32_t aPresShellId)
 {
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
     return true;
   }
-  return PCompositorBridgeChild::SendClearVisibleRegions(aLayersId, aPresShellId);
+  return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId,
+                                                                aPresShellId);
 }
 
 bool
-CompositorBridgeChild::SendUpdateVisibleRegion(VisibilityCounter aCounter,
-                                               const ScrollableLayerGuid& aGuid,
-                                               const CSSIntRegion& aRegion)
+CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                                            const CSSIntRegion& aRegion)
 {
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
     return true;
   }
-  return PCompositorBridgeChild::SendUpdateVisibleRegion(aCounter, aGuid, aRegion);
+  return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
 }
 
 PTextureChild*
 CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                           const LayersBackend&,
                                           const TextureFlags&,
                                           const uint64_t&,
                                           const uint64_t& aSerial)
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -155,20 +155,19 @@ public:
   bool SendAdoptChild(const uint64_t& id);
   bool SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect);
   bool SendFlushRendering();
   bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight);
   bool SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex);
   bool SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals);
   bool SendNotifyRegionInvalidated(const nsIntRegion& region);
   bool SendRequestNotifyAfterRemotePaint();
-  bool SendClearVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
-  bool SendUpdateVisibleRegion(VisibilityCounter aCounter,
-                               const ScrollableLayerGuid& aGuid,
-                               const mozilla::CSSIntRegion& aRegion);
+  bool SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
+  bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                            const mozilla::CSSIntRegion& aRegion);
   bool IsSameProcess() const override;
 
   virtual bool IPCOpen() const override { return mCanSend; }
 
   static void ShutDown();
 
   void UpdateFwdTransactionId() { ++mFwdTransactionId; }
   uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -941,55 +941,46 @@ CompositorBridgeParent::RecvStopFrameTim
 {
   if (mLayerManager) {
     mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals);
   }
   return true;
 }
 
 bool
-CompositorBridgeParent::RecvClearVisibleRegions(const uint64_t& aLayersId,
-                                                const uint32_t& aPresShellId)
+CompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+                                                            const uint32_t& aPresShellId)
 {
-  ClearVisibleRegions(aLayersId, Some(aPresShellId));
+  ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
   return true;
 }
 
 void
-CompositorBridgeParent::ClearVisibleRegions(const uint64_t& aLayersId,
-                                            const Maybe<uint32_t>& aPresShellId)
+CompositorBridgeParent::ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+                                                         const Maybe<uint32_t>& aPresShellId)
 {
   if (mLayerManager) {
-    mLayerManager->ClearVisibleRegions(aLayersId, aPresShellId);
+    mLayerManager->ClearApproximatelyVisibleRegions(aLayersId, aPresShellId);
 
     // We need to recomposite to update the minimap.
     ScheduleComposition();
   }
 }
 
 bool
-CompositorBridgeParent::RecvUpdateVisibleRegion(const VisibilityCounter& aCounter,
-                                                const ScrollableLayerGuid& aGuid,
-                                                const CSSIntRegion& aRegion)
-{
-  UpdateVisibleRegion(aCounter, aGuid, aRegion);
-  return true;
-}
-
-void
-CompositorBridgeParent::UpdateVisibleRegion(const VisibilityCounter& aCounter,
-                                            const ScrollableLayerGuid& aGuid,
-                                            const CSSIntRegion& aRegion)
+CompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                                             const CSSIntRegion& aRegion)
 {
   if (mLayerManager) {
-    mLayerManager->UpdateVisibleRegion(aCounter, aGuid, aRegion);
+    mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion);
 
     // We need to recomposite to update the minimap.
     ScheduleComposition();
   }
+  return true;
 }
 
 void
 CompositorBridgeParent::ActorDestroy(ActorDestroyReason why)
 {
   StopAndClearResources();
 
   RemoveCompositor(mCompositorID);
@@ -1924,17 +1915,17 @@ static void
 EraseLayerState(uint64_t aId)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
 
   auto iter = sIndirectLayerTrees.find(aId);
   if (iter != sIndirectLayerTrees.end()) {
     CompositorBridgeParent* parent = iter->second.mParent;
     if (parent) {
-      parent->ClearVisibleRegions(aId, Nothing());
+      parent->ClearApproximatelyVisibleRegions(aId, Nothing());
     }
 
     sIndirectLayerTrees.erase(iter);
   }
 }
 
 /*static*/ void
 CompositorBridgeParent::DeallocateLayerTreeId(uint64_t aId)
@@ -2147,48 +2138,41 @@ public:
                                 const gfx::IntRect& aRect) override
   { return true; }
   virtual bool RecvFlushRendering() override { return true; }
   virtual bool RecvForcePresent() override { return true; }
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; }
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; }
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override  { return true; }
 
-  virtual bool RecvClearVisibleRegions(const uint64_t& aLayersId,
-                                       const uint32_t& aPresShellId) override
+  virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+                                                    const uint32_t& aPresShellId) override
   {
     CompositorBridgeParent* parent;
     { // scope lock
       MonitorAutoLock lock(*sIndirectLayerTreesLock);
       parent = sIndirectLayerTrees[aLayersId].mParent;
     }
-
-    if (!parent) {
-      return false;
+    if (parent) {
+      parent->ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
     }
-
-    parent->ClearVisibleRegions(aLayersId, Some(aPresShellId));
     return true;
   }
 
-  virtual bool RecvUpdateVisibleRegion(const VisibilityCounter& aCounter,
-                                       const ScrollableLayerGuid& aGuid,
-                                       const CSSIntRegion& aRegion) override
+  virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                                    const CSSIntRegion& aRegion) override
   {
     CompositorBridgeParent* parent;
     { // scope lock
       MonitorAutoLock lock(*sIndirectLayerTreesLock);
       parent = sIndirectLayerTrees[aGuid.mLayersId].mParent;
     }
-
-    if (!parent) {
-      return false;
+    if (parent) {
+      return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion);
     }
-
-    parent->UpdateVisibleRegion(aCounter, aGuid, aRegion);
     return true;
   }
 
   virtual bool RecvAllPluginsCaptured() override { return true; }
 
   virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override
   {
     // Don't support calculating frame uniformity on the child process and
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -299,26 +299,22 @@ public:
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override;
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override;
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override;
 
   // Unused for chrome <-> compositor communication (which this class does).
   // @see CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint
   virtual bool RecvRequestNotifyAfterRemotePaint() override { return true; };
 
-  virtual bool RecvClearVisibleRegions(const uint64_t& aLayersId,
-                                       const uint32_t& aPresShellId) override;
-  void ClearVisibleRegions(const uint64_t& aLayersId,
-                           const Maybe<uint32_t>& aPresShellId);
-  virtual bool RecvUpdateVisibleRegion(const VisibilityCounter& aCounter,
-                                       const ScrollableLayerGuid& aGuid,
-                                       const CSSIntRegion& aRegion) override;
-  void UpdateVisibleRegion(const VisibilityCounter& aCounter,
-                           const ScrollableLayerGuid& aGuid,
-                           const CSSIntRegion& aRegion);
+  virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+                                                    const uint32_t& aPresShellId) override;
+  void ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
+                                        const Maybe<uint32_t>& aPresShellId);
+  virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                                    const CSSIntRegion& aRegion) override;
 
   virtual bool RecvAllPluginsCaptured() override;
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& aTargetConfig,
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -27,17 +27,16 @@ using mozilla::layers::MaybeZoomConstrai
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using mozilla::CSSIntRegion from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDeviceIntRegion from "Units.h";
-using mozilla::VisibilityCounter from "VisibilityIPC.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 
@@ -191,28 +190,25 @@ parent:
   /**
    * The child (content/chrome thread) requests that the parent inform it when
    * the graphics objects are ready to display.
    * @see PBrowser
    * @see RemotePaintIsReady
    */
   async RequestNotifyAfterRemotePaint();
 
-  // The child sends a request to clear the visible regions (approximate,
-  // in-displayport, etc.) associated with the provided layers ID and pres shell
-  // ID (i.e., the regions for all view IDs associated with those IDs).
-  async ClearVisibleRegions(uint64_t layersId, uint32_t presShellId);
+  // The child clears the 'approximately visible' regions associated with the
+  // provided layers ID and pres shell ID (i.e., the regions for all view IDs
+  // associated with those IDs).
+  async ClearApproximatelyVisibleRegions(uint64_t layersId, uint32_t presShellId);
 
   // The child sends a region containing rects associated with the provided
-  // scrollable layer GUID that the child considers visible in the sense
-  // specified by |counter|.
+  // scrollable layer GUID that the child considers 'approximately visible'.
   // We visualize this information in the APZ minimap.
-  async UpdateVisibleRegion(VisibilityCounter counter,
-                            ScrollableLayerGuid guid,
-                            CSSIntRegion region);
+  async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region);
 
   /**
    * Sent when the child has finished CaptureAllPlugins.
    */
   async AllPluginsCaptured();
 
   async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial);
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -41,20 +41,20 @@
 #include "nsRegionFwd.h"
 #include "mozFlushType.h"
 #include "nsWeakReference.h"
 #include <stdio.h> // for FILE definition
 #include "nsChangeHint.h"
 #include "nsRefPtrHashtable.h"
 #include "nsClassHashtable.h"
 #include "nsPresArena.h"
+#include "nsIImageLoadingContent.h"
 #include "nsMargin.h"
 #include "nsFrameState.h"
 #include "Units.h"
-#include "Visibility.h"
 
 #ifdef MOZ_B2G
 #include "nsIHardwareKeyHandler.h"
 #endif
 
 class nsDocShell;
 class nsIDocument;
 class nsIFrame;
@@ -181,17 +181,16 @@ enum nsRectVisibility {
 class nsIPresShell : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRESSHELL_IID)
 
 protected:
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::gfx::SourceSurface SourceSurface;
-  using VisibilityCounter = mozilla::VisibilityCounter;
 
   enum eRenderFlag {
     STATE_IGNORING_VIEWPORT_SCROLLING = 0x1,
     STATE_DRAWWINDOW_NOT_FLUSHING = 0x2
   };
   typedef uint8_t RenderFlags; // for storing the above flags
 
 public:
@@ -1246,17 +1245,17 @@ public:
   {
     mObservesMutationsForPrint = aObserve;
   }
   bool ObservesNativeAnonMutationsForPrint()
   {
     return mObservesMutationsForPrint;
   }
 
-  virtual void SetIsActive(bool aIsActive, bool aIsHidden = true) = 0;
+  virtual nsresult SetIsActive(bool aIsActive, bool aIsHidden = true) = 0;
 
   bool IsActive()
   {
     return mIsActive;
   }
 
   // mouse capturing
   static CapturingContentInfo gCaptureInfo;
@@ -1603,22 +1602,21 @@ public:
   virtual void ScheduleApproximateFrameVisibilityUpdateNow() = 0;
 
   /// Clears the current list of approximately visible frames on this pres shell
   /// and replaces it with frames that are in the display list @aList.
   virtual void RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList) = 0;
   virtual void RebuildApproximateFrameVisibility(nsRect* aRect = nullptr,
                                                  bool aRemoveOnly = false) = 0;
 
-  /// Adds @aFrame to the visible frames set specified by @aCounter.
-  /// VisibilityCounter::MAY_BECOME_VISIBLE is not a valid argument.
-  virtual void MarkFrameVisible(nsIFrame* aFrame, VisibilityCounter aCounter) = 0;
+  /// Ensures @aFrame is in the list of approximately visible frames.
+  virtual void EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) = 0;
 
-  /// Marks @aFrame nonvisible and removes it from all sets of visible frames.
-  virtual void MarkFrameNonvisible(nsIFrame* aFrame) = 0;
+  /// Removes @aFrame from the list of approximately visible frames if present.
+  virtual void RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) = 0;
 
   /// Whether we should assume all frames are visible.
   virtual bool AssumeAllFramesVisible() = 0;
 
 
   /**
    * Returns whether the document's style set's rule processor for the
    * specified level of the cascade is shared by multiple style sets.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -21,17 +21,16 @@
 #include "mozilla/Logging.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CSSStyleSheet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/IMEStateManager.h"
-#include "mozilla/InitializerList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/UniquePtr.h"
@@ -212,18 +211,16 @@ using namespace mozilla::tasktracer;
 
 #define ANCHOR_SCROLL_FLAGS \
   (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES)
 
   // define the scalfactor of drag and drop images
   // relative to the max screen height/width
 #define RELATIVE_SCALEFACTOR 0.0925f
 
-using std::initializer_list;
-
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 using namespace mozilla::layout;
 using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
@@ -1190,19 +1187,18 @@ PresShell::Destroy()
   if (mDelayedPaintTimer) {
     mDelayedPaintTimer->Cancel();
     mDelayedPaintTimer = nullptr;
   }
 
   mSynthMouseMoveEvent.Revoke();
 
   mUpdateApproximateFrameVisibilityEvent.Revoke();
-  mNotifyCompositorOfVisibleRegionsChangeEvent.Revoke();
-
-  ClearVisibleFramesSets(Some(OnNonvisible::DISCARD_IMAGES));
+
+  ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DISCARD_IMAGES));
 
   if (mCaret) {
     mCaret->Terminate();
     mCaret = nullptr;
   }
 
   if (mSelection) {
     mSelection->DisconnectFromPresShell();
@@ -4608,197 +4604,16 @@ PresShell::StyleRuleRemoved(StyleSheetHa
 }
 
 nsIFrame*
 PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
 {
   return mFrameConstructor->GetPlaceholderFrameFor(aFrame);
 }
 
-static void
-SendUpdateVisibleRegion(CompositorBridgeChild* aCompositorChild,
-                        const VisibleRegions& aRegions,
-                        VisibilityCounter aCounter,
-                        uint64_t aLayersId,
-                        uint32_t aPresShellId)
-{
-  for (auto iter = aRegions.ConstIter(); !iter.Done(); iter.Next()) {
-    const ViewID viewId = iter.Key();
-    const CSSIntRegion* region = iter.UserData();
-    MOZ_ASSERT(region);
-
-    const ScrollableLayerGuid guid(aLayersId, aPresShellId, viewId);
-
-    aCompositorChild->SendUpdateVisibleRegion(aCounter, guid, *region);
-  }
-}
-
-void
-PresShell::NotifyCompositorOfVisibleRegionsChange()
-{
-  mNotifyCompositorOfVisibleRegionsChangeEvent.Revoke();
-
-  if (!mVisibleRegions) {
-    return;
-  }
-
-  // Retrieve the layers ID and pres shell ID.
-  TabChild* tabChild = TabChild::GetFrom(this);
-  if (!tabChild) {
-    return;
-  }
-
-  const uint64_t layersId = tabChild->LayersId();
-  const uint32_t presShellId = GetPresShellId();
-
-  // Retrieve the CompositorBridgeChild, which we'll use to communicate with
-  // the compositor.
-  LayerManager* layerManager = GetRootLayerManager();
-  if (!layerManager) {
-    return;
-  }
-
-  ClientLayerManager* clientLayerManager = layerManager->AsClientLayerManager();
-  if (!clientLayerManager) {
-    return;
-  }
-
-  CompositorBridgeChild* compositorChild = clientLayerManager->GetCompositorBridgeChild();
-  if (!compositorChild) {
-    return;
-  }
-
-  // Clear the old visible regions associated with this pres shell.
-  compositorChild->SendClearVisibleRegions(layersId, presShellId);
-
-  // Send the new visible regions to the compositor.
-  SendUpdateVisibleRegion(compositorChild,
-                          mVisibleRegions->mApproximate,
-                          VisibilityCounter::MAY_BECOME_VISIBLE,
-                          layersId, presShellId);
-
-  SendUpdateVisibleRegion(compositorChild,
-                          mVisibleRegions->mInDisplayPort,
-                          VisibilityCounter::IN_DISPLAYPORT,
-                          layersId, presShellId);
-}
-
-template <typename Func> void
-ForAllTrackedFramesInVisibleSet(const VisibleFrames& aFrames, Func aFunc)
-{
-  for (auto iter = aFrames.ConstIter(); !iter.Done(); iter.Next()) {
-    nsIFrame* frame = iter.Get()->GetKey();
-
-    // Call |aFunc| if we're still tracking the frame's visibility. (We may
-    // not be, if the frame disabled visibility tracking after we added it to
-    // the visible frames list.)
-    if (frame->TrackingVisibility()) {
-      aFunc(frame);
-    }
-  }
-}
-
-void
-PresShell::VisibleFramesContainer::AddFrame(nsIFrame* aFrame,
-                                            VisibilityCounter aCounter)
-{
-  MOZ_ASSERT(aFrame->TrackingVisibility());
-
-  VisibleFrames& frameSet = ForCounter(aCounter);
-
-  uint32_t count = frameSet.Count();
-  frameSet.PutEntry(aFrame);
-
-  if (frameSet.Count() == count) {
-    return;  // The frame was already present.
-  }
-
-  if (mSuppressingVisibility) {
-    return;  // We're not updating visibility counters right now.
-  }
-
-  aFrame->IncVisibilityCount(aCounter);
-}
-
-void
-PresShell::VisibleFramesContainer::RemoveFrame(nsIFrame* aFrame,
-                                               VisibilityCounter aCounter)
-{
-  VisibleFrames& frameSet = ForCounter(aCounter);
-
-  uint32_t count = frameSet.Count();
-  frameSet.RemoveEntry(aFrame);
-
-  if (frameSet.Count() == count) {
-    return;  // The frame wasn't present.
-  }
-
-  if (mSuppressingVisibility) {
-    return;  // We're not updating visibility counters right now.
-  }
-
-  if (!aFrame->TrackingVisibility()) {
-    // We stopped tracking visibility for this frame after it got added to the
-    // set. We don't need to update counters.
-    return;
-  }
-
-  aFrame->DecVisibilityCount(aCounter);
-}
-
-void
-PresShell::VisibleFramesContainer::SuppressVisibility()
-{
-  if (mSuppressingVisibility) {
-    return;  // Nothing to do.
-  }
-
-  mSuppressingVisibility = true;
-
-  // Decrement counters for all the frames we're tracking right now to
-  // maintain the invariant that when visibility is suppressed we don't
-  // increment the counters for any of the frames in our sets. Note that we
-  // decrement the visibility counters from lowest to highest priority to
-  // minimize the number of notifications we have to send - for example, if a
-  // frame is both MAY_BECOME_VISIBLE and IN_DISPLAYPORT, decrementing
-  // IN_DISPLAYPORT first would send it to the MAY_BECOME_VISIBLE state, and
-  // then decrementing MAY_BECOME_VISIBLE would send it to the NONVISIBLE
-  // state, whereas decrementing in the other order transitions the frame
-  // directly from IN_DISPLAYPORT to NONVISIBLE since the frame remains
-  // IN_DISPLAYPORT even if its MAY_BECOME_VISIBLE counter is 0.
-  ForAllTrackedFramesInVisibleSet(mApproximate, [&](nsIFrame* aFrame) {
-    aFrame->DecVisibilityCount(VisibilityCounter::MAY_BECOME_VISIBLE);
-  });
-  ForAllTrackedFramesInVisibleSet(mInDisplayPort, [&](nsIFrame* aFrame) {
-    aFrame->DecVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
-  });
-}
-
-void
-PresShell::VisibleFramesContainer::UnsuppressVisibility()
-{
-  if (!mSuppressingVisibility) {
-    return;  // Nothing to do.
-  }
-
-  mSuppressingVisibility = false;
-
-  // Increment counters for all the frames we're tracking right now to
-  // maintain the invariant that when visibility is not suppressed we
-  // increment the counters for the frames in our sets - this is the normal
-  // state, in other words. See SuppressVisibility() for why we increment in
-  // this order - the same reasoning applies, but in reverse.
-  ForAllTrackedFramesInVisibleSet(mInDisplayPort, [&](nsIFrame* aFrame) {
-    aFrame->IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
-  });
-  ForAllTrackedFramesInVisibleSet(mApproximate, [&](nsIFrame* aFrame) {
-    aFrame->IncVisibilityCount(VisibilityCounter::MAY_BECOME_VISIBLE);
-  });
-}
-
 nsresult
 PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags,
                           nscolor aBackgroundColor,
                           gfxContext* aThebesContext)
 {
   NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
 
   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
@@ -4897,19 +4712,16 @@ PresShell::RenderDocument(const nsRect& 
     flags |= PaintFrameFlags::PAINT_DOCUMENT_RELATIVE;
   }
 
   // Don't let drawWindow blow away our retained layer tree
   if ((flags & PaintFrameFlags::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) {
     flags &= ~PaintFrameFlags::PAINT_WIDGET_LAYERS;
   }
 
-  // We don't update the visible regions here because we're not painting to
-  // the window, and hence there should be no change.
-
   nsLayoutUtils::PaintFrame(&rc, rootFrame, nsRegion(aRect),
                             aBackgroundColor,
                             nsDisplayListBuilderMode::PAINTING,
                             flags);
 
   return NS_OK;
 }
 
@@ -5536,38 +5348,16 @@ LayerManager* PresShell::GetLayerManager
   if (rootView) {
     if (nsIWidget* widget = rootView->GetWidget()) {
       return widget->GetLayerManager();
     }
   }
   return nullptr;
 }
 
-LayerManager*
-PresShell::GetRootLayerManager()
-{
-  MOZ_ASSERT(mViewManager);
-  nsViewManager* viewManager = mViewManager;
-
-  while (nsView* view = viewManager->GetRootView()) {
-    if (nsIWidget* widget = view->GetWidget()) {
-      return widget->GetLayerManager();
-    }
-
-    nsView* parentView = view->GetParent();
-    if (!parentView) {
-      return nullptr;
-    }
-
-    viewManager = parentView->GetViewManager();
-  }
-
-  return nullptr;
-}
-
 bool PresShell::AsyncPanZoomEnabled()
 {
   NS_ASSERTION(mViewManager, "Should have view manager");
   nsView* rootView = mViewManager->GetRootView();
   if (rootView) {
     if (nsIWidget* widget = rootView->GetWidget()) {
       return widget->AsyncPanZoomEnabled();
     }
@@ -5862,24 +5652,27 @@ PresShell::ProcessSynthMouseMoveEvent(bo
     shell->DispatchSynthMouseMove(&event, !aFromScroll);
   }
 
   if (!aFromScroll) {
     mSynthMouseMoveEvent.Forget();
   }
 }
 
-void
-PresShell::AddFrameToVisibleRegions(nsIFrame* aFrame, VisibilityCounter aForCounter)
-{
-  if (!mVisibleRegions) {
+static void
+AddFrameToVisibleRegions(nsIFrame* aFrame,
+                         nsViewManager* aViewManager,
+                         Maybe<VisibleRegions>& aVisibleRegions)
+{
+  if (!aVisibleRegions) {
     return;
   }
 
   MOZ_ASSERT(aFrame);
+  MOZ_ASSERT(aViewManager);
 
   // Retrieve the view ID for this frame (which we obtain from the enclosing
   // scrollable frame).
   nsIScrollableFrame* scrollableFrame =
     nsLayoutUtils::GetNearestScrollableFrame(aFrame,
                                              nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE |
                                              nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT);
   if (!scrollableFrame) {
@@ -5904,245 +5697,191 @@ PresShell::AddFrameToVisibleRegions(nsIF
   nsLayoutUtils::TransformResult result =
     nsLayoutUtils::TransformRect(aFrame,
                                  scrollableFrame->GetScrolledFrame(),
                                  frameRectInScrolledFrameSpace);
   if (result != nsLayoutUtils::TransformResult::TRANSFORM_SUCCEEDED) {
     return;
   }
 
-  VisibleRegions& regions = mVisibleRegions->ForCounter(aForCounter);
-  CSSIntRegion* regionForView = regions.LookupOrAdd(viewID);
+  CSSIntRegion* regionForView = aVisibleRegions->LookupOrAdd(viewID);
   MOZ_ASSERT(regionForView);
 
   regionForView->OrWith(CSSPixel::FromAppUnitsRounded(frameRectInScrolledFrameSpace));
 }
 
 /* static */ void
-PresShell::MarkFramesInListApproximatelyVisible(const nsDisplayList& aList)
+PresShell::MarkFramesInListApproximatelyVisible(const nsDisplayList& aList,
+                                                Maybe<VisibleRegions>& aVisibleRegions)
 {
   for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
     nsDisplayList* sublist = item->GetChildren();
     if (sublist) {
-      MarkFramesInListApproximatelyVisible(*sublist);
+      MarkFramesInListApproximatelyVisible(*sublist, aVisibleRegions);
       continue;
     }
 
     nsIFrame* frame = item->Frame();
     MOZ_ASSERT(frame);
 
     if (!frame->TrackingVisibility()) {
       continue;
     }
 
     // Use the presshell containing the frame.
     auto* presShell = static_cast<PresShell*>(frame->PresContext()->PresShell());
+    uint32_t count = presShell->mApproximatelyVisibleFrames.Count();
     MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
-    presShell->mVisibleFrames.AddFrame(frame, VisibilityCounter::MAY_BECOME_VISIBLE);
-    presShell->AddFrameToVisibleRegions(frame, VisibilityCounter::MAY_BECOME_VISIBLE);
-  }
-}
-
-/**
- * This RAII class automatically handles updating visible frames sets. It also
- * handles updating visible regions (used for the APZ minimap debugger) when
- * the appropriate prefs are enabled.
- *
- * Because we don't want to cause unnecessary IPC traffic between the content
- * process and the compositor process, avoid nesting multiple
- * AutoUpdateVisibility instances. Instead, pass a list of VisibilityCounters
- * for all the types of visibility you want to update to
- * AutoUpdateVisibility's constructor.
- *
- * Normally the compositor is notified of new visible regions synchronously;
- * however, this is not safe within a layers transaction. In that situation,
- * specify Notify::eAsync to delay notifying the compositor until the
- * transaction is complete. Regardless, the visible frame sets themselves are
- * always updating synchronously.
- */
-struct MOZ_STACK_CLASS AutoUpdateVisibility
-{
-  enum class Notify
-  {
-    eSync,
-    eAsync
-  };
-
-  AutoUpdateVisibility(PresShell* aPresShell,
-                       std::initializer_list<VisibilityCounter> aCounters,
-                       Maybe<OnNonvisible> aNonvisibleAction = Nothing())
-    : AutoUpdateVisibility(aPresShell, Notify::eSync, aCounters, aNonvisibleAction)
-  { }
-
-  AutoUpdateVisibility(PresShell* aPresShell,
-                       Notify aNotifyStrategy,
-                       std::initializer_list<VisibilityCounter> aCounters,
-                       Maybe<OnNonvisible> aNonvisibleAction = Nothing())
-    : mNonvisibleAction(aNonvisibleAction)
-    , mPresShell(aPresShell)
-    , mNotifyStrategy(aNotifyStrategy)
-  {
-    // If visibility tracking is suppressed, we're not incrementing or
-    // decrementing visibility counters and we don't want to visualize visible
-    // regions, so we can just clear the visible frame sets and skip the rest.
-    if (mPresShell->mVisibleFrames.IsVisibilitySuppressed()) {
-      mPresShell->mVisibleFrames.mApproximate.Clear();
-      mPresShell->mVisibleFrames.mInDisplayPort.Clear();
-      mPresShell->mVisibleRegions = nullptr;
-      return;
-    }
-
-    // Clear the visible frames sets we're updating, but save the old set so
-    // we can decrement their counter later. This is how we mark frames
-    // nonvisible if they don't end up in the set during the visibility
-    // update.
-    for (VisibilityCounter counter : aCounters) {
-      switch (counter) {
-        case VisibilityCounter::MAY_BECOME_VISIBLE:
-          mOldApproximatelyVisibleFrames.emplace();
-          mPresShell->mVisibleFrames.mApproximate.SwapElements(*mOldApproximatelyVisibleFrames);
-          break;
-
-        case VisibilityCounter::IN_DISPLAYPORT:
-          mOldInDisplayPortFrames.emplace();
-          mPresShell->mVisibleFrames.mInDisplayPort.SwapElements(*mOldInDisplayPortFrames);
-          break;
-      }
-    }
-
-    // If we're not visualizing visible regions, we're done.
-    if (!gfxPrefs::APZMinimap() ||
-        !gfxPrefs::APZMinimapVisibilityEnabled()) {
-      mPresShell->mVisibleRegions = nullptr;
-      return;
-    }
-
-    // We're visualizing visible regions, so initialize a
-    // VisibleRegionsContainer to store them. Visibility-related functions we
-    // call will only do the work of populating this object and sending it to
-    // the compositor if we've created it, so we don't need to check the prefs
-    // everywhere.
-
-    if (mPresShell->mVisibleRegions) {
-      // Clear the regions we're about to update. We don't want to clear them
-      // all - we only clear the ones being updated. Otherwise, different
-      // visibility tracking methods will interfere with each other.
-      for (VisibilityCounter counter : aCounters) {
-        VisibleRegions& regions =
-          mPresShell->mVisibleRegions->ForCounter(counter);
-        regions.Clear();
-      }
-
-      return;
-    }
-
-    mPresShell->mVisibleRegions =
-      MakeUnique<PresShell::VisibleRegionsContainer>();
-  }
-
-  ~AutoUpdateVisibility()
-  {
-    // Decrement the counters for the old visible frames sets now. If they
-    // didn't get incremented during the visibility update, this will mark
-    // them nonvisible.
-    if (mOldApproximatelyVisibleFrames) {
-      ForAllTrackedFramesInVisibleSet(*mOldApproximatelyVisibleFrames, [&](nsIFrame* aFrame) {
-        aFrame->DecVisibilityCount(VisibilityCounter::MAY_BECOME_VISIBLE,
-                                   mNonvisibleAction);
-      });
-    }
-    if (mOldInDisplayPortFrames) {
-      ForAllTrackedFramesInVisibleSet(*mOldInDisplayPortFrames, [&](nsIFrame* aFrame) {
-        aFrame->DecVisibilityCount(VisibilityCounter::IN_DISPLAYPORT,
-                                   mNonvisibleAction);
-      });
-    }
-
-    // If we're not visualizing visible regions, we're done.
-    if (!mPresShell->mVisibleRegions) {
-      return;
-    }
-
-    if (mNotifyStrategy == Notify::eSync) {
-      mPresShell->NotifyCompositorOfVisibleRegionsChange();
-      return;
-    }
-
-    if (mPresShell->mNotifyCompositorOfVisibleRegionsChangeEvent.IsPending()) {
-      return;  // An async notify is already pending.
-    }
-
-    // Asynchronously notify the compositor of the new visible regions.
-    RefPtr<nsRunnableMethod<PresShell>> event =
-      NewRunnableMethod(mPresShell, &PresShell::NotifyCompositorOfVisibleRegionsChange);
-    if (NS_SUCCEEDED(NS_DispatchToMainThread(event))) {
-      mPresShell->mNotifyCompositorOfVisibleRegionsChangeEvent = event;
-    }
-  }
-
-private:
-  Maybe<VisibleFrames> mOldApproximatelyVisibleFrames;
-  Maybe<VisibleFrames> mOldInDisplayPortFrames;
-  Maybe<OnNonvisible> mNonvisibleAction;
-  PresShell* mPresShell;
-  Notify mNotifyStrategy;
-};
+    presShell->mApproximatelyVisibleFrames.PutEntry(frame);
+    if (presShell->mApproximatelyVisibleFrames.Count() > count) {
+      // The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
+      frame->IncApproximateVisibleCount();
+    }
+
+    AddFrameToVisibleRegions(frame, presShell->mViewManager, aVisibleRegions);
+  }
+}
+
+static void
+NotifyCompositorOfVisibleRegionsChange(PresShell* aPresShell,
+                                       const Maybe<VisibleRegions>& aRegions)
+{
+  if (!aRegions) {
+    return;
+  }
+
+  MOZ_ASSERT(aPresShell);
+
+  // Retrieve the layers ID and pres shell ID.
+  TabChild* tabChild = TabChild::GetFrom(aPresShell);
+  if (!tabChild) {
+    return;
+  }
+
+  const uint64_t layersId = tabChild->LayersId();
+  const uint32_t presShellId = aPresShell->GetPresShellId();
+
+  // Retrieve the CompositorBridgeChild.
+  LayerManager* layerManager = aPresShell->GetLayerManager();
+  if (!layerManager) {
+    return;
+  }
+
+  ClientLayerManager* clientLayerManager = layerManager->AsClientLayerManager();
+  if (!clientLayerManager) {
+    return;
+  }
+
+  CompositorBridgeChild* compositorChild = clientLayerManager->GetCompositorBridgeChild();
+  if (!compositorChild) {
+    return;
+  }
+
+  // Clear the old approximately visible regions associated with this document.
+  compositorChild->SendClearApproximatelyVisibleRegions(layersId, presShellId);
+
+  // Send the new approximately visible regions to the compositor.
+  for (auto iter = aRegions->ConstIter(); !iter.Done(); iter.Next()) {
+    const ViewID viewId = iter.Key();
+    const CSSIntRegion* region = iter.UserData();
+    MOZ_ASSERT(region);
+
+    const ScrollableLayerGuid guid(layersId, presShellId, viewId);
+
+    compositorChild->SendNotifyApproximatelyVisibleRegion(guid, *region);
+  }
+}
+
+/* static */ void
+PresShell::DecApproximateVisibleCount(VisibleFrames& aFrames,
+                                      Maybe<OnNonvisible> aNonvisibleAction
+                                        /* = Nothing() */)
+{
+  for (auto iter = aFrames.Iter(); !iter.Done(); iter.Next()) {
+    nsIFrame* frame = iter.Get()->GetKey();
+    // Decrement the frame's visible count if we're still tracking its
+    // visibility. (We may not be, if the frame disabled visibility tracking
+    // after we added it to the visible frames list.)
+    if (frame->TrackingVisibility()) {
+      frame->DecApproximateVisibleCount(aNonvisibleAction);
+    }
+  }
+}
 
 void
 PresShell::RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList)
 {
   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
   mApproximateFrameVisibilityVisited = true;
 
-  AutoUpdateVisibility update(this, { VisibilityCounter::MAY_BECOME_VISIBLE });
-
-  MarkFramesInListApproximatelyVisible(aList);
+  // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
+  // them in oldApproxVisibleFrames.
+  VisibleFrames oldApproximatelyVisibleFrames;
+  mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
+
+  // If we're visualizing visible regions, create a VisibleRegions object to
+  // store information about them. The functions we call will populate this
+  // object and send it to the compositor only if it's Some(), so we don't
+  // need to check the prefs everywhere.
+  Maybe<VisibleRegions> visibleRegions;
+  if (gfxPrefs::APZMinimap() && gfxPrefs::APZMinimapVisibilityEnabled()) {
+    visibleRegions.emplace();
+  }
+
+  MarkFramesInListApproximatelyVisible(aList, visibleRegions);
+
+  DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
+
+  NotifyCompositorOfVisibleRegionsChange(this, visibleRegions);
 }
 
 /* static */ void
-PresShell::ClearVisibleFramesForUnvisitedPresShells(nsView* aView, bool aClear)
+PresShell::ClearApproximateFrameVisibilityVisited(nsView* aView, bool aClear)
 {
   nsViewManager* vm = aView->GetViewManager();
   if (aClear) {
     PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell());
     if (!presShell->mApproximateFrameVisibilityVisited) {
-      presShell->ClearVisibleFramesSets();
+      presShell->ClearApproximatelyVisibleFramesList();
     }
     presShell->mApproximateFrameVisibilityVisited = false;
   }
   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
-    ClearVisibleFramesForUnvisitedPresShells(v, v->GetViewManager() != vm);
-  }
-}
-
-void
-PresShell::ClearVisibleFramesSets(Maybe<OnNonvisible> aNonvisibleAction
-                                    /* = Nothing() */)
-{
-  // Do an empty update, which will clear any existing visible frames and
-  // regions.
-  AutoUpdateVisibility update(this, {
-    VisibilityCounter::MAY_BECOME_VISIBLE,
-    VisibilityCounter::IN_DISPLAYPORT
-  }, aNonvisibleAction);
+    ClearApproximateFrameVisibilityVisited(v, v->GetViewManager() != vm);
+  }
+}
+
+void
+PresShell::ClearApproximatelyVisibleFramesList(Maybe<OnNonvisible> aNonvisibleAction
+                                                 /* = Nothing() */)
+{
+  DecApproximateVisibleCount(mApproximatelyVisibleFrames, aNonvisibleAction);
+  mApproximatelyVisibleFrames.Clear();
 }
 
 void
 PresShell::MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
                                                    const nsRect& aRect,
+                                                   Maybe<VisibleRegions>& aVisibleRegions,
                                                    bool aRemoveOnly /* = false */)
 {
   MOZ_ASSERT(aFrame->PresContext()->PresShell() == this, "wrong presshell");
 
   if (aFrame->TrackingVisibility() &&
       aFrame->StyleVisibility()->IsVisible() &&
-      (!aRemoveOnly || aFrame->IsVisibleOrMayBecomeVisibleSoon())) {
+      (!aRemoveOnly || aFrame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE)) {
     MOZ_ASSERT(!AssumeAllFramesVisible());
-    mVisibleFrames.AddFrame(aFrame, VisibilityCounter::MAY_BECOME_VISIBLE);
-    AddFrameToVisibleRegions(aFrame, VisibilityCounter::MAY_BECOME_VISIBLE);
+    uint32_t count = mApproximatelyVisibleFrames.Count();
+    mApproximatelyVisibleFrames.PutEntry(aFrame);
+    if (mApproximatelyVisibleFrames.Count() > count) {
+      // The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
+      aFrame->IncApproximateVisibleCount();
+    }
+
+    AddFrameToVisibleRegions(aFrame, mViewManager, aVisibleRegions);
   }
 
   nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
   if (subdocFrame) {
     nsIPresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
       nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
     if (presShell && !presShell->AssumeAllFramesVisible()) {
       nsRect rect = aRect;
@@ -6201,41 +5940,57 @@ PresShell::MarkFramesInSubtreeApproximat
           nsRect out;
           if (nsDisplayTransform::UntransformRect(r, overflow, child, &out)) {
             r = out;
           } else {
             r.SetEmpty();
           }
         }
       }
-      MarkFramesInSubtreeApproximatelyVisible(child, r);
+      MarkFramesInSubtreeApproximatelyVisible(child, r, aVisibleRegions);
     }
   }
 }
 
 void
 PresShell::RebuildApproximateFrameVisibility(nsRect* aRect,
                                              bool aRemoveOnly /* = false */)
 {
   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
   mApproximateFrameVisibilityVisited = true;
 
   nsIFrame* rootFrame = GetRootFrame();
   if (!rootFrame) {
     return;
   }
 
-  AutoUpdateVisibility update(this, { VisibilityCounter::MAY_BECOME_VISIBLE });
+  // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
+  // them in oldApproximatelyVisibleFrames.
+  VisibleFrames oldApproximatelyVisibleFrames;
+  mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
+
+  // If we're visualizing visible regions, create a VisibleRegions object to
+  // store information about them. The functions we call will populate this
+  // object and send it to the compositor only if it's Some(), so we don't
+  // need to check the prefs everywhere.
+  Maybe<VisibleRegions> visibleRegions;
+  if (gfxPrefs::APZMinimap() && gfxPrefs::APZMinimapVisibilityEnabled()) {
+    visibleRegions.emplace();
+  }
 
   nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
   if (aRect) {
     vis = *aRect;
   }
 
-  MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, aRemoveOnly);
+  MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, visibleRegions, aRemoveOnly);
+
+  DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
+
+  NotifyCompositorOfVisibleRegionsChange(this, visibleRegions);
 }
 
 void
 PresShell::UpdateApproximateFrameVisibility()
 {
   DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
 }
 
@@ -6249,29 +6004,29 @@ PresShell::DoUpdateApproximateFrameVisib
 
   if (mHaveShutDown || mIsDestroying) {
     return;
   }
 
   // call update on that frame
   nsIFrame* rootFrame = GetRootFrame();
   if (!rootFrame) {
-    ClearVisibleFramesSets(Some(OnNonvisible::DISCARD_IMAGES));
+    ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DISCARD_IMAGES));
     return;
   }
 
   RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
-  ClearVisibleFramesForUnvisitedPresShells(rootFrame->GetView(), true);
+  ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
 
 #ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
   // This can be used to debug the frame walker by comparing beforeFrameList
-  // and mVisibleFrames.mApproximate in RebuildFrameVisibilityDisplayList to
-  // see if they produce the same results (mVisibleFrames.mApproximate holds
-  // the frames the display list thinks are visible, beforeFrameList holds the
-  // frames the frame walker thinks are visible).
+  // and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see if
+  // they produce the same results (mApproximatelyVisibleFrames holds the frames the
+  // display list thinks are visible, beforeFrameList holds the frames the
+  // frame walker thinks are visible).
   nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilderMode::FRAME_VISIBILITY, false);
   nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
   nsIFrame* rootScroll = GetRootScrollFrame();
   if (rootScroll) {
     nsIContent* content = rootScroll->GetContent();
     if (content) {
       Unused << nsLayoutUtils::GetDisplayPortForVisibilityTesting(content, &updateRect,
         RelativeTo::ScrollFrame);
@@ -6284,17 +6039,17 @@ PresShell::DoUpdateApproximateFrameVisib
   builder.IgnorePaintSuppression();
   builder.EnterPresShell(rootFrame, updateRect);
   nsDisplayList list;
   rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
   builder.LeavePresShell(rootFrame, updateRect);
 
   RebuildApproximateFrameVisibilityDisplayList(list);
 
-  ClearVisibleFramesForUnvisitedPresShells(rootFrame->GetView(), true);
+  ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
 
   list.DeleteAll();
 #endif
 }
 
 bool
 PresShell::AssumeAllFramesVisible()
 {
@@ -6389,68 +6144,70 @@ PresShell::ScheduleApproximateFrameVisib
   RefPtr<nsRunnableMethod<PresShell> > ev =
     NewRunnableMethod(this, &PresShell::UpdateApproximateFrameVisibility);
   if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
     mUpdateApproximateFrameVisibilityEvent = ev;
   }
 }
 
 void
-PresShell::MarkFrameVisible(nsIFrame* aFrame, VisibilityCounter aCounter)
-{
-  MOZ_ASSERT(aCounter != VisibilityCounter::MAY_BECOME_VISIBLE,
-             "MAY_BECOME_VISIBLE should only be managed from within PresShell");
-
+PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame)
+{
   if (!aFrame->TrackingVisibility()) {
     return;
   }
 
   if (AssumeAllFramesVisible()) {
-    // Force to maximum visibility (IN_DISPLAYPORT) regardless of aCounter's value.
-    if (aFrame->GetVisibility() != Visibility::IN_DISPLAYPORT) {
-      aFrame->IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
-    }
+    aFrame->IncApproximateVisibleCount();
     return;
   }
 
 #ifdef DEBUG
   // Make sure it's in this pres shell.
   nsCOMPtr<nsIContent> content = aFrame->GetContent();
   if (content) {
     PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
     MOZ_ASSERT(!shell || shell == this, "wrong shell");
   }
 #endif
 
-  mVisibleFrames.AddFrame(aFrame, aCounter);
-  AddFrameToVisibleRegions(aFrame, aCounter);
-}
-
-void
-PresShell::MarkFrameNonvisible(nsIFrame* aFrame)
+  if (!mApproximatelyVisibleFrames.Contains(aFrame)) {
+    MOZ_ASSERT(!AssumeAllFramesVisible());
+    mApproximatelyVisibleFrames.PutEntry(aFrame);
+    aFrame->IncApproximateVisibleCount();
+  }
+}
+
+void
+PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame)
 {
 #ifdef DEBUG
   // Make sure it's in this pres shell.
   nsCOMPtr<nsIContent> content = aFrame->GetContent();
   if (content) {
     PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
     MOZ_ASSERT(!shell || shell == this, "wrong shell");
   }
 #endif
 
   if (AssumeAllFramesVisible()) {
-    MOZ_ASSERT(mVisibleFrames.mApproximate.Count() == 0,
-               "Shouldn't have any frames in the approximate visibility set");
-    MOZ_ASSERT(mVisibleFrames.mInDisplayPort.Count() == 0,
-               "Shouldn't have any frames in the in-displayport visibility set");
-    return;
-  }
-
-  mVisibleFrames.RemoveFrame(aFrame, VisibilityCounter::MAY_BECOME_VISIBLE);
-  mVisibleFrames.RemoveFrame(aFrame, VisibilityCounter::IN_DISPLAYPORT);
+    MOZ_ASSERT(mApproximatelyVisibleFrames.Count() == 0,
+               "Shouldn't have any frames in the table");
+    return;
+  }
+
+  uint32_t count = mApproximatelyVisibleFrames.Count();
+  mApproximatelyVisibleFrames.RemoveEntry(aFrame);
+
+  if (aFrame->TrackingVisibility() &&
+      mApproximatelyVisibleFrames.Count() < count) {
+    // aFrame was in the hashtable, and we're still tracking its visibility,
+    // so we need to decrement its visible count.
+    aFrame->DecApproximateVisibleCount();
+  }
 }
 
 class nsAutoNotifyDidPaint
 {
 public:
   nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags)
     : mShell(aShell), mFlags(aFlags)
   {
@@ -6628,24 +6385,19 @@ PresShell::Paint(nsView*        aViewToP
     flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
   }
   if (mNextPaintCompressed) {
     flags |= PaintFrameFlags::PAINT_COMPRESSED;
     mNextPaintCompressed = false;
   }
 
   if (frame) {
-    AutoUpdateVisibility update(this, AutoUpdateVisibility::Notify::eAsync, {
-      VisibilityCounter::IN_DISPLAYPORT
-    });
-
     // We can paint directly into the widget using its layer manager.
     nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor,
                               nsDisplayListBuilderMode::PAINTING, flags);
-
     return;
   }
 
   RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
   if (root) {
     nsPresContext* pc = GetPresContext();
     nsIntRect bounds =
       pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
@@ -9243,17 +8995,16 @@ FreezeSubDocument(nsIDocument *aDocument
 
   return true;
 }
 
 void
 PresShell::Freeze()
 {
   mUpdateApproximateFrameVisibilityEvent.Revoke();
-  mNotifyCompositorOfVisibleRegionsChangeEvent.Revoke();
 
   MaybeReleaseCapturingContent();
 
   mDocument->EnumerateActivityObservers(FreezeElement, nullptr);
 
   if (mCaret) {
     SetCaretEnabled(false);
   }
@@ -9266,18 +9017,19 @@ PresShell::Freeze()
 
   nsPresContext* presContext = GetPresContext();
   if (presContext &&
       presContext->RefreshDriver()->PresContext() == presContext) {
     presContext->RefreshDriver()->Freeze();
   }
 
   mFrozen = true;
-
-  UpdateFrameVisibilityOnActiveStateChange();
+  if (mDocument) {
+    UpdateImageLockingState();
+  }
 }
 
 void
 PresShell::FireOrClearDelayedEvents(bool aFireEvents)
 {
   mNoDelayedMouseEvents = false;
   mNoDelayedKeyEvents = false;
   if (!aFireEvents) {
@@ -9333,18 +9085,18 @@ PresShell::Thaw()
     mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr);
 
   // Get the activeness of our presshell, as this might have changed
   // while we were in the bfcache
   QueryIsActive();
 
   // We're now unfrozen
   mFrozen = false;
-
-  UpdateFrameVisibilityOnActiveStateChange();
+  UpdateImageLockingState();
+
   UnsuppressPainting();
 }
 
 //--------------------------------------------------------
 // Start of protected and private methods on the PresShell
 //--------------------------------------------------------
 
 void
@@ -11165,17 +10917,17 @@ SetPluginIsActive(nsISupports* aSupports
 
   nsIFrame *frame = content->GetPrimaryFrame();
   nsIObjectFrame *objectFrame = do_QueryFrame(frame);
   if (objectFrame) {
     objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
   }
 }
 
-void
+nsresult
 PresShell::SetIsActive(bool aIsActive, bool aIsHidden)
 {
   NS_PRECONDITION(mDocument, "should only be called with a document");
 
   mIsActive = aIsActive;
 
   // Keep track of whether we've called TabChild::MakeHidden() or not.
   // This can still be true even if aIsHidden is false.
@@ -11187,18 +10939,17 @@ PresShell::SetIsActive(bool aIsActive, b
     presContext->RefreshDriver()->SetThrottled(!mIsActive);
   }
 
   // Propagate state-change to my resource documents' PresShells
   mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
                                         &aIsActive);
   mDocument->EnumerateActivityObservers(SetPluginIsActive,
                                         &aIsActive);
-  UpdateFrameVisibilityOnActiveStateChange();
-
+  nsresult rv = UpdateImageLockingState();
 #ifdef ACCESSIBILITY
   if (aIsActive) {
     nsAccessibilityService* accService = AccService();
     if (accService) {
       accService->PresShellActivated(this);
     }
   }
 #endif
@@ -11236,47 +10987,43 @@ PresShell::SetIsActive(bool aIsActive, b
             root->SchedulePaint();
           }
         }
       } else {
         tab->MakeHidden();
       }
     }
   }
-}
-
-void
-PresShell::UpdateFrameVisibilityOnActiveStateChange()
-{
-  // A pres shell is "active" if it's being displayed in a visible tab. We
-  // only consider frames visible if they're in an active pres shell. A pres
-  // shell may also be "frozen", which means that it does not receive events -
-  // this is the case for e.g. documents in the back/forward cache. We don't
-  // want to consider frames visible in frozen pres shells, so we'll only
-  // treat this pres shell as active if it's also not frozen.
-  bool treatAsActive = mIsActive && !mFrozen;
-
-  // Update the document's image locking state.
-  // XXX(seth): Note that in the future the visibility tracking API will allow
-  // frames and content to manage their own image locking state. (And they
-  // mostly already do.) However, we can't get rid of the old approach until
-  // CSS images have visibility tracking - see bug 1218990.
-  mDocument->SetImageLockingState(treatAsActive);
-
-  if (treatAsActive) {
-    // Unsuppress frame visibility, marking the frames in all of our visible
-    // frame sets as visible. This will trigger decoding for visible images,
-    // which will help minimize flashing when a document gets foregrounded.
-    mVisibleFrames.UnsuppressVisibility();
-  } else {
-    // Suppress frame visibility, marking the frames in all of our visible
-    // frame sets nonvisible. This will allow them to stop animations, release
-    // memory, and generally reduce their resource usage.
-    mVisibleFrames.SuppressVisibility();
-  }
+  return rv;
+}
+
+/*
+ * Determines the current image locking state. Called when one of the
+ * dependent factors changes.
+ */
+nsresult
+PresShell::UpdateImageLockingState()
+{
+  // We're locked if we're both thawed and active.
+  bool locked = !mFrozen && mIsActive;
+
+  nsresult rv = mDocument->SetImageLockingState(locked);
+
+  if (locked) {
+    // Request decodes for visible image frames; we want to start decoding as
+    // quickly as possible when we get foregrounded to minimize flashing.
+    for (auto iter = mApproximatelyVisibleFrames.Iter(); !iter.Done(); iter.Next()) {
+      nsImageFrame* imageFrame = do_QueryFrame(iter.Get()->GetKey());
+      if (imageFrame) {
+        imageFrame->MaybeDecodeForPredictedSize();
+      }
+    }
+  }
+
+  return rv;
 }
 
 PresShell*
 PresShell::GetRootPresShell()
 {
   if (mPresContext) {
     nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
     if (rootPresContext) {
@@ -11294,22 +11041,17 @@ PresShell::AddSizeOfIncludingThis(Malloc
                                   size_t *aTextRunsSize,
                                   size_t *aPresContextSize)
 {
   mFrameArena.AddSizeOfExcludingThis(aMallocSizeOf, aArenaObjectsSize);
   *aPresShellSize += aMallocSizeOf(this);
   if (mCaret) {
     *aPresShellSize += mCaret->SizeOfIncludingThis(aMallocSizeOf);
   }
-  *aPresShellSize += mVisibleFrames.mApproximate.ShallowSizeOfExcludingThis(aMallocSizeOf);
-  *aPresShellSize += mVisibleFrames.mInDisplayPort.ShallowSizeOfExcludingThis(aMallocSizeOf);
-  *aPresShellSize += mVisibleRegions
-                   ? mVisibleRegions->mApproximate.ShallowSizeOfIncludingThis(aMallocSizeOf) +
-                     mVisibleRegions->mInDisplayPort.ShallowSizeOfIncludingThis(aMallocSizeOf)
-                   : 0;
+  *aPresShellSize += mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
   *aPresShellSize += mFramesToDirty.ShallowSizeOfExcludingThis(aMallocSizeOf);
   *aPresShellSize += aArenaObjectsSize->mOther;
 
   if (nsStyleSet* styleSet = StyleSet()->GetAsGecko()) {
     *aStyleSetsSize += styleSet->SizeOfIncludingThis(aMallocSizeOf);
   } else {
     NS_WARNING("ServoStyleSets do not support memory measurements yet");
   }
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -77,18 +77,16 @@ class PresShell final : public nsIPresSh
                         public nsIObserver,
                         public nsSupportsWeakReference
 {
   template <typename T> using Maybe = mozilla::Maybe<T>;
   using Nothing = mozilla::Nothing;
   using OnNonvisible = mozilla::OnNonvisible;
   using RawSelectionType = mozilla::RawSelectionType;
   using SelectionType = mozilla::SelectionType;
-  template <typename T> using UniquePtr = mozilla::UniquePtr<T>;
-  using VisibilityCounter = mozilla::VisibilityCounter;
   using VisibleFrames = mozilla::VisibleFrames;
   using VisibleRegions = mozilla::VisibleRegions;
 
 public:
   PresShell();
 
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
@@ -366,17 +364,17 @@ public:
 
   virtual void AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
                                              nsDisplayList& aList,
                                              nsIFrame* aFrame,
                                              const nsRect& aBounds) override;
 
   virtual nscolor ComputeBackstopColor(nsView* aDisplayRoot) override;
 
-  virtual void SetIsActive(bool aIsActive, bool aIsHidden = true) override;
+  virtual nsresult SetIsActive(bool aIsActive, bool aIsHidden = true) override;
 
   virtual bool GetIsViewportOverridden() override {
     return (mMobileViewportManager != nullptr);
   }
 
   virtual bool IsLayoutFlushObserver() override
   {
     return GetPresContext()->RefreshDriver()->
@@ -410,18 +408,18 @@ public:
 
   void ScheduleApproximateFrameVisibilityUpdateSoon() override;
   void ScheduleApproximateFrameVisibilityUpdateNow() override;
 
   void RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList) override;
   void RebuildApproximateFrameVisibility(nsRect* aRect = nullptr,
                                          bool aRemoveOnly = false) override;
 
-  void MarkFrameVisible(nsIFrame* aFrame, VisibilityCounter aCounter) override;
-  void MarkFrameNonvisible(nsIFrame* aFrame) override;
+  void EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) override;
+  void RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) override;
 
   bool AssumeAllFramesVisible() override;
 
 
   virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) override;
 
   virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
                                           const mozilla::WidgetKeyboardEvent& aEvent,
@@ -588,19 +586,16 @@ protected:
    * Methods to handle changes to user and UA sheet lists that we get
    * notified about.
    */
   void AddUserSheet(nsISupports* aSheet);
   void AddAgentSheet(nsISupports* aSheet);
   void AddAuthorSheet(nsISupports* aSheet);
   void RemoveSheet(mozilla::SheetType aType, nsISupports* aSheet);
 
-  /// @return the LayerManager at the root of the view tree.
-  LayerManager* GetRootLayerManager();
-
   // Hide a view if it is a popup
   void HideViewIfPopup(nsView* aView);
 
   // Utility method to restore the root scrollframe state
   void RestoreRootScrollPosition();
 
   void MaybeReleaseCapturingContent();
 
@@ -683,16 +678,17 @@ protected:
     }
   private:
     PresShell* mPresShell;
     bool mFromScroll;
   };
   void ProcessSynthMouseMoveEvent(bool aFromScroll);
 
   void QueryIsActive();
+  nsresult UpdateImageLockingState();
 
   bool InZombieDocument(nsIContent *aContent);
   already_AddRefed<nsIPresShell> GetParentPresShellForEventHandling();
   nsIContent* GetCurrentEventContent();
   nsIFrame* GetCurrentEventFrame();
   nsresult RetargetEventToParent(mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus* aEventStatus);
   void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
@@ -775,90 +771,34 @@ protected:
 
   //////////////////////////////////////////////////////////////////////////////
   // Approximate frame visibility tracking implementation.
   //////////////////////////////////////////////////////////////////////////////
 
   void UpdateApproximateFrameVisibility();
   void DoUpdateApproximateFrameVisibility(bool aRemoveOnly);
 
-  void ClearVisibleFramesSets(Maybe<OnNonvisible> aNonvisibleAction = Nothing());
-  static void ClearVisibleFramesForUnvisitedPresShells(nsView* aView, bool aClear);
-  static void MarkFramesInListApproximatelyVisible(const nsDisplayList& aList);
+  void ClearApproximatelyVisibleFramesList(Maybe<mozilla::OnNonvisible> aNonvisibleAction
+                                             = Nothing());
+  static void ClearApproximateFrameVisibilityVisited(nsView* aView, bool aClear);
+  static void MarkFramesInListApproximatelyVisible(const nsDisplayList& aList,
+                                                   Maybe<VisibleRegions>& aVisibleRegions);
   void MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
                                                const nsRect& aRect,
+                                               Maybe<VisibleRegions>& aVisibleRegions,
                                                bool aRemoveOnly = false);
-  void UpdateFrameVisibilityOnActiveStateChange();
 
-  void InitVisibleRegionsIfVisualizationEnabled(VisibilityCounter aForCounter);
-  void AddFrameToVisibleRegions(nsIFrame* aFrame, VisibilityCounter aForCounter);
-  void NotifyCompositorOfVisibleRegionsChange();
+  void DecApproximateVisibleCount(VisibleFrames& aFrames,
+                                  Maybe<OnNonvisible> aNonvisibleAction = Nothing());
 
   nsRevocableEventPtr<nsRunnableMethod<PresShell>> mUpdateApproximateFrameVisibilityEvent;
-  nsRevocableEventPtr<nsRunnableMethod<PresShell>> mNotifyCompositorOfVisibleRegionsChangeEvent;
 
-  struct VisibleFramesContainer
-  {
-    VisibleFramesContainer() : mSuppressingVisibility(false) { }
-
-    void AddFrame(nsIFrame* aFrame, VisibilityCounter aCounter);
-    void RemoveFrame(nsIFrame* aFrame, VisibilityCounter aCounter);
-
-    bool IsVisibilitySuppressed() const { return mSuppressingVisibility; }
-    void SuppressVisibility();
-    void UnsuppressVisibility();
-
-    VisibleFrames& ForCounter(VisibilityCounter aCounter)
-    {
-      switch (aCounter)
-      {
-        case VisibilityCounter::MAY_BECOME_VISIBLE: return mApproximate;
-        case VisibilityCounter::IN_DISPLAYPORT:     return mInDisplayPort;
-      }
-      MOZ_CRASH();
-    }
-
-    // A set of frames that were visible or could be visible soon at the time
-    // that we last did an approximate frame visibility update.
-    VisibleFrames mApproximate;
-
-    // A set of frames that were visible in the displayport the last time we painted.
-    VisibleFrames mInDisplayPort;
-
-    bool mSuppressingVisibility;
-  };
-
-  VisibleFramesContainer mVisibleFrames;
-
-  struct VisibleRegionsContainer
-  {
-    VisibleRegions& ForCounter(VisibilityCounter aCounter)
-    {
-      switch (aCounter)
-      {
-        case VisibilityCounter::MAY_BECOME_VISIBLE: return mApproximate;
-        case VisibilityCounter::IN_DISPLAYPORT:     return mInDisplayPort;
-      }
-      MOZ_CRASH();
-    }
-
-    // The approximately visible regions calculated during the last update to
-    // approximate frame visibility.
-    VisibleRegions mApproximate;
-
-    // The in-displayport visible regions calculated during the last paint.
-    VisibleRegions mInDisplayPort;
-  };
-
-  // The most recent visible regions we've computed. Only non-null if the APZ
-  // minimap visibility visualization was enabled during the last visibility
-  // update.
-  UniquePtr<VisibleRegionsContainer> mVisibleRegions;
-
-  friend struct AutoUpdateVisibility;
+  // A set of frames that were visible or could be visible soon at the time
+  // that we last did an approximate frame visibility update.
+  VisibleFrames mApproximatelyVisibleFrames;
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
   //////////////////////////////////////////////////////////////////////////////
 
   void HandleKeyboardEvent(nsINode* aTarget,
                            mozilla::WidgetKeyboardEvent& aEvent,
--- a/layout/generic/Visibility.h
+++ b/layout/generic/Visibility.h
@@ -3,57 +3,41 @@
  * 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/. */
 
 /**
  * Declares visibility-related types. @Visibility is an enumeration of the
  * possible visibility states of a frame. @OnNonvisible is an enumeration that
  * allows callers to request a specific action when a frame transitions from
  * visible to nonvisible.
- *
- * IPC serializers are available in VisibilityIPC.h.
  */
 
 #ifndef mozilla_layout_generic_Visibility_h
 #define mozilla_layout_generic_Visibility_h
 
 namespace mozilla {
 
 // Visibility states for frames.
 enum class Visibility : uint8_t
 {
-  // We're not tracking visibility for this frame.
+  // Indicates that we're not tracking visibility for this frame.
   UNTRACKED,
 
-  // This frame is nonvisible - i.e., it was not within the displayport as of
-  // the last paint (in which case it'd be IN_DISPLAYPORT) and our heuristics
-  // aren't telling us that it may become visible soon (in which case it'd be
-  // MAY_BECOME_VISIBLE).
-  NONVISIBLE,
-
-  // This frame is nonvisible now, but our heuristics tell us it may become
-  // visible soon. These heuristics are updated on a relatively slow timer, so a
-  // frame being marked MAY_BECOME_VISIBLE does not imply any particular
-  // relationship between the frame and the displayport.
-  MAY_BECOME_VISIBLE,
+  // Indicates that the frame is probably nonvisible. Visible frames *may* be
+  // APPROXIMATELY_NONVISIBLE because approximate visibility is not updated
+  // synchronously. Some truly nonvisible frames may be marked
+  // APPROXIMATELY_VISIBLE instead if our heuristics lead us to think they may
+  // be visible soon.
+  APPROXIMATELY_NONVISIBLE,
 
-  // This frame was within the displayport as of the last paint. That doesn't
-  // necessarily mean that the frame is visible - it may still lie outside the
-  // viewport - but it does mean that the user may scroll the frame into view
-  // asynchronously at any time (due to APZ), so for most purposes such a frame
-  // should be treated as truly visible.
-  IN_DISPLAYPORT
-};
-
-// The subset of the states in @Visibility which have a per-frame counter. This
-// is used in the implementation of visibility tracking.
-enum class VisibilityCounter : uint8_t
-{
-  MAY_BECOME_VISIBLE,
-  IN_DISPLAYPORT
+  // Indicates that the frame is either visible now or is likely to be visible
+  // soon according to our heuristics. As with APPROXIMATELY_NONVISIBLE, it's
+  // important to note that approximately visibility is not updated
+  // synchronously, so this information may be out of date.
+  APPROXIMATELY_VISIBLE
 };
 
 // Requested actions when frames transition to the nonvisible state.
 enum class OnNonvisible : uint8_t
 {
   DISCARD_IMAGES  // Discard images associated with the frame.
 };
 
deleted file mode 100644
--- a/layout/generic/VisibilityIPC.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-/**
- * Declares IPC serializers for the visibility-related types in Visibility.h.
- * These are separated out to reduce the number of moz.build files that need to
- * include chromium IPC headers, since Visibility.h is included from nsIFrame.h
- * which is widely included.
- */
-
-#ifndef mozilla_layout_generic_VisibilityIPC_h
-#define mozilla_layout_generic_VisibilityIPC_h
-
-#include "ipc/IPCMessageUtils.h"
-
-#include "Visibility.h"
-
-namespace IPC {
-
-template<>
-struct ParamTraits<mozilla::VisibilityCounter>
-{
-  typedef mozilla::VisibilityCounter paramType;
-
-  static void Write(Message* aMsg, const paramType& aParam)
-  {
-    WriteParam(aMsg, uint8_t(aParam));
-  }
-
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
-  {
-    uint8_t valueAsByte;
-    if (ReadParam(aMsg, aIter, &valueAsByte)) {
-      *aResult = paramType(valueAsByte);
-      return true;
-    }
-    return false;
-  }
-};
-
-} // namespace IPC
-
-#endif // mozilla_layout_generic_VisibilityIPC_h
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -91,17 +91,16 @@ EXPORTS += [
     'nsRubyTextFrame.h',
     'nsSplittableFrame.h',
     'nsSubDocumentFrame.h',
     'nsTextRunTransformations.h',
     'RubyUtils.h',
     'ScrollbarActivity.h',
     'ScrollSnap.h',
     'Visibility.h',
-    'VisibilityIPC.h',
 ]
 
 EXPORTS.mozilla += [
     'ReflowInput.h',
     'ReflowOutput.h',
     'WritingModes.h',
 ]
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -405,17 +405,17 @@ nsFrame::nsFrame(nsStyleContext* aContex
   mStyleContext->FrameAddRef();
 #endif
 }
 
 nsFrame::~nsFrame()
 {
   MOZ_COUNT_DTOR(nsFrame);
 
-  MOZ_ASSERT(!IsVisibleOrMayBecomeVisibleSoon(),
+  MOZ_ASSERT(GetVisibility() != Visibility::APPROXIMATELY_VISIBLE,
              "Visible nsFrame is being destroyed");
 
   NS_IF_RELEASE(mContent);
 #ifdef DEBUG
   mStyleContext->FrameRelease();
 #endif
   mStyleContext->Release();
 }
@@ -544,17 +544,17 @@ nsFrame::Init(nsIContent*       aContent
     mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
                        NS_FRAME_GENERATED_CONTENT |
                        NS_FRAME_IS_SVG_TEXT |
                        NS_FRAME_IN_POPUP |
                        NS_FRAME_IS_NONDISPLAY);
 
     if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
       // Assume all frames in popups are visible.
-      IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+      IncApproximateVisibleCount();
     }
   }
   const nsStyleDisplay *disp = StyleDisplay();
   if (disp->HasTransform(this) ||
       nsLayoutUtils::HasRelevantAnimationOfProperty(this,
                                                     eCSSProperty_transform)) {
     // The frame gets reconstructed if we toggle the -moz-transform
     // property, so we can set this bit here and then ignore it.
@@ -595,17 +595,17 @@ nsFrame::Init(nsIContent*       aContent
     }
     NS_ASSERTION(GetParent() ||
                  (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
                  "root frame should always be a container");
   }
 
   if (PresContext()->PresShell()->AssumeAllFramesVisible() &&
       TrackingVisibility()) {
-    IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+    IncApproximateVisibleCount();
   }
 
   DidSetStyleContext(nullptr);
 
   if (::IsXULBoxWrapped(this))
     ::InitBoxMetrics(this, false);
 }
 
@@ -713,18 +713,18 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
   // Disable visibility tracking. Note that we have to do this before calling
   // NotifyDestroyingFrame(), which will clear frame properties and make us lose
   // track of whether we were previously visible or not.
   // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
   // here, but it's unfortunately tricky to guarantee in the face of things like
   // frame reconstruction induced by style changes.
   DisableVisibilityTracking();
 
-  // Ensure that we're not in the visible list anymore.
-  PresContext()->GetPresShell()->MarkFrameNonvisible(this);
+  // Ensure that we're not in the approximately visible list anymore.
+  PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
 
   shell->NotifyDestroyingFrame(this);
 
   if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
     shell->ClearFrameRefs(this);
   }
 
   if (view) {
@@ -1462,55 +1462,45 @@ nsIFrame::GetCrossDocChildLists(nsTArray
         nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
         nsIFrame::kPrincipalList));
     }
   }
 
   GetChildLists(aLists);
 }
 
-static Visibility
-VisibilityStateAsVisibility(const nsIFrame::VisibilityState& aState)
-{
-  if (aState.mInDisplayPortCounter > 0) {
-    return Visibility::IN_DISPLAYPORT;  // Takes priority over MAY_BECOME_VISIBLE.
-  }
-  if (aState.mApproximateCounter > 0) {
-    return Visibility::MAY_BECOME_VISIBLE;
-  }
-  return Visibility::NONVISIBLE;
-}
-
 Visibility
 nsIFrame::GetVisibility() const
 {
   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
     return Visibility::UNTRACKED;
   }
 
   bool isSet = false;
   FrameProperties props = Properties();
-  VisibilityState state = props.Get(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
-  return VisibilityStateAsVisibility(state);
+  return visibleCount > 0
+       ? Visibility::APPROXIMATELY_VISIBLE
+       : Visibility::APPROXIMATELY_NONVISIBLE;
 }
 
 void
 nsIFrame::UpdateVisibilitySynchronously()
 {
   nsIPresShell* presShell = PresContext()->PresShell();
   if (!presShell) {
     return;
   }
 
   if (presShell->AssumeAllFramesVisible()) {
-    presShell->MarkFrameVisible(this, VisibilityCounter::IN_DISPLAYPORT);
+    presShell->EnsureFrameInApproximatelyVisibleList(this);
     return;
   }
 
   bool visible = true;
   nsIFrame* f = GetParent();
   nsRect rect = GetRectRelativeToSelf();
   nsIFrame* rectFrame = this;
   while (f) {
@@ -1541,19 +1531,19 @@ nsIFrame::UpdateVisibilitySynchronously(
       if (parent && parent->PresContext()->IsChrome()) {
         break;
       }
     }
     f = parent;
   }
 
   if (visible) {
-    presShell->MarkFrameVisible(this, VisibilityCounter::IN_DISPLAYPORT);
+    presShell->EnsureFrameInApproximatelyVisibleList(this);
   } else {
-    presShell->MarkFrameNonvisible(this);
+    presShell->RemoveFrameFromApproximatelyVisibleList(this);
   }
 }
 
 void
 nsIFrame::EnableVisibilityTracking()
 {
   if (GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED) {
     return;  // Nothing to do.
@@ -1562,17 +1552,17 @@ nsIFrame::EnableVisibilityTracking()
   FrameProperties props = Properties();
   MOZ_ASSERT(!props.Has(VisibilityStateProperty()),
              "Shouldn't have a VisibilityStateProperty value "
              "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
 
   // Add the state bit so we know to track visibility for this frame, and
   // initialize the frame property.
   AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
-  props.Set(VisibilityStateProperty(), VisibilityState{0, 0});
+  props.Set(VisibilityStateProperty(), 0);
 
   nsIPresShell* presShell = PresContext()->PresShell();
   if (!presShell) {
     return;
   }
 
   // Schedule a visibility update. This method will virtually always be called
   // when layout has changed anyway, so it's very unlikely that any additional
@@ -1585,111 +1575,85 @@ void
 nsIFrame::DisableVisibilityTracking()
 {
   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
     return;  // Nothing to do.
   }
 
   bool isSet = false;
   FrameProperties props = Properties();
-  VisibilityState state = props.Remove(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = props.Remove(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
   RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
 
-  Visibility previousVisibility = VisibilityStateAsVisibility(state);
-  if (previousVisibility == Visibility::NONVISIBLE) {
-    return;  // We were already nonvisible.
+  if (visibleCount == 0) {
+    return;  // We were nonvisible.
   }
 
   // We were visible, so send an OnVisibilityChange() notification.
-  OnVisibilityChange(previousVisibility, Visibility::NONVISIBLE);
+  OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE);
 }
 
 void
-nsIFrame::DecVisibilityCount(VisibilityCounter aCounter,
-                             Maybe<OnNonvisible> aNonvisibleAction /* = Nothing() */)
+nsIFrame::DecApproximateVisibleCount(Maybe<OnNonvisible> aNonvisibleAction
+                                       /* = Nothing() */)
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
 
   bool isSet = false;
   FrameProperties props = Properties();
-  VisibilityState state = props.Get(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
-  MOZ_ASSERT_IF(aCounter == VisibilityCounter::MAY_BECOME_VISIBLE,
-                state.mApproximateCounter > 0);
-  MOZ_ASSERT_IF(aCounter == VisibilityCounter::IN_DISPLAYPORT,
-                state.mInDisplayPortCounter > 0);
-
-  Visibility previousVisibility = VisibilityStateAsVisibility(state);
-
-  if (aCounter == VisibilityCounter::MAY_BECOME_VISIBLE) {
-    state.mApproximateCounter--;
-  } else {
-    state.mInDisplayPortCounter--;
-  }
-
-  props.Set(VisibilityStateProperty(), state);
-
-  Visibility newVisibility = VisibilityStateAsVisibility(state);
-  if (newVisibility == previousVisibility) {
-    return;  // Nothing changed.
-  }
-
-  // Our visibility just changed, so send an OnVisibilityChange() notification.
-  OnVisibilityChange(previousVisibility, newVisibility, aNonvisibleAction);
+  MOZ_ASSERT(visibleCount > 0, "Frame is already nonvisible and we're "
+                               "decrementing its visible count?");
+
+  visibleCount--;
+  props.Set(VisibilityStateProperty(), visibleCount);
+  if (visibleCount > 0) {
+    return;
+  }
+
+  // We just became nonvisible, so send an OnVisibilityChange() notification.
+  OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE, aNonvisibleAction);
 }
 
 void
-nsIFrame::IncVisibilityCount(VisibilityCounter aCounter)
+nsIFrame::IncApproximateVisibleCount()
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
 
   bool isSet = false;
   FrameProperties props = Properties();
-  VisibilityState state = props.Get(VisibilityStateProperty(), &isSet);
+  uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
 
   MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
                     "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
 
-  Visibility previousVisibility = VisibilityStateAsVisibility(state);
-
-  if (aCounter == VisibilityCounter::MAY_BECOME_VISIBLE) {
-    state.mApproximateCounter++;
-  } else {
-    state.mInDisplayPortCounter++;
-  }
-
-  props.Set(VisibilityStateProperty(), state);
-
-  Visibility newVisibility = VisibilityStateAsVisibility(state);
-  if (newVisibility == previousVisibility) {
-    return;  // Nothing changed.
-  }
-
-  // Our visibility just changed, so send an OnVisibilityChange() notification.
-  OnVisibilityChange(previousVisibility, newVisibility);
+  visibleCount++;
+  props.Set(VisibilityStateProperty(), visibleCount);
+  if (visibleCount > 1) {
+    return;
+  }
+
+  // We just became visible, so send an OnVisibilityChange() notification.
+  OnVisibilityChange(Visibility::APPROXIMATELY_VISIBLE);
 }
 
 void
-nsIFrame::OnVisibilityChange(Visibility aOldVisibility,
-                             Visibility aNewVisibility,
+nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
                              Maybe<OnNonvisible> aNonvisibleAction
                                /* = Nothing() */)
 {
   // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
   // images here.
-  MOZ_ASSERT(aOldVisibility != Visibility::UNTRACKED,
-             "Should've started at Visibility::NONVISIBLE");
-  MOZ_ASSERT(aNewVisibility != Visibility::UNTRACKED,
-             "Shouldn't notify for Visibility::UNTRACKED");
 }
 
 static nsIFrame*
 GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
 {
   nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
   if (capturingContent) {
     nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
@@ -2775,18 +2739,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
   if (IsThemed(ourDisp) &&
       !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
     return;
 
   // Since we're now sure that we're adding this frame to the display list
   // (which means we're painting it, modulo occlusion), mark it as visible
   // within the displayport.
   if (aBuilder->IsPaintingToWindow() && child->TrackingVisibility()) {
-    nsIPresShell* shell = child->PresContext()->PresShell();
-    shell->MarkFrameVisible(child, VisibilityCounter::IN_DISPLAYPORT);
+    child->PresContext()->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
   }
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects or a blend mode..
   const nsStyleDisplay* disp = child->StyleDisplay();
   const nsStyleEffects* effects = child->StyleEffects();
   const nsStylePosition* pos = child->StylePosition();
   bool isVisuallyAtomic = child->HasOpacity()
@@ -9310,17 +9273,17 @@ nsFrame::BoxMetrics() const
 }
 
 /* static */ void
 nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame)
 {
   if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
       aFrame->TrackingVisibility()) {
     // Assume all frames in popups are visible.
-    aFrame->IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+    aFrame->IncApproximateVisibleCount();
   }
 
   aFrame->AddStateBits(NS_FRAME_IN_POPUP);
 
   AutoTArray<nsIFrame::ChildList,4> childListArray;
   aFrame->GetCrossDocChildLists(&childListArray);
 
   nsIFrame::ChildListArrayIterator lists(childListArray);
@@ -9340,17 +9303,17 @@ nsIFrame::RemoveInPopupStateBitFromDesce
     return;
   }
 
   aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
 
   if (aFrame->TrackingVisibility()) {
     // We assume all frames in popups are visible, so this decrement balances
     // out the increment in AddInPopupStateBitToDescendants above.
-    aFrame->DecVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+    aFrame->DecApproximateVisibleCount();
   }
 
   AutoTArray<nsIFrame::ChildList,4> childListArray;
   aFrame->GetCrossDocChildLists(&childListArray);
 
   nsIFrame::ChildListArrayIterator lists(childListArray);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -423,17 +423,16 @@ public:
   template <typename T> using Maybe = mozilla::Maybe<T>;
   using Nothing = mozilla::Nothing;
   using OnNonvisible = mozilla::OnNonvisible;
   template<typename T=void>
   using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
   using ReflowInput = mozilla::ReflowInput;
   using ReflowOutput = mozilla::ReflowOutput;
   using Visibility = mozilla::Visibility;
-  using VisibilityCounter = mozilla::VisibilityCounter;
 
   typedef mozilla::FrameProperties FrameProperties;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layout::FrameChildList ChildList;
   typedef mozilla::layout::FrameChildListID ChildListID;
   typedef mozilla::layout::FrameChildListIDs ChildListIDs;
   typedef mozilla::layout::FrameChildListIterator ChildListIterator;
   typedef mozilla::layout::FrameChildListArrayIterator ChildListArrayIterator;
@@ -1110,44 +1109,29 @@ public:
   {
     return bool(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
   }
 
   /// @return the visibility state of this frame. See the Visibility enum
   /// for the possible return values and their meanings.
   Visibility GetVisibility() const;
 
-  /// @return true if this frame is either in the displayport now or may
-  /// become visible soon.
-  bool IsVisibleOrMayBecomeVisibleSoon() const
-  {
-    Visibility visibility = GetVisibility();
-    return visibility == Visibility::MAY_BECOME_VISIBLE ||
-           visibility == Visibility::IN_DISPLAYPORT;
-  }
-
   /// Update the visibility state of this frame synchronously.
   /// XXX(seth): Avoid using this method; we should be relying on the refresh
   /// driver for visibility updates. This method, which replaces
   /// nsLayoutUtils::UpdateApproximateFrameVisibility(), exists purely as a
   /// temporary measure to avoid changing behavior during the transition from
   /// the old image visibility code.
   void UpdateVisibilitySynchronously();
 
-  struct VisibilityState
-  {
-    unsigned int mApproximateCounter : 16;
-    unsigned int mInDisplayPortCounter : 16;
-  };
-
-  // A frame property which stores the visibility state of this frame, which
-  // consists of a VisibilityState value that stores counters for each type of
-  // visibility we track. When the visibility of this frame is not being
-  // tracked, this property is absent.
-  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(VisibilityStateProperty, VisibilityState);
+  // A frame property which stores the visibility state of this frame. Right
+  // now that consists of an approximate visibility counter represented as a
+  // uint32_t. When the visibility of this frame is not being tracked, this
+  // property is absent.
+  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(VisibilityStateProperty, uint32_t);
 
 protected:
 
   /**
    * Subclasses can call this method to enable visibility tracking for this frame.
    *
    * If visibility tracking was previously disabled, this will schedule an
    * update an asynchronous update of visibility.
@@ -1161,62 +1145,52 @@ protected:
    * tracking will cause a synchronous call to OnVisibilityChange().
    */
   void DisableVisibilityTracking();
 
   /**
    * Called when a frame transitions between visibility states (for example,
    * from nonvisible to visible, or from visible to nonvisible).
    *
-   * @param aOldVisibility    The previous visibility state.
    * @param aNewVisibility    The new visibility state.
    * @param aNonvisibleAction A requested action if the frame has become
    *                          nonvisible. If Nothing(), no action is
    *                          requested. If DISCARD_IMAGES is specified, the
    *                          frame is requested to ask any images it's
    *                          associated with to discard their surfaces if
    *                          possible.
    *
    * Subclasses which override this method should call their parent class's
    * implementation.
    */
-  virtual void OnVisibilityChange(Visibility aOldVisibility,
-                                  Visibility aNewVisibility,
+  virtual void OnVisibilityChange(Visibility aNewVisibility,
                                   Maybe<OnNonvisible> aNonvisibleAction = Nothing());
 
 public:
 
   ///////////////////////////////////////////////////////////////////////////////
   // Internal implementation for the approximate frame visibility API.
   ///////////////////////////////////////////////////////////////////////////////
 
   /**
-   * We track the visibility of frames using counters; if any of the counters
-   * are non-zero, then the frame is considered visible. Using counters allows
-   * us to account for situations where the frame may be visible in more than
-   * one place (for example, via -moz-element), and it simplifies the
+   * We track the approximate visibility of frames using a counter; if it's
+   * non-zero, then the frame is considered visible. Using a counter allows us
+   * to account for situations where the frame may be visible in more than one
+   * place (for example, via -moz-element), and it simplifies the
    * implementation of our approximate visibility tracking algorithms.
    *
-   * There are two visibility counters for each frame: the approximate counter
-   * (which is based on our heuristics for which frames may become visible
-   * "soon"), and the in-displayport counter (which records if the frame was
-   * within the displayport at the last paint).
-   *
-   *
-   * @param aCounter          Which counter to increment or decrement.
    * @param aNonvisibleAction A requested action if the frame has become
    *                          nonvisible. If Nothing(), no action is
    *                          requested. If DISCARD_IMAGES is specified, the
    *                          frame is requested to ask any images it's
    *                          associated with to discard their surfaces if
    *                          possible.
    */
-  void DecVisibilityCount(VisibilityCounter aCounter,
-                          Maybe<OnNonvisible> aNonvisibleAction = Nothing());
-  void IncVisibilityCount(VisibilityCounter aCounter);
+  void DecApproximateVisibleCount(Maybe<OnNonvisible> aNonvisibleAction = Nothing());
+  void IncApproximateVisibleCount();
 
 
   /**
    * Get the specified child list.
    *
    * @param   aListID identifies the requested child list.
    * @return  the child list.  If the requested list is unsupported by this
    *          frame type, an empty list will be returned.
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -713,17 +713,17 @@ nsImageFrame::MaybeDecodeForPredictedSiz
   if (!mImage) {
     return;  // Nothing to do yet.
   }
 
   if (mComputedSize.IsEmpty()) {
     return;  // We won't draw anything, so no point in decoding.
   }
 
-  if (!IsVisibleOrMayBecomeVisibleSoon()) {
+  if (GetVisibility() != Visibility::APPROXIMATELY_VISIBLE) {
     return;  // We're not visible, so don't decode.
   }
 
   // OK, we're ready to decode. Compute the scale to the screen...
   nsIPresShell* presShell = PresContext()->GetPresShell();
   LayoutDeviceToScreenScale2D resolutionToScreen(
       presShell->GetCumulativeResolution()
     * nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(this));
@@ -2088,39 +2088,33 @@ nsImageFrame::AttributeChanged(int32_t a
                                                  nsIPresShell::eStyleChange,
                                                  NS_FRAME_IS_DIRTY);
   }
 
   return NS_OK;
 }
 
 void
-nsImageFrame::OnVisibilityChange(Visibility aOldVisibility,
-                                 Visibility aNewVisibility,
+nsImageFrame::OnVisibilityChange(Visibility aNewVisibility,
                                  Maybe<OnNonvisible> aNonvisibleAction)
 {
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   if (!imageLoader) {
     MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
-    nsAtomicContainerFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                               aNonvisibleAction);
+    nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
     return;
   }
 
-  imageLoader->OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                  aNonvisibleAction);
+  imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 
-  if (aOldVisibility == Visibility::NONVISIBLE &&
-        (aNewVisibility == Visibility::MAY_BECOME_VISIBLE ||
-         aNewVisibility == Visibility::IN_DISPLAYPORT)) {
+  if (aNewVisibility == Visibility::APPROXIMATELY_VISIBLE) {
     MaybeDecodeForPredictedSize();
   }
 
-  nsAtomicContainerFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                             aNonvisibleAction);
+  nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 }
 
 nsIAtom*
 nsImageFrame::GetType() const
 {
   return nsGkAtoms::imageFrame;
 }
 
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -99,18 +99,17 @@ public:
                                mozilla::WidgetGUIEvent* aEvent,
                                nsEventStatus* aEventStatus) override;
   virtual nsresult GetCursor(const nsPoint& aPoint,
                              nsIFrame::Cursor& aCursor) override;
   virtual nsresult AttributeChanged(int32_t aNameSpaceID,
                                     nsIAtom* aAttribute,
                                     int32_t aModType) override;
 
-  void OnVisibilityChange(Visibility aOldVisibility,
-                          Visibility aNewVisibility,
+  void OnVisibilityChange(Visibility aNewVisibility,
                           Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   virtual nsIAtom* GetType() const override;
 
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -662,33 +662,31 @@ nsVideoFrame::AttributeChanged(int32_t a
     UpdatePosterSource(true);
   }
   return nsContainerFrame::AttributeChanged(aNameSpaceID,
                                             aAttribute,
                                             aModType);
 }
 
 void
-nsVideoFrame::OnVisibilityChange(Visibility aOldVisibility,
-                                 Visibility aNewVisibility,
+nsVideoFrame::OnVisibilityChange(Visibility aNewVisibility,
                                  Maybe<OnNonvisible> aNonvisibleAction)
 {
   if (HasVideoElement()) {
     nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
-    mediaDomElement->OnVisibilityChange(aOldVisibility, aNewVisibility);
+    mediaDomElement->OnVisibilityChange(aNewVisibility);
   }
 
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mPosterImage);
   if (imageLoader) {
-    imageLoader->OnVisibilityChange(aOldVisibility, aNewVisibility,
+    imageLoader->OnVisibilityChange(aNewVisibility,
                                     aNonvisibleAction);
   }
 
-  nsContainerFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                       aNonvisibleAction);
+  nsContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 }
 
 bool nsVideoFrame::HasVideoElement() {
   nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
   return mediaDomElement->IsVideo();
 }
 
 bool nsVideoFrame::HasVideoData()
--- a/layout/generic/nsVideoFrame.h
+++ b/layout/generic/nsVideoFrame.h
@@ -47,18 +47,17 @@ public:
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   virtual nsresult AttributeChanged(int32_t aNameSpaceID,
                                     nsIAtom* aAttribute,
                                     int32_t aModType) override;
 
-  void OnVisibilityChange(Visibility aOldVisibility,
-                          Visibility aNewVisibility,
+  void OnVisibilityChange(Visibility aNewVisibility,
                           Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
 
   /* get the size of the video's display */
   nsSize GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext);
   virtual nsSize GetIntrinsicRatio() override;
   virtual mozilla::LogicalSize
   ComputeSize(nsRenderingContext *aRenderingContext,
               mozilla::WritingMode aWritingMode,
--- a/layout/svg/SVGFEImageFrame.cpp
+++ b/layout/svg/SVGFEImageFrame.cpp
@@ -59,18 +59,17 @@ public:
    * @see nsGkAtoms::svgFEImageFrame
    */
   virtual nsIAtom* GetType() const override;
 
   virtual nsresult AttributeChanged(int32_t  aNameSpaceID,
                                     nsIAtom* aAttribute,
                                     int32_t  aModType) override;
 
-  void OnVisibilityChange(Visibility aOldVisibility,
-                          Visibility aNewVisibility,
+  void OnVisibilityChange(Visibility aNewVisibility,
                           Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
     // We don't maintain a visual overflow rect
     return false;
   }
 };
 
@@ -80,17 +79,17 @@ NS_NewSVGFEImageFrame(nsIPresShell* aPre
   return new (aPresShell) SVGFEImageFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(SVGFEImageFrame)
 
 /* virtual */ void
 SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
-  DecVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+  DecApproximateVisibleCount();
 
   nsCOMPtr<nsIImageLoadingContent> imageLoader =
     do_QueryInterface(nsFrame::mContent);
   if (imageLoader) {
     imageLoader->FrameDestroyed(this);
   }
 
   nsFrame::DestroyFrom(aDestructRoot);
@@ -103,17 +102,17 @@ SVGFEImageFrame::Init(nsIContent*       
 {
   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::feImage),
                "Trying to construct an SVGFEImageFrame for a "
                "content element that doesn't support the right interfaces");
 
   nsFrame::Init(aContent, aParent, aPrevInFlow);
 
   // We assume that feImage's are always visible.
-  IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+  IncApproximateVisibleCount();
 
   nsCOMPtr<nsIImageLoadingContent> imageLoader =
     do_QueryInterface(nsFrame::mContent);
   if (imageLoader) {
     imageLoader->FrameCreated(this);
   }
 }
 
@@ -147,27 +146,23 @@ SVGFEImageFrame::AttributeChanged(int32_
       element->CancelImageRequests(true);
     }
   }
 
   return nsFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
 
 void
-SVGFEImageFrame::OnVisibilityChange(Visibility aOldVisibility,
-                                    Visibility aNewVisibility,
+SVGFEImageFrame::OnVisibilityChange(Visibility aNewVisibility,
                                     Maybe<OnNonvisible> aNonvisibleAction)
 {
   nsCOMPtr<nsIImageLoadingContent> imageLoader =
     do_QueryInterface(nsFrame::mContent);
   if (!imageLoader) {
     MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
-    nsFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                aNonvisibleAction);
+    nsFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
     return;
   }
 
-  imageLoader->OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                  aNonvisibleAction);
+  imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 
-  nsFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                              aNonvisibleAction);
+  nsFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 }
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -75,18 +75,17 @@ public:
   // nsSVGPathGeometryFrame methods:
   virtual uint16_t GetHitTestFlags() override;
 
   // nsIFrame interface:
   virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType) override;
 
-  void OnVisibilityChange(Visibility aOldVisibility,
-                          Visibility aNewVisibility,
+  void OnVisibilityChange(Visibility aNewVisibility,
                           Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
 
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) override;
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
   /**
@@ -155,17 +154,17 @@ nsSVGImageFrame::Init(nsIContent*       
   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::image),
                "Content is not an SVG image!");
 
   nsSVGPathGeometryFrame::Init(aContent, aParent, aPrevInFlow);
 
   if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
     // Non-display frames are likely to be patterns, masks or the like.
     // Treat them as always visible.
-    IncVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+    IncApproximateVisibleCount();
   }
 
   mListener = new nsSVGImageListener(this);
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   if (!imageLoader) {
     NS_RUNTIMEABORT("Why is this not an image loading content?");
   }
 
@@ -175,17 +174,17 @@ nsSVGImageFrame::Init(nsIContent*       
 
   imageLoader->AddObserver(mListener);
 }
 
 /* virtual */ void
 nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
-    DecVisibilityCount(VisibilityCounter::IN_DISPLAYPORT);
+    DecApproximateVisibleCount();
   }
 
   if (mReflowCallbackPosted) {
     PresContext()->PresShell()->CancelReflowCallback(this);
     mReflowCallbackPosted = false;
   }
 
   nsCOMPtr<nsIImageLoadingContent> imageLoader =
@@ -241,32 +240,28 @@ nsSVGImageFrame::AttributeChanged(int32_
     }
   }
 
   return nsSVGPathGeometryFrame::AttributeChanged(aNameSpaceID,
                                                   aAttribute, aModType);
 }
 
 void
-nsSVGImageFrame::OnVisibilityChange(Visibility aOldVisibility,
-                                    Visibility aNewVisibility,
+nsSVGImageFrame::OnVisibilityChange(Visibility aNewVisibility,
                                     Maybe<OnNonvisible> aNonvisibleAction)
 {
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   if (!imageLoader) {
-    nsSVGPathGeometryFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                               aNonvisibleAction);
+    nsSVGPathGeometryFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
     return;
   }
 
-  imageLoader->OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                  aNonvisibleAction);
+  imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 
-  nsSVGPathGeometryFrame::OnVisibilityChange(aOldVisibility, aNewVisibility,
-                                             aNonvisibleAction);
+  nsSVGPathGeometryFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
 }
 
 gfx::Matrix
 nsSVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth,
                                          int32_t aNativeHeight)
 {
   float x, y, width, height;
   SVGImageElement *element = static_cast<SVGImageElement*>(mContent);