Bug 1251150 - Add crash annotations if image visibility is re-entering. r=mats, a=ritu
authorTimothy Nikkel <tnikkel@gmail.com>
Mon, 14 Mar 2016 22:59:02 -0500
changeset 323641 6fb4fbd3b8feb67f9aa94f7c942f93afcbed4929
parent 323640 2d1dca51243a0f40ffb1d37b4807fef196e08aee
child 323642 8ce5c83cfbc6d95eb2352bc77f10cbe090a1681f
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)
reviewersmats, ritu
bugs1251150
milestone47.0a2
Bug 1251150 - Add crash annotations if image visibility is re-entering. r=mats, a=ritu
gfx/2d/Logging.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -540,16 +540,17 @@ typedef Log<LOG_CRITICAL, CriticalLogger
 // This is a shortcut for errors we want logged in crash reports/about support
 // but we do not want asserting.  These are available in all builds, so it is
 // not worth trying to do magic to avoid matching the syntax of gfxCriticalError.
 // So, this one is used as
 // gfxCriticalNote << "Something to report and not assert";
 // while the critical error is
 // gfxCriticalError() << "Something to report and assert";
 #define gfxCriticalNote gfxCriticalError(gfxCriticalError::DefaultOptions(false))
+#define gfxCriticalNoteOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalNote
 
 // The "once" versions will only trigger the first time through. You can do this:
 // gfxCriticalErrorOnce() << "This message only shows up once;
 // instead of the usual:
 // static bool firstTime = true;
 // if (firstTime) {
 //   firstTime = false;
 //   gfxCriticalError() << "This message only shows up once;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5536,16 +5536,19 @@ PresShell::MarkImagesInListVisible(const
       }
     }
   }
 }
 
 void
 PresShell::ReportAnyBadState()
 {
+  if (!NS_IsMainThread()) {
+    gfxCriticalNote << "Got null image in image visibility: off-main-thread";
+  }
   if (mIsZombie) {
     gfxCriticalNote << "Got null image in image visibility: mIsZombie";
   }
   if (mIsDestroying) {
     gfxCriticalNote << "Got null image in image visibility: mIsDestroying";
   }
   if (mIsReflowing) {
     gfxCriticalNote << "Got null image in image visibility: mIsReflowing";
@@ -5562,27 +5565,35 @@ PresShell::ReportAnyBadState()
   if (mIsDocumentGone) {
     gfxCriticalNote << "Got null image in image visibility: mIsDocumentGone";
   }
   if (!nsContentUtils::IsSafeToRunScript()) {
     gfxCriticalNote << "Got null image in image visibility: not safe to run script";
   }
 }
 
+void
+PresShell::SetInImageVisibility(bool aState)
+{
+  mInImageVisibility = aState;
+}
+
 static void
 DecrementVisibleCount(nsTHashtable<nsRefPtrHashKey<nsIImageLoadingContent>>& aImages,
                       uint32_t aNonvisibleAction, PresShell* aPresShell)
 {
   for (auto iter = aImages.Iter(); !iter.Done(); iter.Next()) {
     if (MOZ_UNLIKELY(!iter.Get()->GetKey())) {
       // We are about to crash, annotate crash report with some info that might
       // help debug the crash (bug 1251150)
       aPresShell->ReportAnyBadState();
     }
+    aPresShell->SetInImageVisibility(true);
     iter.Get()->GetKey()->DecrementVisibleCount(aNonvisibleAction);
+    aPresShell->SetInImageVisibility(false);
   }
 }
 
 void
 PresShell::RebuildImageVisibilityDisplayList(const nsDisplayList& aList)
 {
   MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
   mImageVisibilityVisited = true;
@@ -5610,16 +5621,20 @@ PresShell::ClearImageVisibilityVisited(n
   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
     ClearImageVisibilityVisited(v, v->GetViewManager() != vm);
   }
 }
 
 void
 PresShell::ClearVisibleImagesList(uint32_t aNonvisibleAction)
 {
+  if (mInImageVisibility) {
+    gfxCriticalNoteOnce << "ClearVisibleImagesList is re-entering on "
+                        << (NS_IsMainThread() ? "" : "non-") << "main thread";
+  }
   DecrementVisibleCount(mVisibleImages, aNonvisibleAction, this);
   mVisibleImages.Clear();
 }
 
 void
 PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
                                       const nsRect& aRect,
                                       bool aRemoveOnly /* = false */)
