Bug 1259281 - Mark frames NONVISIBLE if their pres shell is inactive or frozen. r=mstange
authorSeth Fowler <mark.seth.fowler@gmail.com>
Wed, 11 May 2016 19:27:29 -0700
changeset 297060 719d6d5d9d2114dec8bff62d646435ab42cf5491
parent 297059 d0aa5cf74699aa4bacbde93e45965a58bb7f8081
child 297061 7043159e6b741f094e2f81b6b5354a1bc70b3e01
push id76583
push usermfowler@mozilla.com
push dateThu, 12 May 2016 02:42:59 +0000
treeherdermozilla-inbound@719d6d5d9d21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1259281
milestone49.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
Bug 1259281 - Mark frames NONVISIBLE if their pres shell is inactive or frozen. r=mstange
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1238,17 +1238,17 @@ public:
   {
     mObservesMutationsForPrint = aObserve;
   }
   bool ObservesNativeAnonMutationsForPrint()
   {
     return mObservesMutationsForPrint;
   }
 
-  virtual nsresult SetIsActive(bool aIsActive, bool aIsHidden = true) = 0;
+  virtual void SetIsActive(bool aIsActive, bool aIsHidden = true) = 0;
 
   bool IsActive()
   {
     return mIsActive;
   }
 
   // mouse capturing
   static CapturingContentInfo gCaptureInfo;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4635,16 +4635,129 @@ PresShell::NotifyCompositorOfVisibleRegi
                           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();
@@ -5731,43 +5844,22 @@ PresShell::MarkFramesInListApproximately
     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->mApproximatelyVisibleFrames.PutEntry(frame);
-    if (presShell->mApproximatelyVisibleFrames.Count() > count) {
-      // The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
-      frame->IncVisibilityCount(VisibilityCounter::MAY_BECOME_VISIBLE);
-    }
-
+    presShell->mVisibleFrames.AddFrame(frame, VisibilityCounter::MAY_BECOME_VISIBLE);
     presShell->AddFrameToVisibleRegions(frame, VisibilityCounter::MAY_BECOME_VISIBLE);
   }
 }
 
