Bug 1303167 P2 Use new window suspend and freeze methods. r=smaug
authorBen Kelly <ben@wanderview.com>
Wed, 26 Oct 2016 10:20:15 -0700
changeset 362488 a0d387a5da07ab663881bf7802f659cd8df166dc
parent 362487 69547514d7bdbd60dca72a10a677b02bad9d820b
child 362489 89289670d84b1866882cc8cd6eb3e716edc317bc
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1303167
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1303167 P2 Use new window suspend and freeze methods. r=smaug * * * Bug 1303167 P2 interdiff 0001 Review feedback
docshell/base/nsDocShell.cpp
dom/base/nsDOMWindowUtils.cpp
dom/base/nsDocument.cpp
dom/base/nsFocusManager.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsWindowMemoryReporter.cpp
dom/bindings/BindingUtils.cpp
dom/indexedDB/ActorsChild.cpp
dom/offline/nsDOMOfflineResourceList.cpp
dom/workers/WorkerPrivate.cpp
dom/xhr/XMLHttpRequestMainThread.cpp
layout/printing/nsPrintEngine.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8759,36 +8759,31 @@ nsDocShell::RestoreFromHistory()
     newCv->SetMinFontSize(minFontSize);
     newCv->SetTextZoom(textZoom);
     newCv->SetFullZoom(pageZoom);
     newCv->SetOverrideDPPX(overrideDPPX);
     newCv->SetAuthorStyleDisabled(styleDisabled);
   }
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
-  uint32_t parentSuspendCount = 0;
   if (document) {
     RefPtr<nsDocShell> parent = GetParentDocshell();
     if (parent) {
       nsCOMPtr<nsIDocument> d = parent->GetDocument();
       if (d) {
         if (d->EventHandlingSuppressed()) {
           document->SuppressEventHandling(nsIDocument::eEvents,
                                           d->EventHandlingSuppressed());
         }
 
         // Ick, it'd be nicer to not rewalk all of the subdocs here.
         if (d->AnimationsPaused()) {
           document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
                                           d->AnimationsPaused());
         }
-
-        if (nsCOMPtr<nsPIDOMWindowOuter> parentWindow = d->GetWindow()) {
-          parentSuspendCount = parentWindow->TimeoutSuspendCount();
-        }
       }
     }
 
     // Use the uri from the mLSHE we had when we entered this function
     // (which need not match the document's URI if anchors are involved),
     // since that's the history entry we're loading.  Note that if we use
     // origLSHE we don't have to worry about whether the entry in question
     // is still mLSHE or whether it's now mOSHE.
@@ -8915,26 +8910,26 @@ nsDocShell::RestoreFromHistory()
                             rootViewSibling,
                             rootViewSibling ? true : false);
 
       NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
                    "error in InsertChild");
     }
   }
 
+  nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
+
   // If parent is suspended, increase suspension count.
   // This can't be done as early as event suppression since this
   // depends on docshell tree.
-  if (parentSuspendCount) {
-    privWin->SuspendTimeouts(parentSuspendCount, false);
-  }
+  privWinInner->NewSyncStateFromParentWindow();
 
   // Now that all of the child docshells have been put into place, we can
   // restart the timers for the window and all of the child frames.
