Bug 1034036 - Part 6: Tests that use ss.setBrowserState() or ss.setWindowState() should wait until the window is restored to continue. r?dao draft
authorMike de Boer <mdeboer@mozilla.com>
Thu, 29 Mar 2018 19:19:40 +0200
changeset 774855 2e5ca6ae2d8230e3bfabcfee110824f91ee77af0
parent 774854 018150dd76992ea5ab032fc2cbebc7a52c5f8fc0
child 774856 17a4c9506b0d70f0e28e3e284634a0b5e9ce468d
push id104528
push usermdeboer@mozilla.com
push dateThu, 29 Mar 2018 17:20:41 +0000
reviewersdao
bugs1034036
milestone61.0a1
Bug 1034036 - Part 6: Tests that use ss.setBrowserState() or ss.setWindowState() should wait until the window is restored to continue. r?dao MozReview-Commit-ID: 4xewVHtbsfg
browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
browser/components/extensions/test/browser/head.js
browser/components/sessionstore/test/browser_423132.js
browser/components/sessionstore/test/browser_461634.js
browser/components/sessionstore/test/browser_464199.js
browser/components/sessionstore/test/browser_465223.js
browser/components/sessionstore/test/browser_477657.js
browser/components/sessionstore/test/browser_490040.js
browser/components/sessionstore/test/browser_491577.js
browser/components/sessionstore/test/browser_495495.js
browser/components/sessionstore/test/browser_514751.js
browser/components/sessionstore/test/browser_607016.js
browser/components/sessionstore/test/browser_637020.js
browser/components/sessionstore/test/browser_687710.js
browser/components/sessionstore/test/browser_694378.js
browser/components/sessionstore/test/browser_frame_history.js
browser/components/sessionstore/test/browser_merge_closed_tabs.js
browser/components/sessionstore/test/browser_remoteness_flip_on_restore.js
browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js
browser/components/sessionstore/test/browser_windowStateContainer.js
browser/components/sessionstore/test/head.js
--- a/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
@@ -9,16 +9,17 @@ const SESSION = {
       {entries: [{url: "about:blank", triggeringPrincipal_base64}]},
       {entries: [{url: "https://example.com/", triggeringPrincipal_base64}]},
     ],
   }],
 };
 
 add_task(async function() {
   SessionStore.setBrowserState(JSON.stringify(SESSION));
+  await promiseWindowRestored(window);
   const tab = gBrowser.tabs[1];
 
   is(tab.getAttribute("pending"), "true", "The tab is pending restore");
   is(tab.linkedBrowser.isConnected, false, "The tab is lazy");
 
   async function background() {
     const [tab] = await browser.tabs.query({url: "https://example.com/"});
     browser.test.assertRejects(
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -16,17 +16,17 @@
  *          openToolsMenu closeToolsMenu
  *          imageBuffer imageBufferFromDataURI
  *          getListStyleImage getPanelForNode
  *          awaitExtensionPanel awaitPopupResize
  *          promiseContentDimensions alterContent
  *          promisePrefChangeObserved openContextMenuInFrame
  *          promiseAnimationFrame getCustomizableUIPanelID
  *          awaitEvent BrowserWindowIterator
- *          navigateTab historyPushState
+ *          navigateTab historyPushState promiseWindowRestored
  */
 
 // There are shutdown issues for which multiple rejections are left uncaught.
 // This bug should be fixed, but for the moment this directory is whitelisted.
 //
 // NOTE: Entire directory whitelisting should be kept to a minimum. Normally you
 //       should use "expectUncaughtRejection" to flag individual failures.
 const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", {});
@@ -472,16 +472,20 @@ function closePageAction(extension, win 
 function promisePrefChangeObserved(pref) {
   return new Promise((resolve, reject) =>
     Preferences.observe(pref, function prefObserver() {
       Preferences.ignore(pref, prefObserver);
       resolve();
     }));
 }
 
+function promiseWindowRestored(window) {
+  return new Promise(resolve => window.addEventListener("SSWindowRestored", resolve, {once: true}));
+}
+
 function awaitEvent(eventName, id) {
   return new Promise(resolve => {
     let listener = (_eventName, ...args) => {
       let extension = args[0];
       if (_eventName === eventName &&
           extension.id == id) {
         Management.off(eventName, listener);
         resolve();
--- a/browser/components/sessionstore/test/browser_423132.js
+++ b/browser/components/sessionstore/test/browser_423132.js
@@ -31,17 +31,17 @@ add_task(async function() {
     i++;
   }
   Assert.equal(i, 1, "expected one cookie");
 
   // remove the cookie
   Services.cookies.removeAll();
 
   // restore the window state
-  ss.setBrowserState(state);
+  await setBrowserState(state);
 
   // at this point, the cookie should be restored...
   enumerator = Services.cookies.enumerator;
   let cookie2;
   while (enumerator.hasMoreElements()) {
     cookie2 = enumerator.getNext().QueryInterface(Ci.nsICookie);
     if (cookie.name == cookie2.name)
       break;
--- a/browser/components/sessionstore/test/browser_461634.js
+++ b/browser/components/sessionstore/test/browser_461634.js
@@ -1,19 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm");
 
-function test() {
+add_task(async function testClosedTabData() {
   /** Test for Bug 461634 **/
 
-  waitForExplicitFinish();
-
   const REMEMBER = Date.now(), FORGET = Math.random();
   let test_state = { windows: [{ "tabs": [{ "entries": [] }], _closedTabs: [
     { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
     { state: { entries: [{ url: "http://www.example.net/" }] }, title: REMEMBER },
     { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
     { state: { entries: [{ url: "http://www.example.net/" }] }, title: REMEMBER },
   ] }] };
   let remember_count = 2;
@@ -28,57 +26,57 @@ function test() {
       return false;
     } catch (ex) {
       return ex.name == "NS_ERROR_ILLEGAL_VALUE";
     }
   }
 
   // Open a window and add the above closed tab list.
   let newWin = openDialog(location, "", "chrome,all,dialog=no");
-  promiseWindowLoaded(newWin).then(() => {
-    Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo",
-                              test_state.windows[0]._closedTabs.length);
-    ss.setWindowState(newWin, JSON.stringify(test_state), true);
+  await promiseWindowLoaded(newWin);
 
-    let closedTabs = SessionStore.getClosedTabData(newWin, false);
+  Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo",
+                            test_state.windows[0]._closedTabs.length);
+  await setWindowState(newWin, test_state);
+
+  let closedTabs = SessionStore.getClosedTabData(newWin, false);
 
-    // Verify that non JSON serialized data is the same as JSON serialized data.
-    is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
-       "Non-serialized data is the same as serialized data");
+  // Verify that non JSON serialized data is the same as JSON serialized data.
+  is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
+     "Non-serialized data is the same as serialized data");
 
-    is(closedTabs.length, test_state.windows[0]._closedTabs.length,
-       "Closed tab list has the expected length");
-    is(countByTitle(closedTabs, FORGET),
-       test_state.windows[0]._closedTabs.length - remember_count,
-       "The correct amout of tabs are to be forgotten");
-    is(countByTitle(closedTabs, REMEMBER), remember_count,
-       "Everything is set up");
+  is(closedTabs.length, test_state.windows[0]._closedTabs.length,
+     "Closed tab list has the expected length");
+  is(countByTitle(closedTabs, FORGET),
+     test_state.windows[0]._closedTabs.length - remember_count,
+     "The correct amout of tabs are to be forgotten");
+  is(countByTitle(closedTabs, REMEMBER), remember_count,
+     "Everything is set up");
 
-    // All of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE.
-    ok(testForError(() => ss.forgetClosedTab({}, 0)),
-       "Invalid window for forgetClosedTab throws");
-    ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
-       "Invalid tab for forgetClosedTab throws");
-    ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
-       "Invalid tab for forgetClosedTab throws");
+  // All of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE.
+  ok(testForError(() => ss.forgetClosedTab({}, 0)),
+     "Invalid window for forgetClosedTab throws");
+  ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
+     "Invalid tab for forgetClosedTab throws");
+  ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
+     "Invalid tab for forgetClosedTab throws");
 
-    // Remove third tab, then first tab.
-    ss.forgetClosedTab(newWin, 2);
-    ss.forgetClosedTab(newWin, null);
+  // Remove third tab, then first tab.
+  ss.forgetClosedTab(newWin, 2);
+  ss.forgetClosedTab(newWin, null);
 
-    closedTabs = SessionStore.getClosedTabData(newWin, false);
+  closedTabs = SessionStore.getClosedTabData(newWin, false);
 
-    // Verify that non JSON serialized data is the same as JSON serialized data.
-    is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
-       "Non-serialized data is the same as serialized data");
+  // Verify that non JSON serialized data is the same as JSON serialized data.
+  is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
+     "Non-serialized data is the same as serialized data");
 
-    is(closedTabs.length, remember_count,
-       "The correct amout of tabs was removed");
-    is(countByTitle(closedTabs, FORGET), 0,
-       "All tabs specifically forgotten were indeed removed");
-    is(countByTitle(closedTabs, REMEMBER), remember_count,
-       "... and tabs not specifically forgetten weren't");
+  is(closedTabs.length, remember_count,
+     "The correct amout of tabs was removed");
+  is(countByTitle(closedTabs, FORGET), 0,
+     "All tabs specifically forgotten were indeed removed");
+  is(countByTitle(closedTabs, REMEMBER), remember_count,
+     "... and tabs not specifically forgetten weren't");
 
-    // Clean up.
-    Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
-    BrowserTestUtils.closeWindow(newWin).then(finish);
-  });
-}
+  // Clean up.
+  Services.prefs.clearUserPref("browser.sessionstore.max_tabs_undo");
+  await BrowserTestUtils.closeWindow(newWin);
+});
--- a/browser/components/sessionstore/test/browser_464199.js
+++ b/browser/components/sessionstore/test/browser_464199.js
@@ -52,16 +52,17 @@ add_task(async function() {
   }
 
   // open a window and add the above closed tab list
   let newWin = openDialog(location, "", "chrome,all,dialog=no");
   await promiseWindowLoaded(newWin);
   Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo",
                             test_state.windows[0]._closedTabs.length);
   ss.setWindowState(newWin, JSON.stringify(test_state), true);
+  await promiseWindowRestored(newWin);
 
   let closedTabs = JSON.parse(ss.getClosedTabData(newWin));
   is(closedTabs.length, test_state.windows[0]._closedTabs.length,
      "Closed tab list has the expected length");
   is(countByTitle(closedTabs, FORGET),
      test_state.windows[0]._closedTabs.length - remember_count,
      "The correct amout of tabs are to be forgotten");
   is(countByTitle(closedTabs, REMEMBER), remember_count,
--- a/browser/components/sessionstore/test/browser_465223.js
+++ b/browser/components/sessionstore/test/browser_465223.js
@@ -1,45 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-function test() {
+add_task(async function test_clearWindowValues() {
   /** Test for Bug 465223 **/
 
-  // test setup
-  waitForExplicitFinish();
-
   let uniqueKey1 = "bug 465223.1";
   let uniqueKey2 = "bug 465223.2";
   let uniqueValue1 = "unik" + Date.now();
   let uniqueValue2 = "pi != " + Math.random();
 
   // open a window and set a value on it
   let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
-  promiseWindowLoaded(newWin).then(() => {
-    ss.setWindowValue(newWin, uniqueKey1, uniqueValue1);
+  await promiseWindowLoaded(newWin);
+  ss.setWindowValue(newWin, uniqueKey1, uniqueValue1);
 
-    let newState = { windows: [{ tabs: [{ entries: [] }], extData: {} }] };
-    newState.windows[0].extData[uniqueKey2] = uniqueValue2;
-    ss.setWindowState(newWin, JSON.stringify(newState), false);
+  let newState = { windows: [{ tabs: [{ entries: [] }], extData: {} }] };
+  newState.windows[0].extData[uniqueKey2] = uniqueValue2;
+  await setWindowState(newWin, newState);
 
-    is(newWin.gBrowser.tabs.length, 2,
-       "original tab wasn't overwritten");
-    is(ss.getWindowValue(newWin, uniqueKey1), uniqueValue1,
-       "window value wasn't overwritten when the tabs weren't");
-    is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue2,
-       "new window value was correctly added");
+  is(newWin.gBrowser.tabs.length, 2,
+    "original tab wasn't overwritten");
+  is(ss.getWindowValue(newWin, uniqueKey1), uniqueValue1,
+    "window value wasn't overwritten when the tabs weren't");
+  is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue2,
+    "new window value was correctly added");
 
-    newState.windows[0].extData[uniqueKey2] = uniqueValue1;
-    ss.setWindowState(newWin, JSON.stringify(newState), true);
+  newState.windows[0].extData[uniqueKey2] = uniqueValue1;
+  await setWindowState(newWin, newState, true);
 
-    is(newWin.gBrowser.tabs.length, 1,
-       "original tabs were overwritten");
-    is(ss.getWindowValue(newWin, uniqueKey1), "",
-       "window value was cleared");
-    is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue1,
-       "window value was correctly overwritten");
+  is(newWin.gBrowser.tabs.length, 1,
+    "original tabs were overwritten");
+  is(ss.getWindowValue(newWin, uniqueKey1), "",
+    "window value was cleared");
+  is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue1,
+    "window value was correctly overwritten");
 
-    // clean up
-    BrowserTestUtils.closeWindow(newWin).then(finish);
-  });
-}
+  // clean up
+  await BrowserTestUtils.closeWindow(newWin);
+});
--- a/browser/components/sessionstore/test/browser_477657.js
+++ b/browser/components/sessionstore/test/browser_477657.js
@@ -1,60 +1,57 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-function test() {
+add_task(async function test_sizemodeDefaults() {
   /** Test for Bug 477657 **/
-  waitForExplicitFinish();
-
   let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
-  promiseWindowLoaded(newWin).then(() => {
-    let newState = { windows: [{
-      tabs: [{ entries: [] }],
-      _closedTabs: [{
-        state: { entries: [{ url: "about:" }]},
-        title: "About:"
-      }],
-      sizemode: "maximized"
-    }] };
+  await promiseWindowLoaded(newWin);
+  let newState = { windows: [{
+    tabs: [{ entries: [] }],
+    _closedTabs: [{
+      state: { entries: [{ url: "about:" }]},
+      title: "About:"
+    }],
+    sizemode: "maximized"
+  }] };
 
-    let uniqueKey = "bug 477657";
-    let uniqueValue = "unik" + Date.now();
+  let uniqueKey = "bug 477657";
+  let uniqueValue = "unik" + Date.now();
+
+  ss.setWindowValue(newWin, uniqueKey, uniqueValue);
+  is(ss.getWindowValue(newWin, uniqueKey), uniqueValue,
+     "window value was set before the window was overwritten");
 
-    ss.setWindowValue(newWin, uniqueKey, uniqueValue);
-    is(ss.getWindowValue(newWin, uniqueKey), uniqueValue,
-       "window value was set before the window was overwritten");
-    ss.setWindowState(newWin, JSON.stringify(newState), true);
+  await setWindowState(newWin, newState, true);
+  // use newWin.setTimeout(..., 0) to mirror sss_restoreWindowFeatures
+  await new Promise(resolve => newWin.setTimeout(resolve, 0));
 
-    // use newWin.setTimeout(..., 0) to mirror sss_restoreWindowFeatures
-    newWin.setTimeout(function() {
-      is(ss.getWindowValue(newWin, uniqueKey), "",
-         "window value was implicitly cleared");
+  is(ss.getWindowValue(newWin, uniqueKey), "",
+    "window value was implicitly cleared");
 
-      is(newWin.windowState, newWin.STATE_MAXIMIZED,
-         "the window was maximized");
+  is(newWin.windowState, newWin.STATE_MAXIMIZED,
+    "the window was maximized");
 
-      is(JSON.parse(ss.getClosedTabData(newWin)).length, 1,
-         "the closed tab was added before the window was overwritten");
-      delete newState.windows[0]._closedTabs;
-      delete newState.windows[0].sizemode;
-      ss.setWindowState(newWin, JSON.stringify(newState), true);
+  is(JSON.parse(ss.getClosedTabData(newWin)).length, 1,
+    "the closed tab was added before the window was overwritten");
+  delete newState.windows[0]._closedTabs;
+  delete newState.windows[0].sizemode;
 
-      newWin.setTimeout(function() {
-        is(JSON.parse(ss.getClosedTabData(newWin)).length, 0,
-           "closed tabs were implicitly cleared");
+  await setWindowState(newWin, newState, true);
+  await new Promise(resolve => newWin.setTimeout(resolve, 0));
 
-        is(newWin.windowState, newWin.STATE_MAXIMIZED,
-           "the window remains maximized");
-        newState.windows[0].sizemode = "normal";
-        ss.setWindowState(newWin, JSON.stringify(newState), true);
+  is(JSON.parse(ss.getClosedTabData(newWin)).length, 0,
+    "closed tabs were implicitly cleared");
+
+  is(newWin.windowState, newWin.STATE_MAXIMIZED,
+    "the window remains maximized");
+  newState.windows[0].sizemode = "normal";
 
-        newWin.setTimeout(function() {
-          isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
-                "the window was explicitly unmaximized");
+  await setWindowState(newWin, newState, true);
+  await new Promise(resolve => newWin.setTimeout(resolve, 0));
 
-          BrowserTestUtils.closeWindow(newWin).then(finish);
-        }, 0);
-      }, 0);
-    }, 0);
-  });
-}
+  isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
+    "the window was explicitly unmaximized");
+
+  await BrowserTestUtils.closeWindow(newWin);
+});
--- a/browser/components/sessionstore/test/browser_490040.js
+++ b/browser/components/sessionstore/test/browser_490040.js
@@ -45,17 +45,17 @@ add_task(async function test_bug_490040(
     // Ensure we can store the window if needed.
     let startingClosedWindowCount = ss.getClosedWindowCount();
     await pushPrefs(["browser.sessionstore.max_windows_undo",
                      startingClosedWindowCount + 1]);
 
     let curClosedWindowCount = ss.getClosedWindowCount();
     let win = await BrowserTestUtils.openNewBrowserWindow();
 
-    ss.setWindowState(win, JSON.stringify(state.windowState), true);
+    await setWindowState(win, state.windowState, true);
     if (state.windowState.windows[0].tabs.length) {
       await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
     }
 
     await BrowserTestUtils.closeWindow(win);
 
     is(ss.getClosedWindowCount(),
        curClosedWindowCount + (state.shouldBeAdded ? 1 : 0),
--- a/browser/components/sessionstore/test/browser_491577.js
+++ b/browser/components/sessionstore/test/browser_491577.js
@@ -1,18 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-function test() {
+add_task(async function test_deleteClosedWindow() {
   /** Test for Bug 491577 **/
 
-  // test setup
-  waitForExplicitFinish();
-
   const REMEMBER = Date.now(), FORGET = Math.random();
   let test_state = {
     windows: [ { tabs: [{ entries: [{ url: "http://example.com/", triggeringPrincipal_base64 }] }], selected: 1 } ],
     _closedWindows: [
       // _closedWindows[0]
       {
         tabs: [
           { entries: [{ url: "http://example.com/", triggeringPrincipal_base64, title: "title" }] },
@@ -75,45 +72,44 @@ function test() {
       return false;
     } catch (ex) {
       return ex.name == "NS_ERROR_ILLEGAL_VALUE";
     }
   }
 
   // open a window and add the above closed window list
   let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
-  promiseWindowLoaded(newWin).then(() => {
-    Services.prefs.setIntPref("browser.sessionstore.max_windows_undo",
-                              test_state._closedWindows.length);
-    ss.setWindowState(newWin, JSON.stringify(test_state), true);
+  await promiseWindowLoaded(newWin);
+  Services.prefs.setIntPref("browser.sessionstore.max_windows_undo",
+                            test_state._closedWindows.length);
+  await setWindowState(newWin, test_state, true);
 
-    let closedWindows = JSON.parse(ss.getClosedWindowData());
-    is(closedWindows.length, test_state._closedWindows.length,
-       "Closed window list has the expected length");
-    is(countByTitle(closedWindows, FORGET),
-       test_state._closedWindows.length - remember_count,
-       "The correct amount of windows are to be forgotten");
-    is(countByTitle(closedWindows, REMEMBER), remember_count,
-       "Everything is set up.");
+  let closedWindows = JSON.parse(ss.getClosedWindowData());
+  is(closedWindows.length, test_state._closedWindows.length,
+     "Closed window list has the expected length");
+  is(countByTitle(closedWindows, FORGET),
+     test_state._closedWindows.length - remember_count,
+     "The correct amount of windows are to be forgotten");
+  is(countByTitle(closedWindows, REMEMBER), remember_count,
+     "Everything is set up.");
 
-    // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
-    ok(testForError(() => ss.forgetClosedWindow(-1)),
-       "Invalid window for forgetClosedWindow throws");
-    ok(testForError(() => ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
-       "Invalid window for forgetClosedWindow throws");
+  // all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
+  ok(testForError(() => ss.forgetClosedWindow(-1)),
+     "Invalid window for forgetClosedWindow throws");
+  ok(testForError(() => ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
+     "Invalid window for forgetClosedWindow throws");
 
-    // Remove third window, then first window
-    ss.forgetClosedWindow(2);
-    ss.forgetClosedWindow(null);
+  // Remove third window, then first window
+  ss.forgetClosedWindow(2);
+  ss.forgetClosedWindow(null);
 
-    closedWindows = JSON.parse(ss.getClosedWindowData());
-    is(closedWindows.length, remember_count,
-       "The correct amount of windows were removed");
-    is(countByTitle(closedWindows, FORGET), 0,
-       "All windows specifically forgotten were indeed removed");
-    is(countByTitle(closedWindows, REMEMBER), remember_count,
-       "... and windows not specifically forgetten weren't.");
+  closedWindows = JSON.parse(ss.getClosedWindowData());
+  is(closedWindows.length, remember_count,
+     "The correct amount of windows were removed");
+  is(countByTitle(closedWindows, FORGET), 0,
+     "All windows specifically forgotten were indeed removed");
+  is(countByTitle(closedWindows, REMEMBER), remember_count,
+     "... and windows not specifically forgetten weren't.");
 
-    // clean up
-    Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
-    BrowserTestUtils.closeWindow(newWin).then(finish);
-  });
-}
+  // clean up
+  Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
+  await BrowserTestUtils.closeWindow(newWin);
+});
--- a/browser/components/sessionstore/test/browser_495495.js
+++ b/browser/components/sessionstore/test/browser_495495.js
@@ -1,42 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-function test() {
+add_task(async function test_urlbarFocus() {
   /** Test for Bug 495495 **/
 
-  waitForExplicitFinish();
-
   let newWin = openDialog(location, "_blank", "chrome,all,dialog=no,toolbar=yes");
-  promiseWindowLoaded(newWin).then(() => {
-    let state1 = ss.getWindowState(newWin);
-    BrowserTestUtils.closeWindow(newWin).then(() => {
+  await promiseWindowLoaded(newWin);
+  let state1 = ss.getWindowState(newWin);
+  await BrowserTestUtils.closeWindow(newWin);
 
-      newWin = openDialog(location, "_blank",
-        "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
-      promiseWindowLoaded(newWin).then(() => {
-        let state2 = ss.getWindowState(newWin);
+  newWin = openDialog(location, "_blank",
+    "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
+  await promiseWindowLoaded(newWin);
+  let state2 = ss.getWindowState(newWin);
 
-        function testState(state, expected, callback) {
-          let win = openDialog(location, "_blank", "chrome,all,dialog=no");
-          promiseWindowLoaded(win).then(() => {
+  async function testState(state, expected) {
+    let win = openDialog(location, "_blank", "chrome,all,dialog=no");
+    await promiseWindowLoaded(win);
 
-            is(win.gURLBar.readOnly, false,
-               "URL bar should not be read-only before setting the state");
-            ss.setWindowState(win, state, true);
-            is(win.gURLBar.readOnly, expected.readOnly,
-               "URL bar read-only state should be restored correctly");
-
-            BrowserTestUtils.closeWindow(win).then(callback);
-          });
-        }
+    is(win.gURLBar.readOnly, false,
+       "URL bar should not be read-only before setting the state");
+    await setWindowState(win, state, true);
+    is(win.gURLBar.readOnly, expected.readOnly,
+       "URL bar read-only state should be restored correctly");
 
-        BrowserTestUtils.closeWindow(newWin).then(() => {
-          testState(state1, {readOnly: false}, function() {
-            testState(state2, {readOnly: true}, finish);
-          });
-        });
-      });
-    });
-  });
-}
+    await BrowserTestUtils.closeWindow(win);
+  }
+
+  await BrowserTestUtils.closeWindow(newWin);
+  await testState(state1, {readOnly: false});
+  await testState(state2, {readOnly: true});
+});
--- a/browser/components/sessionstore/test/browser_514751.js
+++ b/browser/components/sessionstore/test/browser_514751.js
@@ -1,36 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-function test() {
+add_task(async function test_malformedURI() {
   /** Test for Bug 514751 (Wallpaper) **/
 
-  waitForExplicitFinish();
-
   let state = {
     windows: [{
       tabs: [{
         entries: [
           { url: "about:mozilla", triggeringPrincipal_base64, title: "Mozilla" },
           {}
         ]
       }]
     }]
   };
 
   var theWin = openDialog(location, "", "chrome,all,dialog=no");
-  theWin.addEventListener("load", function() {
-    executeSoon(function() {
-      var gotError = false;
-      try {
-        ss.setWindowState(theWin, JSON.stringify(state), true);
-      } catch (e) {
-        if (/NS_ERROR_MALFORMED_URI/.test(e))
-          gotError = true;
-      }
-      ok(!gotError, "Didn't get a malformed URI error.");
-      BrowserTestUtils.closeWindow(theWin).then(finish);
-    });
-  }, {once: true});
-}
+  await promiseWindowLoaded(theWin);
 
+  var gotError = false;
+  try {
+    await setWindowState(theWin, state, true);
+  } catch (e) {
+    if (/NS_ERROR_MALFORMED_URI/.test(e))
+      gotError = true;
+  }
+
+  ok(!gotError, "Didn't get a malformed URI error.");
+  await BrowserTestUtils.closeWindow(theWin);
+});
--- a/browser/components/sessionstore/test/browser_607016.js
+++ b/browser/components/sessionstore/test/browser_607016.js
@@ -77,17 +77,17 @@ add_task(async function() {
        "(creating) new data is stored in extData where there was none");
 
     while (gBrowser.tabs.length > 1) {
       BrowserTestUtils.removeTab(gBrowser.tabs[1]);
     }
   }
 
   // Set the test state.
-  ss.setBrowserState(JSON.stringify(state));
+  await setBrowserState(state);
 
   // Wait until the selected tab is restored and all others are pending.
   await Promise.all(Array.map(gBrowser.tabs, tab => {
     return (tab == gBrowser.selectedTab) ?
       promiseTabRestored(tab) : promiseTabRestoring(tab);
   }));
 
   // Kick off the actual tests.
--- a/browser/components/sessionstore/test/browser_637020.js
+++ b/browser/components/sessionstore/test/browser_637020.js
@@ -37,16 +37,17 @@ add_task(async function test() {
     }, "domwindowopened");
   });
 
   // Set the new browser state that will
   // restore a window with two slowly loading tabs.
   let backupState = SessionStore.getBrowserState();
   SessionStore.setBrowserState(JSON.stringify(TEST_STATE));
   let win = await promiseWindow;
+  await promiseWindowRestored(win);
 
   // The window has now been opened. Check the state that is returned,
   // this should come from the cache while the window isn't restored, yet.
   info("the window has been opened");
   checkWindows();
 
   // The history has now been restored and the tabs are loading. The data must
   // now come from the window, if it's correctly been marked as dirty before.
--- a/browser/components/sessionstore/test/browser_687710.js
+++ b/browser/components/sessionstore/test/browser_687710.js
@@ -32,17 +32,17 @@ var state = {windows: [{tabs: [{entries:
         docIdentifier: 1,
         url: "http://example.com",
         triggeringPrincipal_base64,
       }
     ]
   }
 ]}]}]};
 
-function test() {
+add_task(async function test() {
   registerCleanupFunction(function() {
     ss.setBrowserState(stateBackup);
   });
 
   /* This test fails by hanging. */
-  ss.setBrowserState(JSON.stringify(state));
+  await setBrowserState(state);
   ok(true, "Didn't hang!");
-}
+});
--- a/browser/components/sessionstore/test/browser_694378.js
+++ b/browser/components/sessionstore/test/browser_694378.js
@@ -1,32 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test Summary:
 // 1.  call ss.setWindowState with a broken state
 // 1a. ensure that it doesn't throw.
 
-function test() {
-  waitForExplicitFinish();
-
+add_task(async function test_brokenWindowState() {
   let brokenState = {
     windows: [
       { tabs: [{ entries: [{ url: "about:mozilla", triggeringPrincipal_base64 }] }] }
     ],
     selectedWindow: 2
   };
-  let brokenStateString = JSON.stringify(brokenState);
 
   let gotError = false;
   try {
-    ss.setWindowState(window, brokenStateString, true);
+    await setWindowState(window, brokenState, true);
   } catch (ex) {
     gotError = true;
     info(ex);
   }
 
   ok(!gotError, "ss.setWindowState did not throw an error");
 
   // Make sure that we reset the state. Use a full state just in case things get crazy.
   let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }]}]};
-  waitForBrowserState(blankState, finish);
-}
+  await promiseBrowserState(blankState);
+});
--- a/browser/components/sessionstore/test/browser_frame_history.js
+++ b/browser/components/sessionstore/test/browser_frame_history.js
@@ -96,24 +96,24 @@ add_task(async function() {
        "frame " + i + " has the right url");
   }
   gBrowser.removeTab(newTab);
 });
 
 // Now, test that we don't record history if the iframe is added dynamically
 add_task(async function() {
   // Start with an empty history
-    let blankState = JSON.stringify({
-      windows: [{
-        tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }],
-        _closedTabs: []
-      }],
-      _closedWindows: []
-    });
-    ss.setBrowserState(blankState);
+  let blankState = JSON.stringify({
+    windows: [{
+      tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }],
+      _closedTabs: []
+    }],
+    _closedWindows: []
+  });
+  await setBrowserState(blankState);
 
   let testURL = getRootDirectory(gTestPath) + "browser_frame_history_index_blank.html";
   let tab = BrowserTestUtils.addTab(gBrowser, testURL);
   gBrowser.selectedTab = tab;
   await waitForLoadsInBrowser(tab.linkedBrowser, 1);
 
   info("dynamic: Opening a page with an iframe containing three frames, 4 dynamic loads should take place");
   let doc = tab.linkedBrowser.contentDocument;
