Bug 1362924 - Part 2: Add script blockers around remaining PresShell::{Destroy,Initialize} calls to protect against re-entrant nsDocumentViewer calls. r=tnikkel

MozReview-Commit-ID: G0lUGsNLb2b
This commit is contained in:
Cameron McCormack 2017-06-28 09:35:45 -04:00
Родитель 35ec5d97b3
Коммит aee3fd5f2f
1 изменённых файлов: 62 добавлений и 17 удалений

Просмотреть файл

@ -714,6 +714,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;
@ -1773,6 +1782,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();
}
@ -1940,6 +1953,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();
}
@ -2145,7 +2162,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) {
// 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<nsIBaseWindow> base_win(mContainer);
@ -2208,8 +2235,8 @@ 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.
RefPtr<nsDocumentShownDispatcher> event =
new nsDocumentShownDispatcher(mDocument);
mDocument->Dispatch("nsDocumentShownDispatcher",
new nsDocumentShownDispatcher(document);
document->Dispatch("nsDocumentShownDispatcher",
TaskCategory::Other,
event.forget());
@ -2253,24 +2280,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<nsIBaseWindow> base_win(mContainer);
mViewManager = nullptr;
mWindow = nullptr;
mDeviceContext = nullptr;
mParentWidget = nullptr;
if (base_win && !mAttachedToParent) {
base_win->SetParentWidget(nullptr);
}
nsCOMPtr<nsIBaseWindow> base_win(mContainer);
if (base_win && !mAttachedToParent) {
base_win->SetParentWidget(nullptr);
}
return NS_OK;
@ -4510,6 +4536,10 @@ nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
mAutoBeforeAndAfterPrint = nullptr;
}
#endif
// Protect against pres shell destruction running scripts.
nsAutoScriptBlocker scriptBlocker;
if (!aIsPrintPreview) {
if (mPresShell) {
DestroyPresShell();
@ -4646,6 +4676,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();
}
@ -4706,6 +4741,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");
nsIFrame* vmRootFrame =
mViewManager && mViewManager->GetRootView()
? mViewManager->GetRootView()->GetFrame()
@ -4720,7 +4762,6 @@ nsDocumentViewer::DestroyPresShell()
if (selection && mSelectionListener)
selection->RemoveSelectionListener(mSelectionListener);
nsAutoScriptBlocker scriptBlocker;
bool hadRootFrame = !!mPresShell->GetRootFrame();
mPresShell->Destroy();
mPresShellDestroyed = true;
@ -4761,6 +4802,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();
}