diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 2ff5273d93..7ad4be98da 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -893,6 +893,8 @@ nsDocShell::~nsDocShell() nsresult nsDocShell::Init() { + MOZ_ASSERT(!mIsBeingDestroyed); + nsresult rv = nsDocLoader::Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -1921,6 +1923,8 @@ bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest, bool aFireOnLocationChange, uint32_t aLocationFlags) { + MOZ_ASSERT(!mIsBeingDestroyed); + if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) { PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, aURI ? aURI->GetSpecOrDefault().get() : ""); @@ -2181,6 +2185,8 @@ nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) NS_IMETHODIMP nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) { + MOZ_ASSERT(!mIsBeingDestroyed); + bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0); if (changed) { mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0; @@ -2251,6 +2257,8 @@ nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) NS_IMETHODIMP nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) { + MOZ_ASSERT(!mIsBeingDestroyed); + bool change = aAffectLifetime != mAffectPrivateSessionLifetime; if (change && UsePrivateBrowsing()) { AssertOriginAttributesMatchPrivateBrowsing(); @@ -2753,6 +2761,8 @@ nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI) NS_IMETHODIMP nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI) { + MOZ_ASSERT(!mIsBeingDestroyed); + mSecurityUI = aSecurityUI; mSecurityUI->SetDocShell(this); return NS_OK; @@ -3935,6 +3945,10 @@ PrintDocTree(nsIDocShellTreeItem* aParentNode) NS_IMETHODIMP nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) { + if (mIsBeingDestroyed && aTreeOwner) { + return NS_ERROR_FAILURE; + } + #ifdef DEBUG_DOCSHELL_FOCUS nsCOMPtr item(do_QueryInterface(aTreeOwner)); if (item) { @@ -5216,6 +5230,8 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, const char* aCSSClass, nsIChannel* aFailedChannel) { + MOZ_ASSERT(!mIsBeingDestroyed); + #if defined(DEBUG) if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) { nsAutoCString chanName; @@ -5571,6 +5587,8 @@ nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory) // make sure that we are the root docshell and // set a handle to root docshell in SH. + MOZ_ASSERT(!mIsBeingDestroyed); + nsCOMPtr root; /* Get the root docshell. If *this* is the root docshell * then save a handle to *this* in SH. SH needs it to do @@ -5751,16 +5769,22 @@ nsDocShell::Create() NS_IMETHODIMP nsDocShell::Destroy() { + // XXX: We allow this function to be called just once. If you are going to + // reset new variables in this function, please make sure the variables will + // never be re-initialized. Adding assertions to check |mIsBeingDestroyed| + // in the setter functions for the variables would be enough. + if (mIsBeingDestroyed) { + return NS_ERROR_DOCSHELL_DYING; + } + NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); - if (!mIsBeingDestroyed) { - nsCOMPtr serv = services::GetObserverService(); - if (serv) { - const char* msg = mItemType == typeContent ? - NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY; - serv->NotifyObservers(GetAsSupports(this), msg, nullptr); - } + nsCOMPtr serv = services::GetObserverService(); + if (serv) { + const char* msg = mItemType == typeContent ? + NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY; + serv->NotifyObservers(GetAsSupports(this), msg, nullptr); } mIsBeingDestroyed = true; @@ -6043,6 +6067,7 @@ nsDocShell::GetParentWidget(nsIWidget** aParentWidget) NS_IMETHODIMP nsDocShell::SetParentWidget(nsIWidget* aParentWidget) { + MOZ_ASSERT(!mIsBeingDestroyed); mParentWidget = aParentWidget; return NS_OK; @@ -6313,6 +6338,8 @@ nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator) return NS_OK; } + MOZ_ASSERT(!mIsBeingDestroyed); + mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator); NS_ASSERTION(mOnePermittedSandboxedNavigator, "One Permitted Sandboxed Navigator must support weak references."); @@ -6673,6 +6700,8 @@ nsDocShell::RefreshURI(nsIURI* aURI, bool aMetaRefresh, nsIPrincipal* aPrincipal) { + MOZ_ASSERT(!mIsBeingDestroyed); + NS_ENSURE_ARG(aURI); /* Check if Meta refresh/redirects are permitted. Some @@ -8056,6 +8085,10 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal, // wrong information :-( // (void)FirePageHideNotification(!mSavingOldViewer); + // pagehide notification might destroy this docshell. + if (mIsBeingDestroyed) { + return NS_ERROR_DOCSHELL_DYING; + } } // Now make sure we don't think we're in the middle of firing unload after @@ -8199,6 +8232,8 @@ nsDocShell::CanSavePresentation(uint32_t aLoadType, void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) { + MOZ_ASSERT(!mIsBeingDestroyed); + NS_ASSERTION(!mEditorData, "Why reattach an editor when we already have one?"); NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(), @@ -8236,6 +8271,9 @@ nsDocShell::DetachEditorFromWindow() if (NS_SUCCEEDED(res)) { // Make mOSHE hold the owning ref to the editor data. if (mOSHE) { + MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(), + "We should not set the editor data again once after we " + "detached the editor data during destroying this docshell"); mOSHE->SetEditorData(mEditorData.forget()); } else { mEditorData = nullptr; @@ -8422,6 +8460,8 @@ nsDocShell::GetRestoringDocument(bool* aRestoring) nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring) { + MOZ_ASSERT(!mIsBeingDestroyed); + NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY, "RestorePresentation should only be called for history loads"); @@ -8583,6 +8623,10 @@ nsDocShell::RestoreFromHistory() // Notify the old content viewer that it's being hidden. FirePageHideNotification(!mSavingOldViewer); + // pagehide notification might destroy this docshell. + if (mIsBeingDestroyed) { + return NS_ERROR_DOCSHELL_DYING; + } // If mLSHE was changed as a result of the pagehide event, then // something else was loaded. Don't finish restoring. @@ -9040,7 +9084,7 @@ nsDocShell::CreateContentViewer(const nsACString& aContentType, { *aContentHandler = nullptr; - if (!mTreeOwner) { + if (!mTreeOwner || mIsBeingDestroyed) { // If we don't have a tree owner, then we're in the process of being // destroyed. Rather than continue trying to load something, just give up. return NS_ERROR_DOCSHELL_DYING; @@ -9085,6 +9129,11 @@ nsDocShell::CreateContentViewer(const nsACString& aContentType, aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI)); } FirePageHideNotification(!mSavingOldViewer); + if (mIsBeingDestroyed) { + // Force to stop the newly created orphaned viewer. + viewer->Stop(); + return NS_ERROR_DOCSHELL_DYING; + } mLoadingURI = nullptr; // Set mFiredUnloadEvent = false so that the unload handler for the @@ -9274,6 +9323,8 @@ nsDocShell::NewContentViewerObj(const nsACString& aContentType, nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer) { + MOZ_ASSERT(!mIsBeingDestroyed); + // // Copy content viewer state from previous or parent content viewer. // @@ -13466,6 +13517,8 @@ nsDocShell::EnsureScriptEnvironment() NS_IMETHODIMP nsDocShell::EnsureEditorData() { + MOZ_ASSERT(!mIsBeingDestroyed); + bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor(); if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) { // We shouldn't recreate the editor data if it already exists, or @@ -13481,6 +13534,8 @@ nsDocShell::EnsureEditorData() nsresult nsDocShell::EnsureTransferableHookData() { + MOZ_ASSERT(!mIsBeingDestroyed); + if (!mTransferableHookData) { mTransferableHookData = new nsTransferableHookData(); } @@ -14568,6 +14623,8 @@ nsDocShell::CanSetOriginAttributes() nsresult nsDocShell::SetOriginAttributes(const DocShellOriginAttributes& aAttrs) { + MOZ_ASSERT(!mIsBeingDestroyed); + if (!CanSetOriginAttributes()) { return NS_ERROR_FAILURE; } diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 0392fce500..d8760db70c 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -100,6 +100,7 @@ nsContentSink::nsContentSink() , mDeferredFlushTags(0) , mIsDocumentObserver(0) , mRunsToCompletion(0) + , mIsBlockingOnload(false) , mDeflectedCount(0) , mHasPendingEvent(false) , mCurrentParseEndTime(0) @@ -1539,8 +1540,14 @@ nsContentSink::DropParserAndPerfHint(void) FavorPerformanceHint(true, 0); } - if (!mRunsToCompletion) { + // Call UnblockOnload only if mRunsToComletion is false and if + // we have already started loading because it's possible that this function + // is called (i.e. the parser is terminated) before we start loading due to + // destroying the window inside unload event callbacks for the previous + // document. + if (!mRunsToCompletion && mIsBlockingOnload) { mDocument->UnblockOnload(true); + mIsBlockingOnload = false; } } @@ -1595,6 +1602,7 @@ nsContentSink::WillBuildModelImpl() { if (!mRunsToCompletion) { mDocument->BlockOnload(); + mIsBlockingOnload = true; mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow()); } diff --git a/dom/base/nsContentSink.h b/dom/base/nsContentSink.h index 43a873dd6f..4140a3f19f 100644 --- a/dom/base/nsContentSink.h +++ b/dom/base/nsContentSink.h @@ -304,6 +304,8 @@ protected: // True if this is parser is a fragment parser or an HTML DOMParser. // XML DOMParser leaves this to false for now! uint8_t mRunsToCompletion : 1; + // True if we are blocking load event. + bool mIsBlockingOnload : 1; // // -- Can interrupt parsing members --