Bug 1354933. Add some asserts for document viewer teardown. r=mats
authorTimothy Nikkel <tnikkel@gmail.com>
Sat, 03 Jun 2017 01:31:12 -0500
changeset 362182 43af422173e3bdfddc18c02d7f7c430d6ec9cdfe
parent 362181 bb37ef22d35ca7d9c5c77805a1aceb62e15773b1
child 362183 90df8b3f52d60d79b2730153f97300039019b694
push id31961
push userarchaeopteryx@coole-files.de
push dateSat, 03 Jun 2017 18:15:59 +0000
treeherdermozilla-central@130efc657df7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs1354933
milestone55.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 1354933. Add some asserts for document viewer teardown. r=mats
layout/base/PresShell.cpp
layout/base/nsDocumentViewer.cpp
view/nsView.cpp
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1157,16 +1157,22 @@ LogTextPerfStats(gfxTextPerfMetrics* aTe
   }
 }
 
 void
 PresShell::Destroy()
 {
   // Do not add code before this line please!
   if (mHaveShutDown) {
+    // If we never got a root frame the root view could exist now still.
+    // In that case assert that it has no children and no frame.
+    MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView() ||
+      (!mViewManager->GetRootView()->GetFrame() &&
+       !mViewManager->GetRootView()->GetFirstChild()));
+    MOZ_RELEASE_ASSERT(!mFrameConstructor || !mFrameConstructor->GetRootFrame());
     return;
   }
 
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
     "destroy called on presshell while scripts not blocked");
 
   // dump out cumulative text perf metrics
   gfxTextPerfMetrics* tp;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -404,16 +404,18 @@ protected:
   int32_t mHintCharsetSource;
   nsCString mHintCharset;
   nsCString mForceCharacterSet;
   
   bool mIsPageMode;
   bool mInitializedForPrintPreview;
   bool mHidden;
   bool mPrintRelated; // Only use for asserts.