-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);
-    }
-  }
-}
-
 /**
  * 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
@@ -5797,30 +5889,40 @@ struct MOZ_STACK_CLASS AutoUpdateVisibil
   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->mApproximatelyVisibleFrames.SwapElements(*mOldApproximatelyVisibleFrames);
+          mPresShell->mVisibleFrames.mApproximate.SwapElements(*mOldApproximatelyVisibleFrames);
           break;
 
         case VisibilityCounter::IN_DISPLAYPORT:
           mOldInDisplayPortFrames.emplace();
-          mPresShell->mInDisplayPortFrames.SwapElements(*mOldInDisplayPortFrames);
+          mPresShell->mVisibleFrames.mInDisplayPort.SwapElements(*mOldInDisplayPortFrames);
           break;
       }
     }
 
     // If we're not visualizing visible regions, we're done.
     if (!gfxPrefs::APZMinimap() ||
         !gfxPrefs::APZMinimapVisibilityEnabled()) {
       mPresShell->mVisibleRegions = nullptr;
@@ -5943,23 +6045,17 @@ PresShell::MarkFramesInSubtreeApproximat
                                                    bool aRemoveOnly /* = false */)
 {
   MOZ_ASSERT(aFrame->PresContext()->PresShell() == this, "wrong presshell");
 
   if (aFrame->TrackingVisibility() &&
       aFrame->StyleVisibility()->IsVisible() &&
       (!aRemoveOnly || aFrame->IsVisibleOrMayBecomeVisibleSoon())) {
     MOZ_ASSERT(!AssumeAllFramesVisible());
-    uint32_t count = mApproximatelyVisibleFrames.Count();
-    mApproximatelyVisibleFrames.PutEntry(aFrame);
-    if (mApproximatelyVisibleFrames.Count() > count) {
-      // The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
-      aFrame->IncVisibilityCount(VisibilityCounter::MAY_BECOME_VISIBLE);
-    }
-
+    mVisibleFrames.AddFrame(aFrame, VisibilityCounter::MAY_BECOME_VISIBLE);
     AddFrameToVisibleRegions(aFrame, VisibilityCounter::MAY_BECOME_VISIBLE);
   }
 
   nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
   if (subdocFrame) {
     nsIPresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
       nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
     if (presShell && !presShell->AssumeAllFramesVisible()) {
@@ -6076,20 +6172,20 @@ PresShell::DoUpdateApproximateFrameVisib
     return;
   }
 
   RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
   ClearVisibleFramesForUnvisitedPresShells(rootFrame->GetView(), true);
 
 #ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
   // This can be used to debug the frame walker by comparing beforeFrameList
-  // 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).
+  // 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).
   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);
@@ -6233,65 +6329,42 @@ PresShell::MarkFrameVisible(nsIFrame* aF
   // 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
 
-  VisibleFrames& frameSet = VisibleFramesForCounter(aCounter);
-
-  if (!frameSet.Contains(aFrame)) {
-    MOZ_ASSERT(!AssumeAllFramesVisible());
-    frameSet.PutEntry(aFrame);
-    aFrame->IncVisibilityCount(aCounter);
-  }
-
+  mVisibleFrames.AddFrame(aFrame, aCounter);
   AddFrameToVisibleRegions(aFrame, aCounter);
 }
 
-static void
-RemoveFrameFromVisibleSet(nsIFrame* aFrame,
-                          VisibleFrames& aSet,
-                          VisibilityCounter aCounter)
-{
-  uint32_t count = aSet.Count();
-  aSet.RemoveEntry(aFrame);
-
-  if (aFrame->TrackingVisibility() && aSet.Count() < count) {
-    aFrame->DecVisibilityCount(aCounter);
-  }
-
-}
-
 void
 PresShell::MarkFrameNonvisible(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(mApproximatelyVisibleFrames.Count() == 0,
+    MOZ_ASSERT(mVisibleFrames.mApproximate.Count() == 0,
                "Shouldn't have any frames in the approximate visibility set");
-    MOZ_ASSERT(mInDisplayPortFrames.Count() == 0,
+    MOZ_ASSERT(mVisibleFrames.mInDisplayPort.Count() == 0,
                "Shouldn't have any frames in the in-displayport visibility set");
     return;
   }
 
-  RemoveFrameFromVisibleSet(aFrame, mApproximatelyVisibleFrames,
-                            VisibilityCounter::MAY_BECOME_VISIBLE);
-  RemoveFrameFromVisibleSet(aFrame, mInDisplayPortFrames,
-                            VisibilityCounter::IN_DISPLAYPORT);
+  mVisibleFrames.RemoveFrame(aFrame, VisibilityCounter::MAY_BECOME_VISIBLE);
+  mVisibleFrames.RemoveFrame(aFrame, VisibilityCounter::IN_DISPLAYPORT);
 }
 
 class nsAutoNotifyDidPaint
 {
 public:
   nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags)
     : mShell(aShell), mFlags(aFlags)
   {
@@ -9135,19 +9208,18 @@ PresShell::Freeze()
 
   nsPresContext* presContext = GetPresContext();
   if (presContext &&
       presContext->RefreshDriver()->PresContext() == presContext) {
     presContext->RefreshDriver()->Freeze();
   }
 
   mFrozen = true;
-  if (mDocument) {
-    UpdateImageLockingState();
-  }
+
+  UpdateFrameVisibilityOnActiveStateChange();
 }
 
 void
 PresShell::FireOrClearDelayedEvents(bool aFireEvents)
 {
   mNoDelayedMouseEvents = false;
   mNoDelayedKeyEvents = false;
   if (!aFireEvents) {
@@ -9203,18 +9275,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;
-  UpdateImageLockingState();
-
+
+  UpdateFrameVisibilityOnActiveStateChange();
   UnsuppressPainting();
 }
 
 //--------------------------------------------------------
 // Start of protected and private methods on the PresShell
 //--------------------------------------------------------
 
 void
@@ -11049,17 +11121,17 @@ SetPluginIsActive(nsISupports* aSupports
 
   nsIFrame *frame = content->GetPrimaryFrame();
   nsIObjectFrame *objectFrame = do_QueryFrame(frame);
   if (objectFrame) {
     objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
   }
 }
 
-nsresult
+void
 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.
@@ -11071,17 +11143,18 @@ 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);
-  nsresult rv = UpdateImageLockingState();
+  UpdateFrameVisibilityOnActiveStateChange();
+
 #ifdef ACCESSIBILITY
   if (aIsActive) {
     nsAccessibilityService* accService = AccService();
     if (accService) {
       accService->PresShellActivated(this);
     }
   }
 #endif
@@ -11119,43 +11192,47 @@ PresShell::SetIsActive(bool aIsActive, b
             root->SchedulePaint();
           }
         }
       } else {
         tab->MakeHidden();
       }
     }
   }
