merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 12 Sep 2016 13:07:54 +0200
changeset 357002 1851b78b5a9673ee422f189b92e5f1e86b82a01c
parent 356984 3d667f260feb3f7f30fca84f608b92e330aa349c (current diff)
parent 357001 f56deec592bd3cff3e7f9eacb4786a3e50626fca (diff)
child 357004 a18a3e221752e57641ed74c438fa20dc7f50cd1e
child 357025 e8b53c6a566ec9f6d9abb194498044a6003c2b86
child 357031 5d8afbdb3620c7da25955d0c567cc4a4f890adf5
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);