-  privWin->ResumeTimeouts();
+  privWinInner->NewResume();
 
   // Restore the refresh URI list.  The refresh timers will be restarted
   // when EndPageLoad() is called.
   mRefreshURIList = refreshURIList;
 
   // Meta-refresh timers have been restarted for this shell, but not
   // for our children.  Walk the child shells and restart their timers.
   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2274,28 +2274,34 @@ nsDOMWindowUtils::GetCurrentInnerWindowI
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SuspendTimeouts()
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
-  window->SuspendTimeouts(1, true, false);
+  nsCOMPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow();
+  NS_ENSURE_TRUE(inner, NS_ERROR_FAILURE);
+
+  inner->NewSuspend();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::ResumeTimeouts()
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
-  window->ResumeTimeouts(true, false);
+  nsCOMPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow();
+  NS_ENSURE_TRUE(inner, NS_ERROR_FAILURE);
+
+  inner->NewResume();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetLayerManagerType(nsAString& aType)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -8191,18 +8191,23 @@ nsDocument::EnumerateSubDocuments(nsSubD
 
 bool
 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
 {
   if (EventHandlingSuppressed()) {
     return false;
   }
 
+  // Do not allow suspended windows to be placed in the
+  // bfcache.  This method is also used to verify a document
+  // coming out of the bfcache is ok to restore, though.  So
+  // we only want to block suspend windows that aren't also
+  // frozen.
   nsPIDOMWindowInner* win = GetInnerWindow();
-  if (win && win->TimeoutSuspendCount()) {
+  if (win && win->NewIsSuspended() && !win->NewIsFrozen()) {
     return false;
   }
 
   // Check our event listener manager for unload/beforeunload listeners.
   nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
   if (piTarget) {
     EventListenerManager* manager = piTarget->GetExistingListenerManager();
     if (manager && manager->HasUnloadListeners()) {
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1484,23 +1484,23 @@ nsFocusManager::AdjustWindowFocus(nsPIDO
       window->SetFocusedNode(frameElement);
     }
   }
 }
 
 bool
 nsFocusManager::IsWindowVisible(nsPIDOMWindowOuter* aWindow)
 {
-  if (!aWindow || aWindow->IsFrozen())
+  if (!aWindow || aWindow->NewIsFrozen())
     return false;
 
   // Check if the inner window is frozen as well. This can happen when a focus change
   // occurs while restoring a previous page.
   nsPIDOMWindowInner* innerWindow = aWindow->GetCurrentInnerWindow();
-  if (!innerWindow || innerWindow->IsFrozen())
+  if (!innerWindow || innerWindow->NewIsFrozen())
     return false;
 
   nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
   nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
   if (!baseWin)
     return false;
 
   bool visible = false;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1257,19 +1257,18 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
         // events. Use a strong reference.
         os->AddObserver(mObserver, "dom-storage2-changed", false);
       }
 
       Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
-    // remain frozen until they get an inner window, so freeze this
-    // outer window here.
-    Freeze();
+    // remain frozen until they get an inner window.
+    MOZ_ASSERT(NewIsFrozen());
 
     // As an outer window, we may be the root of a constellation. This initial
     // static constellation may be overridden as this window is given a parent
     // window or an opener.
     mStaticConstellation = WindowID();
   }
 
   // We could have failed the first time through trying
@@ -2297,17 +2296,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WindowStat
 
 WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow)
   : mInnerWindow(aWindow),
     mInnerWindowReflector(RootingCx(), aWindow->GetWrapper())
 {
   NS_PRECONDITION(aWindow, "null window");
   NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
 
-  aWindow->SuspendTimeouts();
+  aWindow->NewSuspend();
 
   // When a global goes into the bfcache, we disable script.
   xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false);
 }
 
 WindowStateHolder::~WindowStateHolder()
 {
   if (mInnerWindow) {
@@ -2547,23 +2546,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
                                                     aForceReuseInnerWindow);
   }
 
   NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows");
 
   // Bail out early if we're in process of closing down the window.
   NS_ENSURE_STATE(!mCleanedUp);
 
