diff --git a/docshell/shistory/nsSHEntryShared.cpp b/docshell/shistory/nsSHEntryShared.cpp index 172b397538dd..002a828b5eb4 100644 --- a/docshell/shistory/nsSHEntryShared.cpp +++ b/docshell/shistory/nsSHEntryShared.cpp @@ -168,56 +168,59 @@ nsSHEntryShared::RemoveFromBFCacheSync() { MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!"); + // The call to DropPresentationState could drop the last reference, so hold + // |this| until RemoveDynEntriesForBFCacheEntry finishes. + RefPtr kungFuDeathGrip = this; + + // DropPresentationState would clear mContentViewer. nsCOMPtr viewer = mContentViewer; DropPresentationState(); - // Warning! The call to DropPresentationState could have dropped the last - // reference to this object, so don't access members beyond here. - if (viewer) { viewer->Destroy(); } + // Now that we've dropped the viewer, we have to clear associated dynamic + // subframe entries. + nsCOMPtr shistory = do_QueryReferent(mSHistory); + if (shistory) { + shistory->RemoveDynEntriesForBFCacheEntry(this); + } + return NS_OK; } -class DestroyViewerEvent : public mozilla::Runnable -{ -public: - DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument) - : mozilla::Runnable("DestroyViewerEvent") - , mViewer(aViewer) - , mDocument(aDocument) - { - } - - NS_IMETHOD Run() override - { - if (mViewer) { - mViewer->Destroy(); - } - return NS_OK; - } - - nsCOMPtr mViewer; - nsCOMPtr mDocument; -}; - nsresult nsSHEntryShared::RemoveFromBFCacheAsync() { MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!"); - // Release the reference to the contentviewer asynchronously so that the - // document doesn't get nuked mid-mutation. - + // Check it again to play safe in release builds. if (!mDocument) { return NS_ERROR_UNEXPECTED; } - nsCOMPtr evt = new DestroyViewerEvent(mContentViewer, mDocument); - nsresult rv = mDocument->Dispatch(mozilla::TaskCategory::Other, evt.forget()); + + // DropPresentationState would clear mContentViewer & mDocument. Capture and + // release the references asynchronously so that the document doesn't get + // nuked mid-mutation. + nsCOMPtr viewer = mContentViewer; + nsCOMPtr document = mDocument; + RefPtr self = this; + nsresult rv = mDocument->Dispatch(mozilla::TaskCategory::Other, + NS_NewRunnableFunction("nsSHEntryShared::RemoveFromBFCacheAsync", + [self, viewer, document]() { + if (viewer) { + viewer->Destroy(); + } + + nsCOMPtr shistory = do_QueryReferent(self->mSHistory); + if (shistory) { + shistory->RemoveDynEntriesForBFCacheEntry(self); + } + })); + if (NS_FAILED(rv)) { - NS_WARNING("failed to dispatch DestroyViewerEvent"); + NS_WARNING("Failed to dispatch RemoveFromBFCacheAsync runnable."); } else { // Drop presentation. Only do this if we succeeded in posting the event // since otherwise the document could be torn down mid-mutation, causing @@ -225,9 +228,6 @@ nsSHEntryShared::RemoveFromBFCacheAsync() DropPresentationState(); } - // Careful! The call to DropPresentationState could have dropped the last - // reference to this nsSHEntryShared, so don't access members beyond here. - return NS_OK; }