diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 8178feb4f..075dd3d41 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -629,6 +629,15 @@ nsDocumentViewer::Init(nsIWidget* aParentWidget, nsresult nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) { + // We assert this because initializing the pres shell could otherwise cause + // re-entrancy into nsDocumentViewer methods, which might cause a different + // pres shell to be created. Callers of InitPresentationStuff should ensure + // the call is appropriately bounded by an nsAutoScriptBlocker to decide + // when it is safe for these re-entrant calls to be made. + MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), + "InitPresentationStuff must only be called when scripts are " + "blocked"); + if (GetIsPrintPreview()) return NS_OK; @@ -1652,6 +1661,10 @@ nsDocumentViewer::Destroy() // The document was not put in the bfcache + // Protect against pres shell destruction running scripts and re-entrantly + // creating a new presentation. + nsAutoScriptBlocker scriptBlocker; + if (mPresShell) { DestroyPresShell(); } @@ -1817,6 +1830,10 @@ nsDocumentViewer::SetDocumentInternal(nsIDocument* aDocument, // Replace the current pres shell with a new shell for the new document + // Protect against pres shell destruction running scripts and re-entrantly + // creating a new presentation. + nsAutoScriptBlocker scriptBlocker; + if (mPresShell) { DestroyPresShell(); } @@ -2014,7 +2031,17 @@ nsDocumentViewer::Show(void) } } + // Hold on to the document so we can use it after the script blocker below + // has been released (which might re-entrantly call into other + // nsDocumentViewer methods). + nsCOMPtr document = mDocument; + if (mDocument && !mPresShell) { + // The InitPresentationStuff call below requires a script blocker, because + // its PresShell::Initialize call can cause scripts to run and therefore + // re-entrant calls to nsDocumentViewer methods to be made. + nsAutoScriptBlocker scriptBlocker; + NS_ASSERTION(!mWindow, "Window already created but no presshell?"); nsCOMPtr base_win(mContainer); @@ -2076,7 +2103,7 @@ nsDocumentViewer::Show(void) // Notify observers that a new page has been shown. This will get run // from the event loop after we actually draw the page. - NS_DispatchToMainThread(new nsDocumentShownDispatcher(mDocument)); + NS_DispatchToMainThread(new nsDocumentShownDispatcher(document)); return NS_OK; } @@ -2113,24 +2140,23 @@ nsDocumentViewer::Hide(void) mPresShell->CaptureHistoryState(getter_AddRefs(layoutState)); } - { - // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate - // state before we're done destroying PresShell, PresContext, ViewManager, etc. - nsAutoScriptBlocker scriptBlocker; - DestroyPresShell(); + // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate + // state before we're done destroying PresShell, PresContext, ViewManager, etc. + nsAutoScriptBlocker scriptBlocker; - DestroyPresContext(); + DestroyPresShell(); - mViewManager = nullptr; - mWindow = nullptr; - mDeviceContext = nullptr; - mParentWidget = nullptr; + DestroyPresContext(); - nsCOMPtr base_win(mContainer); + mViewManager = nullptr; + mWindow = nullptr; + mDeviceContext = nullptr; + mParentWidget = nullptr; - if (base_win && !mAttachedToParent) { - base_win->SetParentWidget(nullptr); - } + nsCOMPtr base_win(mContainer); + + if (base_win && !mAttachedToParent) { + base_win->SetParentWidget(nullptr); } return NS_OK; @@ -4153,9 +4179,14 @@ nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview) SetIsPrintingInDocShellTree(docShell, aIsPrintPreview, true); } if (!aIsPrintPreview) { + // Dispatch the 'afterprint' event now, if pending: mBeforeAndAfterPrint = nullptr; } #endif + + // Protect against pres shell destruction running scripts. + nsAutoScriptBlocker scriptBlocker; + if (!aIsPrintPreview) { if (mPresShell) { DestroyPresShell(); @@ -4284,6 +4315,11 @@ NS_IMETHODIMP nsDocumentViewer::SetPageMode(bool aPageMode, nsIPrintSettings* aP // reftests that require a paginated context mIsPageMode = aPageMode; + // The DestroyPresShell call requires a script blocker, since the + // PresShell::Destroy call it does can cause scripts to run, which could + // re-entrantly call methods on the nsDocumentViewer. + nsAutoScriptBlocker scriptBlocker; + if (mPresShell) { DestroyPresShell(); } @@ -4344,6 +4380,13 @@ nsDocumentViewer::SetIsHidden(bool aHidden) void nsDocumentViewer::DestroyPresShell() { + // We assert this because destroying the pres shell could otherwise cause + // re-entrancy into nsDocumentViewer methods, and all callers of + // DestroyPresShell need to do other cleanup work afterwards before it + // is safe for those re-entrant method calls to be made. + MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), + "DestroyPresShell must only be called when scripts are blocked"); + // Break circular reference (or something) mPresShell->EndObservingDocument(); @@ -4351,7 +4394,6 @@ nsDocumentViewer::DestroyPresShell() if (selection && mSelectionListener) selection->RemoveSelectionListener(mSelectionListener); - nsAutoScriptBlocker scriptBlocker; mPresShell->Destroy(); mPresShell = nullptr; } @@ -4380,6 +4422,10 @@ nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager, nsPresContext* aPresContext, nsIPresShell* aPresShell) { + // Protect against pres shell destruction running scripts and re-entrantly + // creating a new presentation. + nsAutoScriptBlocker scriptBlocker; + if (mPresShell) { DestroyPresShell(); } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 76be28c48..e769a015b 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1601,6 +1601,24 @@ PresShell::EndObservingDocument() char* nsPresShell_ReflowStackPointerTop; #endif +class XBLConstructorRunner : public nsRunnable +{ +public: + explicit XBLConstructorRunner(nsIDocument* aDocument) + : mDocument(aDocument) + { + } + + NS_IMETHOD Run() override + { + mDocument->BindingManager()->ProcessAttachedQueue(); + return NS_OK; + } + +private: + nsCOMPtr mDocument; +}; + nsresult PresShell::Initialize(nscoord aWidth, nscoord aHeight) { @@ -1698,24 +1716,14 @@ PresShell::Initialize(nscoord aWidth, nscoord aHeight) mFrameConstructor->EndUpdate(); } - // nsAutoScriptBlocker going out of scope may have killed us too + // nsAutoCauseReflowNotifier (which sets up a script blocker) going out of + // scope may have killed us too NS_ENSURE_STATE(!mHaveShutDown); - // Run the XBL binding constructors for any new frames we've constructed - mDocument->BindingManager()->ProcessAttachedQueue(); - - // Constructors may have killed us too - NS_ENSURE_STATE(!mHaveShutDown); - - // Now flush out pending restyles before we actually reflow, in - // case XBL constructors changed styles somewhere. - { - nsAutoScriptBlocker scriptBlocker; - mPresContext->RestyleManager()->ProcessPendingRestyles(); - } - - // And that might have run _more_ XBL constructors - NS_ENSURE_STATE(!mHaveShutDown); + // Run the XBL binding constructors for any new frames we've constructed. + // (Do this in a script runner, since our caller might have a script + // blocker on the stack.) + nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument)); } NS_ASSERTION(rootFrame, "How did that happen?");