-  if (IsFrozen()) {
-    // This outer is now getting its first inner, thaw the outer now
-    // that it's ready and is getting an inner window.
-
-    Thaw();
-  }
-
   NS_ASSERTION(!AsOuter()->GetCurrentInnerWindow() ||
                AsOuter()->GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
                "Uh, mDoc doesn't match the current inner window "
                "document!");
 
   bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
   if (aForceReuseInnerWindow &&
       !wouldReuseInnerWindow &&
@@ -2644,17 +2636,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   bool thisChrome = IsChromeWindow();
 
   nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
   NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
 
   JS::Rooted<JSObject*> newInnerGlobal(cx);
   if (reUseInnerWindow) {
     // We're reusing the current inner window.
-    NS_ASSERTION(!currentInner->IsFrozen(),
+    NS_ASSERTION(!currentInner->NewIsFrozen(),
                  "We should never be reusing a shared inner window");
     newInnerWindow = currentInner;
     newInnerGlobal = currentInner->GetWrapperPreserveColor();
 
     if (aDocument != oldDoc) {
       JS::ExposeObjectToActiveJS(newInnerGlobal);
     }
 
@@ -2697,44 +2689,43 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       if (thisChrome) {
         newInnerWindow = nsGlobalChromeWindow::Create(this);
       } else if (mIsModalContentWindow) {
         newInnerWindow = nsGlobalModalWindow::Create(this);
       } else {
         newInnerWindow = nsGlobalWindow::Create(this);
       }
 
-      // Freeze the outer window and null out the inner window so
-      // that initializing classes on the new inner doesn't end up
-      // reaching into the old inner window for classes etc.
+      // The outer window is automatically treated as frozen when we
+      // null out the inner window. As a result, initializing classes
+      // on the new inner won't end up reaching into the old inner
+      // window for classes etc.
       //
       // [This happens with Object.prototype when XPConnect creates
       // a temporary global while initializing classes; the reason
       // being that xpconnect creates the temp global w/o a parent
       // and proto, which makes the JS engine look up classes in
       // cx->globalObject, i.e. this outer window].
 
       mInnerWindow = nullptr;
 
-      Freeze();
       mCreatingInnerWindow = true;
       // Every script context we are initialized with must create a
       // new global.
       rv = CreateNativeGlobalForInner(cx, newInnerWindow,
                                       aDocument->GetDocumentURI(),
                                       aDocument->NodePrincipal(),
                                       &newInnerGlobal,
                                       ComputeIsSecureContext(aDocument));
       NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal &&
                    newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
                    "Failed to get script global");
 
       mCreatingInnerWindow = false;
       createdInnerWindow = true;
-      Thaw();
 
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (currentInner && currentInner->GetWrapperPreserveColor()) {
       if (oldDoc == aDocument) {
         // Move the navigator from the old inner window to the new one since
         // this is a document.write. This is safe from a same-origin point of
@@ -2755,17 +2746,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
                                              currentInner->mPerformance->GetDOMTiming(),
                                              currentInner->mPerformance->GetChannel(),
                                              currentInner->mPerformance->GetParentPerformance());
         }
       }
 
       // Don't free objects on our current inner window if it's going to be
       // held in the bfcache.
-      if (!currentInner->IsFrozen()) {
+      if (!currentInner->NewIsFrozen()) {
         currentInner->FreeInnerObjects();
       }
     }
 
     mInnerWindow = newInnerWindow->AsInner();
 
     if (!GetWrapperPreserveColor()) {
       JS::Rooted<JSObject*> outer(cx,
@@ -2855,23 +2846,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
     JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
     JS::Rooted<JSObject*> proto1(cx), proto2(cx);
     JS_GetPrototype(cx, rootedJSObject, &proto1);
     JS_GetPrototype(cx, newInnerGlobal, &proto2);
     NS_ASSERTION(proto1 == proto2,
                  "outer and inner globals should have the same prototype");
 #endif
 
-    nsCOMPtr<Element> frame = AsOuter()->GetFrameElementInternal();
-    if (frame) {
-      nsPIDOMWindowOuter* parentWindow = frame->OwnerDoc()->GetWindow();
-      if (parentWindow && parentWindow->TimeoutSuspendCount()) {
-        SuspendTimeouts(parentWindow->TimeoutSuspendCount());
-      }
-    }
+    mInnerWindow->NewSyncStateFromParentWindow();
   }
 
   // Add an extra ref in case we release mContext during GC.
   nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
 
   aDocument->SetScriptGlobalObject(newInnerWindow);
 
   if (!aState) {
@@ -8847,25 +8832,25 @@ nsGlobalWindow::EnterModalState()
 void
 nsGlobalWindow::RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow,
                                             nsGlobalWindow *aWindow)
 {
   nsGlobalWindow *inner;
 
   // Return early if we're frozen or have no inner window.
   if (!(inner = aWindow->GetCurrentInnerWindowInternal()) ||
-      inner->IsFrozen()) {
+      inner->NewIsFrozen()) {
     return;
   }
 
   inner->RunTimeout(nullptr);
 
   // Check again if we're frozen since running pending timeouts
   // could've frozen us.
-  if (inner->IsFrozen()) {
+  if (inner->NewIsFrozen()) {
     return;
   }
 
   nsCOMPtr<nsIDOMWindowCollection> frames = aWindow->GetFrames();
   if (!frames) {
     return;
   }
 
@@ -10316,17 +10301,17 @@ nsGlobalWindow::DispatchAsyncHashchange(
 
 nsresult
 nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
                                const nsAString &aNewURL)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   // Don't do anything if the window is frozen.
-  if (IsFrozen()) {
+  if (NewIsFrozen()) {
     return NS_OK;
   }
 
   // Get a presentation shell for use in creating the hashchange event.
   NS_ENSURE_STATE(AsInner()->IsCurrentInnerWindow());
 
   nsIPresShell *shell = mDoc->GetShell();
   RefPtr<nsPresContext> presContext;
@@ -10355,17 +10340,17 @@ nsGlobalWindow::DispatchSyncPopState()
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
                "Must be safe to run script here.");
 
   nsresult rv = NS_OK;
 
   // Bail if the window is frozen.
-  if (IsFrozen()) {
+  if (NewIsFrozen()) {
     return NS_OK;
   }
 
   // Get the document's pending state object -- it contains the data we're
   // going to send along with the popstate event.  The object is serialized
   // using structured clone.
   nsCOMPtr<nsIVariant> stateObj;
   rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
@@ -11467,38 +11452,38 @@ nsGlobalWindow::UnregisterIdleObserver(n
   return NS_OK;
 }
 
 nsresult
 nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
                         const char16_t* aData)
 {
   if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
-    if (!IsFrozen()) {
+    if (!NewIsFrozen()) {
         // Fires an offline status event if the offline status has changed
         FireOfflineStatusEventIfChanged();
     }
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE)) {
     mCurrentlyIdle = true;
-    if (IsFrozen()) {
+    if (NewIsFrozen()) {
       // need to fire only one idle event while the window is frozen.
       mNotifyIdleObserversIdleOnThaw = true;
       mNotifyIdleObserversActiveOnThaw = false;
     } else if (AsInner()->IsCurrentInnerWindow()) {
       HandleIdleActiveEvent();
     }
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) {
     mCurrentlyIdle = false;
-    if (IsFrozen()) {
+    if (NewIsFrozen()) {
       mNotifyIdleObserversActiveOnThaw = true;
       mNotifyIdleObserversIdleOnThaw = false;
     } else if (AsInner()->IsCurrentInnerWindow()) {
       MOZ_ASSERT(IsInnerWindow());
       ScheduleActiveTimerCallback();
     }
     return NS_OK;
   }
@@ -11607,17 +11592,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
 
     newEvent->SetTrusted(true);
 
     if (fireMozStorageChanged) {
       WidgetEvent* internalEvent = newEvent->WidgetEventPtr();
       internalEvent->mFlags.mOnlyChromeDispatch = true;
     }
 
-    if (IsFrozen()) {
+    if (NewIsFrozen()) {
       // This window is frozen, rather than firing the events here,
       // store the domain in which the change happened and fire the
       // events if we're ever thawed.
 
       mPendingStorageEvents.AppendElement(newEvent);
       return NS_OK;
     }
 
@@ -12525,46 +12510,48 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
   if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
     // Don't allow timeouts less than DOMMinTimeoutValue() from
     // now...
     realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
   }
 
   TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
 
-  if (!IsFrozen() && !mTimeoutsSuspendDepth) {
-    // If we're not currently frozen, then we set timeout->mWhen to be the
-    // actual firing time of the timer (i.e., now + delta). We also actually
-    // create a timer and fire it off.
-
+  if (NewIsFrozen()) {
+    // If we are frozen simply set timeout->mTimeRemaining to be the
+    // "time remaining" in the timeout (i.e., the interval itself).  This
+    // will be used to create a new mWhen time when the window is thawed.
+    // The end effect is that time does not appear to pass for frozen windows.
+    timeout->mTimeRemaining = delta;
+  } else {
+    // Since we are not frozen we must set a precise mWhen target wakeup
+    // time.  Even if we are suspended we want to use this target time so
+    // that it appears time passes while suspended.
     timeout->mWhen = TimeStamp::Now() + delta;
+  }
+
+  // If we're not suspended, then set the timer.
+  if (!NewIsSuspended()) {
+    MOZ_ASSERT(!timeout->mWhen.IsNull());
 
     nsresult rv;
     timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     RefPtr<nsTimeout> copy = timeout;
 
     rv = timeout->InitTimer(realInterval);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // The timeout is now also held in the timer's closure.
     Unused << copy.forget();
-  } else {
-    // If we are frozen, however, then we instead simply set
-    // timeout->mTimeRemaining to be the "time remaining" in the timeout (i.e.,
-    // the interval itself). We don't create a timer for it, since that will
-    // happen when we are thawed and the timeout will then get a timer and run
-    // to completion.
-
-    timeout->mTimeRemaining = delta;
   }
 
   timeout->mWindow = this;
 
   if (!aIsInterval) {
     timeout->mNestingLevel = nestingLevel;
   }
 
@@ -12800,17 +12787,17 @@ nsGlobalWindow::RescheduleTimeout(nsTime
   // And make sure delay is nonnegative; that might happen if the timer
   // thread is firing our timers somewhat early or if they're taking a long
   // time to run the callback.
   if (delay < TimeDuration(0)) {
     delay = TimeDuration(0);
   }
 
   if (!aTimeout->mTimer) {
-    NS_ASSERTION(IsFrozen() || mTimeoutsSuspendDepth,
+    NS_ASSERTION(NewIsFrozen() || NewIsSuspended(),
                  "How'd our timer end up null if we're not frozen or "
                  "suspended?");
 
     aTimeout->mTimeRemaining = delay;
     return true;
   }
 
   aTimeout->mWhen = currentNow + delay;
@@ -12841,22 +12828,22 @@ nsGlobalWindow::RescheduleTimeout(nsTime
   return true;
 }
 
 void
 nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
 {
   // If a modal dialog is open for this window, return early. Pending
   // timeouts will run when the modal dialog is dismissed.
-  if (IsInModalState() || mTimeoutsSuspendDepth) {
+  if (IsInModalState() || NewIsSuspended()) {
     return;
   }
 
   NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
-  NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");
+  NS_ASSERTION(!NewIsFrozen(), "Timeout running on a window in the bfcache!");
 
   nsTimeout *nextTimeout;
   nsTimeout *last_expired_timeout, *last_insertion_point;
   uint32_t firingDepth = mTimeoutFiringDepth + 1;
 
   // Make sure that the window and the script context don't go away as
   // a result of running timeouts
   nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(this);
@@ -12922,28 +12909,28 @@ nsGlobalWindow::RunTimeout(nsTimeout *aT
   RefPtr<nsTimeout> timeoutExtraRef(dummy_timeout);
 
   last_insertion_point = mTimeoutInsertionPoint;
   // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
   // the logic in ResetTimersForNonBackgroundWindow will need to change.
   mTimeoutInsertionPoint = dummy_timeout;
 
   for (nsTimeout *timeout = mTimeouts.getFirst();
-       timeout != dummy_timeout && !IsFrozen();
+       timeout != dummy_timeout && !NewIsFrozen();
        timeout = nextTimeout) {
     nextTimeout = timeout->getNext();
 
     if (timeout->mFiringDepth != firingDepth) {
       // We skip the timeout since it's on the list to run at another
       // depth.
 
       continue;
     }
 
-    if (mTimeoutsSuspendDepth) {
+    if (NewIsSuspended()) {
       // Some timer did suspend us. Make sure the
       // rest of the timers get executed later.
       timeout->mFiringDepth = 0;
       continue;
     }
 
     // The timeout is on the list to run at this depth, go ahead and
     // process it.
@@ -13034,17 +13021,17 @@ nsGlobalWindow::ClearTimeoutOrInterval(i
   }
 }
 
 nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
 {
   FORWARD_TO_INNER(ResetTimersForNonBackgroundWindow, (),
                    NS_ERROR_NOT_INITIALIZED);
 
-  if (IsFrozen() || mTimeoutsSuspendDepth) {
+  if (NewIsFrozen() || NewIsSuspended()) {
     return NS_OK;
   }
 
   TimeStamp now = TimeStamp::Now();
 
   // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
   // timers and the timers we're planning to fire all come before
   // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
@@ -13173,17 +13160,17 @@ nsGlobalWindow::InsertTimeoutIntoList(ns
   // Start at mLastTimeout and go backwards.  Don't go further than
   // mTimeoutInsertionPoint, though.  This optimizes for the common case of
   // insertion at the end.
   nsTimeout* prevSibling;
   for (prevSibling = mTimeouts.getLast();
        prevSibling && prevSibling != mTimeoutInsertionPoint &&
          // This condition needs to match the one in SetTimeoutOrInterval that
          // determines whether to set mWhen or mTimeRemaining.
-         ((IsFrozen() || mTimeoutsSuspendDepth) ?
+         (NewIsFrozen() ?
           prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
           prevSibling->mWhen > aTimeout->mWhen);
        prevSibling = prevSibling->getPrevious()) {
     /* Do nothing; just searching */
   }
 
   // Now link in aTimeout after prevSibling.
   if (prevSibling) {
@@ -13364,17 +13351,17 @@ nsGlobalWindow::SaveWindowState()
   nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
   NS_ASSERTION(inner, "No inner window to save");
 
   // Don't do anything else to this inner window! After this point, all
   // calls to SetTimeoutOrInterval will create entries in the timeout
   // list that will only run after this window has come out of the bfcache.
   // Also, while we're frozen, we won't dispatch online/offline events
   // to the page.
-  inner->Freeze();
+  inner->NewFreeze();
 
   nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
 
 #ifdef DEBUG_PAGE_CACHE
   printf("saving window state, state = %p\n", (void*)state);
 #endif
 
   return state.forget();
@@ -13407,17 +13394,17 @@ nsGlobalWindow::RestoreWindowState(nsISu
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm) {
       nsCOMPtr<nsIDOMElement> focusedElement(do_QueryInterface(focusedNode));
       fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL |
                                    nsIFocusManager::FLAG_SHOWRING);
     }
   }
 
-  inner->Thaw();
+  inner->NewThaw();
 
   holder->DidRestoreWindow();
 
   return NS_OK;
 }
 
 void
 nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
@@ -13494,17 +13481,17 @@ nsGlobalWindow::SuspendTimeouts(uint32_t
         nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
         if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
           continue;
         }
 
         win->SuspendTimeouts(aIncrease, aFreezeChildren, aFreezeWorkers);
 
         if (inner && aFreezeChildren) {
-          inner->Freeze();
+          inner->NewFreeze();
         }
       }
     }
   }
 }
 
 nsresult
 nsGlobalWindow::ResumeTimeouts(bool aThawChildren, bool aThawWorkers)
@@ -13605,17 +13592,17 @@ nsGlobalWindow::ResumeTimeouts(bool aTha
         // This is a bit hackish. Only thaw/resume windows which are truly our
         // subwindows.
         nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
         if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
           continue;
         }
 
         if (inner && aThawChildren) {
-          inner->Thaw();
+          inner->NewThaw();
         }
 
         rv = win->ResumeTimeouts(aThawChildren, aThawWorkers);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
 
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -275,17 +275,17 @@ CollectWindowReports(nsGlobalWindow *aWi
 
   if (top) {
     windowPath += NS_LITERAL_CSTRING("top(");
     AppendWindowURI(top, windowPath, aAnonymize);
     windowPath.AppendPrintf(", id=%llu)", top->WindowID());
 
     aTopWindowPaths->Put(aWindow->WindowID(), windowPath);
 
-    windowPath += aWindow->IsFrozen() ? NS_LITERAL_CSTRING("/cached/")
+    windowPath += aWindow->NewIsFrozen() ? NS_LITERAL_CSTRING("/cached/")
                                       : NS_LITERAL_CSTRING("/active/");
   } else {
     if (aGhostWindowIDs->Contains(aWindow->WindowID())) {
       windowPath += NS_LITERAL_CSTRING("top(none)/ghost/");
     } else {
       windowPath += NS_LITERAL_CSTRING("top(none)/detached/");
     }
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2727,17 +2727,17 @@ IsNonExposedGlobal(JSContext* aCx, JSObj
 
   return false;
 }
 
 void
 HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow)
 {
   // Suspend the window and its workers, and its children too.
-  aWindow->SuspendTimeouts();
+  aWindow->NewFreeze();
 
   // Suspend event handling on the document
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (doc) {
     doc->SuppressEventHandling(nsIDocument::eEvents);
   }
 }
 
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -1999,17 +1999,17 @@ BackgroundDatabaseChild::RecvVersionChan
     return true;
   }
 
   RefPtr<IDBDatabase> kungFuDeathGrip = mDatabase;
 
   // Handle bfcache'd windows.
   if (nsPIDOMWindowInner* owner = kungFuDeathGrip->GetOwner()) {
     // The database must be closed if the window is already frozen.
-    bool shouldAbortAndClose = owner->IsFrozen();
+    bool shouldAbortAndClose = owner->NewIsFrozen();
 
     // Anything in the bfcache has to be evicted and then we have to close the
     // database also.
     if (nsCOMPtr<nsIDocument> doc = owner->GetExtantDoc()) {
       if (nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry()) {
         bfCacheEntry->RemoveFromBFCacheSync();
         shouldAbortAndClose = true;
       }
--- a/dom/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/offline/nsDOMOfflineResourceList.cpp
@@ -553,17 +553,17 @@ nsDOMOfflineResourceList::SendEvent(cons
   RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
   event->InitEvent(aEventName, false, true);
 
   // We assume anyone that managed to call SendEvent is trusted
   event->SetTrusted(true);
 
   // If the window is frozen or we're still catching up on events that were
   // queued while frozen, save the event for later.
-  if (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) {
+  if (GetOwner()->NewIsFrozen() || mPendingEvents.Count() > 0) {
     mPendingEvents.AppendObject(event);
     return NS_OK;
   }
 
   bool dummy;
   DispatchEvent(event, &dummy);
 
   return NS_OK;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2207,16 +2207,20 @@ WorkerPrivateParent<Derived>::WorkerPriv
     // And manually set our mIsSecureContext, though it's not really relevant to
     // dedicated workers...
     mIsSecureContext = aParent->IsSecureContext();
     MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
 
     MOZ_ASSERT(IsDedicatedWorker());
     mNowBaseTimeStamp = aParent->NowBaseTimeStamp();
     mNowBaseTimeHighRes = aParent->NowBaseTime();
+
+    if (aParent->mParentFrozen) {
+      Freeze(nullptr);
+    }
   }
   else {
     AssertIsOnMainThread();
 
     RuntimeService::GetDefaultJSSettings(mJSSettings);
 
     // Our secure context state depends on the kind of worker we have.
     if (UsesSystemPrincipal() || IsServiceWorker()) {
@@ -2244,16 +2248,26 @@ WorkerPrivateParent<Derived>::WorkerPriv
         GetNavigationStartTimeStamp();
       mNowBaseTimeHighRes =
       mLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
         GetNavigationStartHighRes();
     } else {
       mNowBaseTimeStamp = CreationTimeStamp();
       mNowBaseTimeHighRes = CreationTime();
     }
+
+    // Our parent can get suspended after it initiates the async creation
+    // of a new worker thread.  In this case suspend the new worker as well.
+    if (mLoadInfo.mWindow && mLoadInfo.mWindow->NewIsSuspended()) {
+      ParentWindowPaused();
+    }
+
+    if (mLoadInfo.mWindow && mLoadInfo.mWindow->NewIsFrozen()) {
+      Freeze(mLoadInfo.mWindow);
+    }
   }
 }
 
 template <class Derived>
 WorkerPrivateParent<Derived>::~WorkerPrivateParent()
 {
   DropJSObjects(this);
 }
@@ -2605,22 +2619,17 @@ WorkerPrivateParent<Derived>::Freeze(nsP
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Thaw(nsPIDOMWindowInner* aWindow)
 {
   AssertIsOnParentThread();
 
-  if (IsDedicatedWorker() && !mParentFrozen) {
-    // If we are in here, it means that this worker has been created when the
-    // parent was actually suspended (maybe during a sync XHR), and in this case
-    // we don't need to thaw.
-    return true;
-  }
+  MOZ_ASSERT(mParentFrozen);
 
   // Shared workers are resumed if any of their owning documents are thawed.
   // It can happen that mSharedWorkers is empty but this thread has not been
   // unregistered yet.
   if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
     AssertIsOnMainThread();
 
     bool anyRunning = false;
@@ -3115,17 +3124,17 @@ WorkerPrivateParent<Derived>::RegisterSh
       return false;
     }
   }
 
   mSharedWorkers.AppendElement(aSharedWorker);
 
   // If there were other SharedWorker objects attached to this worker then they
   // may all have been frozen and this worker would need to be thawed.
-  if (mSharedWorkers.Length() > 1 && !Thaw(nullptr)) {
+  if (mSharedWorkers.Length() > 1 && IsFrozen() && !Thaw(nullptr)) {
     return false;
   }
 
   return true;
 }
 
 template <class Derived>
 void
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -131,17 +131,17 @@ NS_IMPL_ISUPPORTS(nsXHRParseEndListener,
 
 class nsResumeTimeoutsEvent : public Runnable
 {
 public:
   explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
 
   NS_IMETHOD Run() override
   {
-    mWindow->ResumeTimeouts(false);
+    mWindow->NewResume();
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
 };
 
 
@@ -2918,17 +2918,17 @@ XMLHttpRequestMainThread::SendInternal(c
     nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
     if (GetOwner()) {
       if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetOwner()->GetOuterWindow()->GetTop()) {
         if (nsCOMPtr<nsPIDOMWindowInner> topInner = topWindow->GetCurrentInnerWindow()) {
           suspendedDoc = topWindow->GetExtantDoc();
           if (suspendedDoc) {
             suspendedDoc->SuppressEventHandling(nsIDocument::eEvents);
           }
-          topWindow->SuspendTimeouts(1, false);
+          topInner->NewSuspend();
           resumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
         }
       }
     }
 
     StopProgressEventTimer();
 
     SyncTimeoutType syncTimeoutType = MaybeStartSyncTimeoutTimer();
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -3388,31 +3388,31 @@ nsPrintEngine::TurnScriptingOn(bool aDoT
       doc->GetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
                        &propThere);
       if (aDoTurnOn) {
         if (propThere != NS_PROPTABLE_PROP_NOT_THERE) {
           doc->DeleteProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview);
           if (go && go->GetGlobalJSObject()) {
             xpc::Scriptability::Get(go->GetGlobalJSObject()).Unblock();
           }
-          window->ResumeTimeouts(false);
+          window->NewResume();
         }
       } else {
         // Have to be careful, because people call us over and over again with
         // aDoTurnOn == false.  So don't set the property if it's already
         // set, since in that case we'd set it to the wrong value.
         if (propThere == NS_PROPTABLE_PROP_NOT_THERE) {
           // Stash the current value of IsScriptEnabled on the document, so
           // that layout code running in print preview doesn't get confused.
           doc->SetProperty(nsGkAtoms::scriptEnabledBeforePrintOrPreview,
                            NS_INT32_TO_PTR(doc->IsScriptEnabled()));
           if (go && go->GetGlobalJSObject()) {
             xpc::Scriptability::Get(go->GetGlobalJSObject()).Block();
           }
-          window->SuspendTimeouts(1, false);
+          window->NewSuspend();
         }
       }
     }
   }
 }
 
 //-----------------------------------------------------------------
 //-- Done: Misc Support Methods