@@ -5715,16 +5730,21 @@ PresShell::RebuildImageVisibility(nsRect
   MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
   mImageVisibilityVisited = true;
 
   nsIFrame* rootFrame = GetRootFrame();
   if (!rootFrame) {
     return;
   }
 
+  if (mInImageVisibility) {
+    gfxCriticalNoteOnce << "RebuildImageVisibility is re-entering on "
+                        << (NS_IsMainThread() ? "" : "non-") << "main thread";
+  }
+
   // Remove the entries of the mVisibleImages hashtable and put them in
   // oldVisibleImages.
   nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > oldVisibleImages;
   mVisibleImages.SwapElements(oldVisibleImages);
 
   nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
   if (aRect) {
     vis = *aRect;
@@ -5868,16 +5888,21 @@ PresShell::EnsureImageInVisibleList(nsII
   // if it has a frame make sure its in this presshell
   nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
   if (content) {
     PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
     MOZ_ASSERT(!shell || shell == this, "wrong shell");
   }
 #endif
 
+  if (mInImageVisibility) {
+    gfxCriticalNoteOnce << "EnsureImageInVisibleList is re-entering on "
+                        << (NS_IsMainThread() ? "" : "non-") << "main thread";
+  }
+
   if (!mVisibleImages.Contains(aImage)) {
     mVisibleImages.PutEntry(aImage);
     aImage->IncrementVisibleCount();
   }
 }
 
 void
 PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage)
@@ -5891,16 +5916,21 @@ PresShell::RemoveImageFromVisibleList(ns
   }
 #endif
 
   if (AssumeAllImagesVisible()) {
     MOZ_ASSERT(mVisibleImages.Count() == 0, "shouldn't have any images in the table");
     return;
   }
 
+  if (mInImageVisibility) {
+    gfxCriticalNoteOnce << "RemoveImageFromVisibleList is re-entering on "
+                        << (NS_IsMainThread() ? "" : "non-") << "main thread";
+  }
+
   uint32_t count = mVisibleImages.Count();
   mVisibleImages.RemoveEntry(aImage);
   if (mVisibleImages.Count() < count) {
     // aImage was in the hashtable, so we need to decrement its visible count
     aImage->DecrementVisibleCount(
       nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION);
   }
 }
@@ -10652,16 +10682,21 @@ nsresult
 PresShell::UpdateImageLockingState()
 {
   // We're locked if we're both thawed and active.
   bool locked = !mFrozen && mIsActive;
 
   nsresult rv = mDocument->SetImageLockingState(locked);
 
   if (locked) {
+    if (mInImageVisibility) {
+      gfxCriticalNoteOnce << "UpdateImageLockingState is re-entering on "
+                          << (NS_IsMainThread() ? "" : "non-") << "main thread";
+    }
+
     // Request decodes for visible images; we want to start decoding as
     // quickly as possible when we get foregrounded to minimize flashing.
     for (auto iter = mVisibleImages.Iter(); !iter.Done(); iter.Next()) {
       nsCOMPtr<nsIContent> content = do_QueryInterface(iter.Get()->GetKey());
       nsImageFrame* imageFrame = do_QueryFrame(content->GetPrimaryFrame());
       if (imageFrame) {
         imageFrame->MaybeDecodeForPredictedSize();
       }
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -389,16 +389,18 @@ public:
   virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
                                           const mozilla::WidgetKeyboardEvent& aEvent,
                                           bool aEmbeddedCancelled) override;
 
   void SetNextPaintCompressed() { mNextPaintCompressed = true; }
 
   void ReportAnyBadState();
 
+  void SetInImageVisibility(bool aState);
+
 protected:
   virtual ~PresShell();
 
   void HandlePostedReflowCallbacks(bool aInterruptible);
   void CancelPostedReflowCallbacks();
 
   void ScheduleBeforeFirstPaint();
   void UnsuppressAndInvalidate();
@@ -880,12 +882,14 @@ protected:
   bool                      mScaleToResolution : 1;
 
   // Whether the last chrome-only escape key event is consumed.
   bool                      mIsLastChromeOnlyEscapeKeyConsumed : 1;
 
   // Whether the widget has received a paint message yet.
   bool                      mHasReceivedPaintMessage : 1;
 
+  bool                      mInImageVisibility : 1;
+
   static bool               sDisableNonTestMouseEvents;
 };
 
 #endif /* !defined(nsPresShell_h_) */