Bug 1171371 - On memory-pressure, remove any stale images from the visible images list. r=tn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Thu, 04 Jun 2015 11:08:19 -0700
changeset 322017 9bf8c3d6ec19585b441345ae42f3e3c83f517a9e
parent 322016 e46b452357990be9b9838e13086e277bedda077b
child 322018 36f8f2d29af6d5c96b18d28613313a283767a48a
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1171371
milestone47.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 1171371 - On memory-pressure, remove any stale images from the visible images list. r=tn
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1565,17 +1565,18 @@ public:
   void CancelInvalidatePresShellIfHidden();
 
   // Schedule an update of the list of visible images.
   virtual void ScheduleImageVisibilityUpdate() = 0;
 
   // Clears the current list of visible images on this presshell and replaces it
   // with images that are in the display list aList.
   virtual void RebuildImageVisibilityDisplayList(const nsDisplayList& aList) = 0;
-  virtual void RebuildImageVisibility(nsRect* aRect = nullptr) = 0;
+  virtual void RebuildImageVisibility(nsRect* aRect = nullptr,
+                                      bool aRemoveOnly = false) = 0;
 
   // Ensures the image is in the list of visible images.
   virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) = 0;
 
   // Removes the image from the list of visible images if it is present there.
   virtual void RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) = 0;
 
   // Whether we should assume all images are visible.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -915,16 +915,17 @@ PresShell::Init(nsIDocument* aDocument,
       os->AddObserver(this, "user-sheet-added", false);
       os->AddObserver(this, "author-sheet-added", false);
       os->AddObserver(this, "agent-sheet-removed", false);
       os->AddObserver(this, "user-sheet-removed", false);
       os->AddObserver(this, "author-sheet-removed", false);
 #ifdef MOZ_XUL
       os->AddObserver(this, "chrome-flush-skin-caches", false);
 #endif
+      os->AddObserver(this, "memory-pressure", false);
     }
   }
 
 #ifdef MOZ_REFLOW_PERF
     if (mReflowCountMgr) {
       bool paintFrameCounts =
         Preferences::GetBool("layout.reflow.showframecounts");
 
@@ -1139,16 +1140,17 @@ PresShell::Destroy()
       os->RemoveObserver(this, "user-sheet-added");
       os->RemoveObserver(this, "author-sheet-added");
       os->RemoveObserver(this, "agent-sheet-removed");
       os->RemoveObserver(this, "user-sheet-removed");
       os->RemoveObserver(this, "author-sheet-removed");
 #ifdef MOZ_XUL
       os->RemoveObserver(this, "chrome-flush-skin-caches");
 #endif
+      os->RemoveObserver(this, "memory-pressure");
     }
   }
 
   // If our paint suppression timer is still active, kill it.
   if (mPaintSuppressionTimer) {
     mPaintSuppressionTimer->Cancel();
     mPaintSuppressionTimer = nullptr;
   }
@@ -5582,22 +5584,25 @@ PresShell::ClearImageVisibilityVisited(n
 void
 PresShell::ClearVisibleImagesList(uint32_t aNonvisibleAction)
 {
   DecrementVisibleCount(mVisibleImages, aNonvisibleAction);
   mVisibleImages.Clear();
 }
 
 void
-PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect)
+PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
+                                      const nsRect& aRect,
+                                      bool aRemoveOnly /* = false */)
 {
   MOZ_ASSERT(aFrame->PresContext()->PresShell() == this, "wrong presshell");
 
   nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(aFrame->GetContent()));
-  if (content && aFrame->StyleVisibility()->IsVisible()) {
+  if (content && aFrame->StyleVisibility()->IsVisible() &&
+      (!aRemoveOnly || content->GetVisibleCount() > 0)) {
     uint32_t count = mVisibleImages.Count();
     mVisibleImages.PutEntry(content);
     if (mVisibleImages.Count() > count) {
       // content was added to mVisibleImages, so we need to increment its visible count
       content->IncrementVisibleCount();
     }
   }
 
@@ -5668,17 +5673,18 @@ PresShell::MarkImagesInSubtreeVisible(ns
         }
       }
       MarkImagesInSubtreeVisible(child, r);
     }
   }
 }
 
 void