+  bool mPresShellDestroyed; // Only use for asserts.
+  bool mDestroyWasFull; // Only use for asserts.
 };
 
 namespace mozilla {
 
 /**
  * A RAII class for automatic dispatch of the 'beforeprint' and 'afterprint'
  * events ('beforeprint' on construction, 'afterprint' on destruction).
  *
@@ -538,17 +540,19 @@ nsDocumentViewer::nsDocumentViewer()
 #ifdef DEBUG
     mDebugFile(nullptr),
 #endif // DEBUG
 #endif // NS_PRINTING
     mHintCharsetSource(kCharsetUninitialized),
     mIsPageMode(false),
     mInitializedForPrintPreview(false),
     mHidden(false),
-    mPrintRelated(false)
+    mPrintRelated(false),
+    mPresShellDestroyed(true),
+    mDestroyWasFull(false)
 {
   PrepareToStartLoad();
 }
 
 void
 nsDocumentViewer::SetPrintRelated()
 {
   if (!mPrintRelated) {
@@ -575,26 +579,41 @@ NS_INTERFACE_MAP_END
 
 nsDocumentViewer::~nsDocumentViewer()
 {
   if (mDocument) {
     Close(nullptr);
     mDocument->Destroy();
   }
 
+  nsIFrame* vmRootFrame =
+    mViewManager && mViewManager->GetRootView()
+      ? mViewManager->GetRootView()->GetFrame()
+      : nullptr;
+  nsIFrame* psRootFrame = mPresShell ? mPresShell->GetRootFrame() : nullptr;
+  MOZ_RELEASE_ASSERT(vmRootFrame == psRootFrame);
+
   NS_ASSERTION(!mPresShell && !mPresContext,
                "User did not call nsIContentViewer::Destroy");
   if (mPresShell || mPresContext) {
     // Make sure we don't hand out a reference to the content viewer to
     // the SHEntry!
     mSHEntry = nullptr;
-
+    mDestroyWasFull = false;
     Destroy();
+    MOZ_RELEASE_ASSERT(mDestroyWasFull);
   }
 
+  MOZ_RELEASE_ASSERT(mPresShellDestroyed);
+
+  MOZ_RELEASE_ASSERT(!mPresShell || !mPresShell->GetRootFrame());
+  MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView() ||
+    (!mViewManager->GetRootView()->GetFrame() &&
+     !mViewManager->GetRootView()->GetFirstChild()));
+
   if (mSelectionListener) {
     mSelectionListener->Disconnect();
   }
 
   if (mFocusListener) {
     mFocusListener->Disconnect();
   }
 
@@ -703,16 +722,17 @@ nsDocumentViewer::InitPresentationStuff(
   StyleSetHandle styleSet = CreateStyleSet(mDocument);
 
   // Now make the shell for the document
   mPresShell = mDocument->CreateShell(mPresContext, mViewManager, styleSet);
   if (!mPresShell) {
     styleSet->Delete();
     return NS_ERROR_FAILURE;
   }
+  mPresShellDestroyed = false;
 
   // We're done creating the style set
   styleSet->EndUpdate();
 
   if (aDoInitialReflow) {
     // Since Initialize() will create frames for *all* items
     // that are currently in the document tree, we need to flush
     // any pending notifications to prevent the content sink from
@@ -1791,16 +1811,18 @@ nsDocumentViewer::Destroy()
   if (mPresContext) {
     DestroyPresContext();
   }
 
   mWindow = nullptr;
   mViewManager = nullptr;
   mContainer = WeakPtr<nsDocShell>();
 
+  mDestroyWasFull = true;
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::Stop(void)
 {
   NS_ASSERTION(mDocument, "Stop called too early or too late");
   if (mDocument) {
@@ -4652,25 +4674,43 @@ nsDocumentViewer::SetIsHidden(bool aHidd
 {
   mHidden = aHidden;
   return NS_OK;
 }
 
 void
 nsDocumentViewer::DestroyPresShell()
 {
+  nsIFrame* vmRootFrame =
+    mViewManager && mViewManager->GetRootView()
+      ? mViewManager->GetRootView()->GetFrame()
+      : nullptr;
+  nsIFrame* psRootFrame = mPresShell ? mPresShell->GetRootFrame() : nullptr;
+  MOZ_RELEASE_ASSERT(vmRootFrame == psRootFrame);
+
   // Break circular reference (or something)
   mPresShell->EndObservingDocument();
 
   RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection();
   if (selection && mSelectionListener)
     selection->RemoveSelectionListener(mSelectionListener);
 
   nsAutoScriptBlocker scriptBlocker;
+  bool hadRootFrame = !!mPresShell->GetRootFrame();
   mPresShell->Destroy();
+  mPresShellDestroyed = true;
+  MOZ_RELEASE_ASSERT(!mPresShell->GetRootFrame());
+  // destroying the frame tree via presshell destroy should have done this
+  if (hadRootFrame) {
+    MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView());
+  }
+  MOZ_RELEASE_ASSERT(!mViewManager || !mViewManager->GetRootView() ||
+    (!mViewManager->GetRootView()->GetFrame() &&
+     !mViewManager->GetRootView()->GetFirstChild()));
+
   mPresShell = nullptr;
 }
 
 void
 nsDocumentViewer::DestroyPresContext()
 {
   mPresContext->Detach();
   mPresContext = nullptr;
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -63,16 +63,20 @@ void nsView::DropMouseGrabbing()
 }
 
 nsView::~nsView()
 {
   MOZ_COUNT_DTOR(nsView);
 
   bool printRelated = mViewManager && mViewManager->GetPrintRelated();
 
+  if (mViewManager && (mViewManager->GetRootView() == this)) {
+    MOZ_RELEASE_ASSERT(!GetFirstChild());
+  }
+
   while (GetFirstChild())
   {
     nsView* child = GetFirstChild();
     if (child->GetViewManager() == mViewManager) {
       child->Destroy();
     } else {
       // just unhook it. Someone else will want to destroy this.
       RemoveChild(child);
@@ -467,16 +471,18 @@ void nsView::InsertChild(nsView *aChild,
       //insert after sibling
       aChild->SetNextSibling(aSibling->GetNextSibling());
       aSibling->SetNextSibling(aChild);
     }
     else
     {
       aChild->SetNextSibling(mFirstChild);
       mFirstChild = aChild;
+      MOZ_RELEASE_ASSERT(!mFirstChild || mFrame ||
+        mFirstChild->GetViewManager() != GetViewManager());
     }
     aChild->SetParent(this);
 
     // If we just inserted a root view, then update the RootViewManager
     // on all view managers in the new subtree.
 
     nsViewManager *vm = aChild->GetViewManager();
     if (vm->GetRootView() == aChild)