--- a/browser/components/sessionstore/test/browser_merge_closed_tabs.js
+++ b/browser/components/sessionstore/test/browser_merge_closed_tabs.js
@@ -31,22 +31,22 @@ add_task(async function() {
     }]
   };
 
   const maxTabsUndo = 4;
   Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", maxTabsUndo);
 
   // Open a new window and restore it to an initial state.
   let win = await promiseNewWindowLoaded({private: false});
-  SessionStore.setWindowState(win, JSON.stringify(initialState), true);
+  await setWindowState(win, initialState, true);
   is(SessionStore.getClosedTabCount(win), 2, "2 closed tabs after restoring initial state");
 
   // Restore the new state but do not overwrite existing tabs (this should
   // cause the closed tabs to be merged).
-  SessionStore.setWindowState(win, JSON.stringify(restoreState), false);
+  await setWindowState(win, restoreState);
 
   // Verify the windows closed tab data is correct.
   let iClosed = initialState.windows[0]._closedTabs;
   let rClosed = restoreState.windows[0]._closedTabs;
   let cData = JSON.parse(SessionStore.getClosedTabData(win));
 
   is(cData.length, Math.min(iClosed.length + rClosed.length, maxTabsUndo),
      "Number of closed tabs is correct");
--- a/browser/components/sessionstore/test/browser_remoteness_flip_on_restore.js
+++ b/browser/components/sessionstore/test/browser_remoteness_flip_on_restore.js
@@ -129,17 +129,17 @@ async function runScenarios(scenarios) {
     if (tabbrowser.selectedTab != tabToSelect) {
       await BrowserTestUtils.switchTab(tabbrowser, tabToSelect);
     }
 
     // Okay, time to test!
     let state = prepareState(scenario.stateToRestore,
                              scenario.selectedTab);
 
-    SessionStore.setWindowState(win, state, true);
+    await setWindowState(win, state, true);
 
     for (let i = 0; i < scenario.expectedRemoteness.length; ++i) {
       let expectedRemoteness = scenario.expectedRemoteness[i];
       let tab = tabbrowser.tabs[i];
 
       Assert.equal(tab.linkedBrowser.isRemoteBrowser, expectedRemoteness,
                    "Should have gotten the expected remoteness " +
                    `for the tab at index ${i}`);
--- a/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js
+++ b/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js
@@ -129,17 +129,17 @@ add_task(async function run_test() {
 
   // Clear cookies.
   Services.cookies.removeAll();
 
   // Open a new window.
   let win = await promiseNewWindowLoaded();
 
   // Restore window with session cookies that have no originAttributes.
-  ss.setWindowState(win, SESSION_DATA, true);
+  await setWindowState(win, SESSION_DATA, true);
 
   let enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
   let cookie;
   let cookieCount = 0;
   while (enumerator.hasMoreElements()) {
     cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
     cookieCount++;
   }
@@ -149,17 +149,17 @@ add_task(async function run_test() {
   is(cookie.name, COOKIE.name, "cookie name successfully restored");
   is(cookie.value, COOKIE.value, "cookie value successfully restored");
   is(cookie.path, COOKIE.path, "cookie path successfully restored");
 
   // Clear cookies.
   Services.cookies.removeAll();
 
   // Restore window with session cookies that have originAttributes within.
-  ss.setWindowState(win, SESSION_DATA_OA, true);
+  await setWindowState(win, SESSION_DATA_OA, true);
 
   enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
   cookieCount = 0;
   while (enumerator.hasMoreElements()) {
     cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
     cookieCount++;
   }
 
--- a/browser/components/sessionstore/test/browser_windowStateContainer.js
+++ b/browser/components/sessionstore/test/browser_windowStateContainer.js
@@ -36,17 +36,17 @@ add_task(async function() {
   // Create tabs with different userContextId, but this time we create them with
   // fewer tabs and with different order with win.
   for (let userContextId = 3; userContextId > 0; userContextId--) {
     let tab = win2.gBrowser.addTab("http://example.com/", {userContextId});
     await promiseBrowserLoaded(tab.linkedBrowser);
     await TabStateFlusher.flush(tab.linkedBrowser);
   }
 
-  ss.setWindowState(win2, JSON.stringify(winState), true);
+  await setWindowState(win2, winState, true);
 
   for (let i = 0; i < 4; i++) {
     let browser = win2.gBrowser.tabs[i].linkedBrowser;
     await ContentTask.spawn(browser, { expectedId: i + 1 }, async function(args) {
       Assert.equal(docShell.getOriginAttributes().userContextId,
                    args.expectedId,
                    "The docShell has the correct userContextId");
 
@@ -96,17 +96,17 @@ add_task(async function() {
   await promiseBrowserLoaded(tab2.linkedBrowser);
   await TabStateFlusher.flush(tab2.linkedBrowser);
 
   // Move the first normal tab to end, so the first tab of win2 will be a
   // container tab.
   win2.gBrowser.moveTabTo(win2.gBrowser.tabs[0], win2.gBrowser.tabs.length - 1);
   await TabStateFlusher.flush(win2.gBrowser.tabs[0].linkedBrowser);
 
-  ss.setWindowState(win2, JSON.stringify(winState), true);
+  await setWindowState(win2, winState, true);
 
   for (let i = 0; i < 2; i++) {
     let browser = win2.gBrowser.tabs[i].linkedBrowser;
     await ContentTask.spawn(browser, { expectedId: i }, async function(args) {
       Assert.equal(docShell.getOriginAttributes().userContextId,
                    args.expectedId,
                    "The docShell has the correct userContextId");
 
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -178,16 +178,30 @@ function promiseTabState(tab, state) {
     state = JSON.stringify(state);
   }
 
   let promise = promiseTabRestored(tab);
   ss.setTabState(tab, state);
   return promise;
 }
 
+function promiseWindowRestored(win) {
+  return new Promise(resolve => win.addEventListener("SSWindowRestored", resolve, {once: true}));
+}
+
+async function setBrowserState(state, win = window) {
+  ss.setBrowserState(typeof state != "string" ? JSON.stringify(state) : state);
+  await promiseWindowRestored(win);
+}
+
+async function setWindowState(win, state, overwrite = false) {
+  ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state, overwrite);
+  await promiseWindowRestored(win);
+}
+
 /**
  * Wait for a content -> chrome message.
  */
 function promiseContentMessage(browser, name) {
   let mm = browser.messageManager;
 
   return new Promise(resolve => {
     function removeListener() {