-PresShell::RebuildImageVisibility(nsRect* aRect)
+PresShell::RebuildImageVisibility(nsRect* aRect,
+                                  bool aRemoveOnly /* = false */)
 {
   MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
   mImageVisibilityVisited = true;
 
   nsIFrame* rootFrame = GetRootFrame();
   if (!rootFrame) {
     return;
   }
@@ -5687,25 +5693,31 @@ PresShell::RebuildImageVisibility(nsRect
   // oldVisibleImages.
   nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > oldVisibleImages;
   mVisibleImages.SwapElements(oldVisibleImages);
 
   nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
   if (aRect) {
     vis = *aRect;
   }
-  MarkImagesInSubtreeVisible(rootFrame, vis);
+  MarkImagesInSubtreeVisible(rootFrame, vis, aRemoveOnly);
 
   DecrementVisibleCount(oldVisibleImages,
                         nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION);
 }
 
 void
 PresShell::UpdateImageVisibility()
 {
+  DoUpdateImageVisibility(/* aRemoveOnly = */ false);
+}
+
+void
+PresShell::DoUpdateImageVisibility(bool aRemoveOnly)
+{
   MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(),
     "updating image visibility on a non-root content document?");
 
   mUpdateImageVisibilityEvent.Revoke();
 
   if (mHaveShutDown || mIsDestroying) {
     return;
   }
@@ -5713,17 +5725,17 @@ PresShell::UpdateImageVisibility()
   // call update on that frame
   nsIFrame* rootFrame = GetRootFrame();
   if (!rootFrame) {
     ClearVisibleImagesList(
       nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD);
     return;
   }
 
-  RebuildImageVisibility();
+  RebuildImageVisibility(/* aRect = */ nullptr, aRemoveOnly);
   ClearImageVisibilityVisited(rootFrame->GetView(), true);
 
 #ifdef DEBUG_IMAGE_VISIBILITY_DISPLAY_LIST
   // This can be used to debug the frame walker by comparing beforeImageList and
   // mVisibleImages in RebuildImageVisibilityDisplayList to see if they produce
   // the same results (mVisibleImages holds the images the display list thinks
   // are visible, beforeImageList holds the images the frame walker thinks are
   // visible).
@@ -9321,16 +9333,23 @@ PresShell::Observe(nsISupports* aSubject
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, "author-sheet-removed") && mStyleSet) {
     RemoveSheet(SheetType::Doc, aSubject);
     return NS_OK;
   }
 
+  if (!nsCRT::strcmp(aTopic, "memory-pressure") &&
+      !AssumeAllImagesVisible() &&
+      mPresContext->IsRootContentDocument()) {
+    DoUpdateImageVisibility(/* aRemoveOnly = */ true);
+    return NS_OK;
+  }
+
   NS_WARNING("unrecognized topic in PresShell::Observe");
   return NS_ERROR_FAILURE;
 }
 
 bool
 nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver,
                                          mozFlushType aFlushType)
 {
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -369,17 +369,18 @@ public:
     ScrollAxis mContentScrollVAxis;
     ScrollAxis mContentScrollHAxis;
     uint32_t   mContentToScrollToFlags;
   };
 
   virtual void ScheduleImageVisibilityUpdate() override;
 
   virtual void RebuildImageVisibilityDisplayList(const nsDisplayList& aList) override;
-  virtual void RebuildImageVisibility(nsRect* aRect = nullptr) override;
+  virtual void RebuildImageVisibility(nsRect* aRect = nullptr,
+                                      bool aRemoveOnly = false) override;
 
   virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) override;
 
   virtual void RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) override;
 
   virtual bool AssumeAllImagesVisible() override;
 
   virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) override;
@@ -721,24 +722,26 @@ protected:
 #ifdef ANDROID
   virtual nsIDocument* GetTouchEventTargetDocument();
 #endif
 
   virtual void PausePainting() override;
   virtual void ResumePainting() override;
 
   void UpdateImageVisibility();
+  void DoUpdateImageVisibility(bool aRemoveOnly);
   void UpdateActivePointerState(mozilla::WidgetGUIEvent* aEvent);
 
   nsRevocableEventPtr<nsRunnableMethod<PresShell> > mUpdateImageVisibilityEvent;
 
   void ClearVisibleImagesList(uint32_t aNonvisibleAction);
   static void ClearImageVisibilityVisited(nsView* aView, bool aClear);
   static void MarkImagesInListVisible(const nsDisplayList& aList);
-  void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect);
+  void MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect,
+                                  bool aRemoveOnly = false);
 
   // Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
   void HandleKeyboardEvent(nsINode* aTarget,
                            mozilla::WidgetKeyboardEvent& aEvent,
                            bool aEmbeddedCancelled,
                            nsEventStatus* aStatus,
                            mozilla::EventDispatchingCallback* aEventCB);
   void DispatchBeforeKeyboardEventInternal(