This commit is contained in:
Cameron Kaiser 2017-07-22 21:47:40 -07:00
parent b1cfd7830c
commit 61ec438893
2 changed files with 86 additions and 32 deletions

View File

@ -629,6 +629,15 @@ nsDocumentViewer::Init(nsIWidget* aParentWidget,
nsresult nsresult
nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) 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()) if (GetIsPrintPreview())
return NS_OK; return NS_OK;
@ -1652,6 +1661,10 @@ nsDocumentViewer::Destroy()
// The document was not put in the bfcache // 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) { if (mPresShell) {
DestroyPresShell(); DestroyPresShell();
} }
@ -1817,6 +1830,10 @@ nsDocumentViewer::SetDocumentInternal(nsIDocument* aDocument,
// Replace the current pres shell with a new shell for the new document // 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) { if (mPresShell) {
DestroyPresShell(); 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<nsIDocument> document = mDocument;
if (mDocument && !mPresShell) { 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?"); NS_ASSERTION(!mWindow, "Window already created but no presshell?");
nsCOMPtr<nsIBaseWindow> base_win(mContainer); nsCOMPtr<nsIBaseWindow> base_win(mContainer);
@ -2076,7 +2103,7 @@ nsDocumentViewer::Show(void)
// Notify observers that a new page has been shown. This will get run // Notify observers that a new page has been shown. This will get run
// from the event loop after we actually draw the page. // from the event loop after we actually draw the page.
NS_DispatchToMainThread(new nsDocumentShownDispatcher(mDocument)); NS_DispatchToMainThread(new nsDocumentShownDispatcher(document));
return NS_OK; return NS_OK;
} }
@ -2113,24 +2140,23 @@ nsDocumentViewer::Hide(void)
mPresShell->CaptureHistoryState(getter_AddRefs(layoutState)); mPresShell->CaptureHistoryState(getter_AddRefs(layoutState));
} }
{ // Do not run ScriptRunners queued by DestroyPresShell() in the intermediate
// Do not run ScriptRunners queued by DestroyPresShell() in the intermediate // state before we're done destroying PresShell, PresContext, ViewManager, etc.
// state before we're done destroying PresShell, PresContext, ViewManager, etc. nsAutoScriptBlocker scriptBlocker;
nsAutoScriptBlocker scriptBlocker;
DestroyPresShell();
DestroyPresContext(); DestroyPresShell();
mViewManager = nullptr; DestroyPresContext();
mWindow = nullptr;
mDeviceContext = nullptr;
mParentWidget = nullptr;
nsCOMPtr<nsIBaseWindow> base_win(mContainer); mViewManager = nullptr;
mWindow = nullptr;
mDeviceContext = nullptr;
mParentWidget = nullptr;
if (base_win && !mAttachedToParent) { nsCOMPtr<nsIBaseWindow> base_win(mContainer);
base_win->SetParentWidget(nullptr);
} if (base_win && !mAttachedToParent) {
base_win->SetParentWidget(nullptr);
} }
return NS_OK; return NS_OK;
@ -4153,9 +4179,14 @@ nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
SetIsPrintingInDocShellTree(docShell, aIsPrintPreview, true); SetIsPrintingInDocShellTree(docShell, aIsPrintPreview, true);
} }
if (!aIsPrintPreview) { if (!aIsPrintPreview) {
// Dispatch the 'afterprint' event now, if pending:
mBeforeAndAfterPrint = nullptr; mBeforeAndAfterPrint = nullptr;
} }
#endif #endif
// Protect against pres shell destruction running scripts.
nsAutoScriptBlocker scriptBlocker;
if (!aIsPrintPreview) { if (!aIsPrintPreview) {
if (mPresShell) { if (mPresShell) {
DestroyPresShell(); DestroyPresShell();
@ -4284,6 +4315,11 @@ NS_IMETHODIMP nsDocumentViewer::SetPageMode(bool aPageMode, nsIPrintSettings* aP
// reftests that require a paginated context // reftests that require a paginated context
mIsPageMode = aPageMode; 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) { if (mPresShell) {
DestroyPresShell(); DestroyPresShell();
} }
@ -4344,6 +4380,13 @@ nsDocumentViewer::SetIsHidden(bool aHidden)
void void
nsDocumentViewer::DestroyPresShell() 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) // Break circular reference (or something)
mPresShell->EndObservingDocument(); mPresShell->EndObservingDocument();
@ -4351,7 +4394,6 @@ nsDocumentViewer::DestroyPresShell()
if (selection && mSelectionListener) if (selection && mSelectionListener)
selection->RemoveSelectionListener(mSelectionListener); selection->RemoveSelectionListener(mSelectionListener);
nsAutoScriptBlocker scriptBlocker;
mPresShell->Destroy(); mPresShell->Destroy();
mPresShell = nullptr; mPresShell = nullptr;
} }
@ -4380,6 +4422,10 @@ nsDocumentViewer::SetPrintPreviewPresentation(nsViewManager* aViewManager,
nsPresContext* aPresContext, nsPresContext* aPresContext,
nsIPresShell* aPresShell) nsIPresShell* aPresShell)
{ {
// Protect against pres shell destruction running scripts and re-entrantly
// creating a new presentation.
nsAutoScriptBlocker scriptBlocker;
if (mPresShell) { if (mPresShell) {
DestroyPresShell(); DestroyPresShell();
} }

View File

@ -1601,6 +1601,24 @@ PresShell::EndObservingDocument()
char* nsPresShell_ReflowStackPointerTop; char* nsPresShell_ReflowStackPointerTop;
#endif #endif
class XBLConstructorRunner : public nsRunnable
{
public:
explicit XBLConstructorRunner(nsIDocument* aDocument)
: mDocument(aDocument)
{
}
NS_IMETHOD Run() override
{
mDocument->BindingManager()->ProcessAttachedQueue();
return NS_OK;
}
private:
nsCOMPtr<nsIDocument> mDocument;
};
nsresult nsresult
PresShell::Initialize(nscoord aWidth, nscoord aHeight) PresShell::Initialize(nscoord aWidth, nscoord aHeight)
{ {
@ -1698,24 +1716,14 @@ PresShell::Initialize(nscoord aWidth, nscoord aHeight)
mFrameConstructor->EndUpdate(); 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); NS_ENSURE_STATE(!mHaveShutDown);
// Run the XBL binding constructors for any new frames we've constructed // Run the XBL binding constructors for any new frames we've constructed.
mDocument->BindingManager()->ProcessAttachedQueue(); // (Do this in a script runner, since our caller might have a script
// blocker on the stack.)
// Constructors may have killed us too nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument));
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);
} }
NS_ASSERTION(rootFrame, "How did that happen?"); NS_ASSERTION(rootFrame, "How did that happen?");