-  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;
+}
+
+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();
+  }
 }
 
 PresShell*
 PresShell::GetRootPresShell()
 {
   if (mPresContext) {
     nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
     if (rootPresContext) {
@@ -11173,18 +11250,18 @@ PresShell::AddSizeOfIncludingThis(Malloc
                                   size_t *aTextRunsSize,
                                   size_t *aPresContextSize)
 {
   mFrameArena.AddSizeOfExcludingThis(aMallocSizeOf, aArenaObjectsSize);
   *aPresShellSize += aMallocSizeOf(this);
   if (mCaret) {
     *aPresShellSize += mCaret->SizeOfIncludingThis(aMallocSizeOf);
   }
-  *aPresShellSize += mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
-  *aPresShellSize += mInDisplayPortFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  *aPresShellSize += mVisibleFrames.mApproximate.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  *aPresShellSize += mVisibleFrames.mInDisplayPort.ShallowSizeOfExcludingThis(aMallocSizeOf);
   *aPresShellSize += mVisibleRegions
                    ? mVisibleRegions->mApproximate.ShallowSizeOfIncludingThis(aMallocSizeOf) +
                      mVisibleRegions->mInDisplayPort.ShallowSizeOfIncludingThis(aMallocSizeOf)
                    : 0;
   *aPresShellSize += mFramesToDirty.ShallowSizeOfExcludingThis(aMallocSizeOf);
   *aPresShellSize += aArenaObjectsSize->mOther;
 
   if (nsStyleSet* styleSet = StyleSet()->GetAsGecko()) {
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -355,17 +355,17 @@ public:
 
   virtual void AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
                                              nsDisplayList& aList,
                                              nsIFrame* aFrame,
                                              const nsRect& aBounds) override;
 
   virtual nscolor ComputeBackstopColor(nsView* aDisplayRoot) override;
 
-  virtual nsresult SetIsActive(bool aIsActive, bool aIsHidden = true) override;
+  virtual void SetIsActive(bool aIsActive, bool aIsHidden = true) override;
 
   virtual bool GetIsViewportOverridden() override {
     return (mMobileViewportManager != nullptr);
   }
 
   virtual bool IsLayoutFlushObserver() override
   {
     return GetPresContext()->RefreshDriver()->
@@ -672,17 +672,16 @@ 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);
@@ -771,49 +770,68 @@ protected:
   void DoUpdateApproximateFrameVisibility(bool aRemoveOnly);
 
   void ClearVisibleFramesSets(Maybe<OnNonvisible> aNonvisibleAction = Nothing());
   static void ClearVisibleFramesForUnvisitedPresShells(nsView* aView, bool aClear);
   static void MarkFramesInListApproximatelyVisible(const nsDisplayList& aList);
   void MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
                                                const nsRect& aRect,
                                                bool aRemoveOnly = false);
+  void UpdateFrameVisibilityOnActiveStateChange();
 
   void InitVisibleRegionsIfVisualizationEnabled(VisibilityCounter aForCounter);
   void AddFrameToVisibleRegions(nsIFrame* aFrame, VisibilityCounter aForCounter);
   void NotifyCompositorOfVisibleRegionsChange();
 
   nsRevocableEventPtr<nsRunnableMethod<PresShell>> mUpdateApproximateFrameVisibilityEvent;
   nsRevocableEventPtr<nsRunnableMethod<PresShell>> mNotifyCompositorOfVisibleRegionsChangeEvent;
 
-  VisibleFrames& VisibleFramesForCounter(VisibilityCounter aCounter)
+  struct VisibleFramesContainer
   {
-    switch (aCounter)
+    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)
     {
-      case VisibilityCounter::MAY_BECOME_VISIBLE: return mApproximatelyVisibleFrames;
-      case VisibilityCounter::IN_DISPLAYPORT:     return mInDisplayPortFrames;
+      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 or could be visible soon at the time
-  // that we last did an approximate frame visibility update.
-  VisibleFrames mApproximatelyVisibleFrames;
+    // A set of frames that were visible in the displayport the last time we painted.
+    VisibleFrames mInDisplayPort;
 
-  // A set of frames that were visible in the displayport the last time we painted.
-  VisibleFrames mInDisplayPortFrames;
+    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;