Bug 1263784 - Fix test_modal_prompts.html and test_modal_select.html to run under E10S. r=adw
authorJustin Dolske <dolske@mozilla.com>
Fri, 15 Apr 2016 13:43:25 -0700
changeset 331394 c8bc5838fca3fe1f8590720bca97b240d563b8af
parent 331393 9295efcd0881d7d591e090151bb3910936f21d38
child 331395 0ce90323cb7c9aabaf29d0ca1b97d4708a67f011
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw
bugs1263784
milestone48.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 1263784 - Fix test_modal_prompts.html and test_modal_select.html to run under E10S. r=adw Use chromeScript and message manager to fetch state of prompt and dismiss it. Enable tests in E10S. MozReview-Commit-ID: 5M9GYijlQPV
toolkit/components/prompts/test/chromeScript.js
toolkit/components/prompts/test/mochitest.ini
toolkit/components/prompts/test/test_modal_prompts.html
toolkit/components/prompts/test/test_modal_select.html
--- a/toolkit/components/prompts/test/chromeScript.js
+++ b/toolkit/components/prompts/test/chromeScript.js
@@ -1,21 +1,202 @@
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/Timer.jsm");
 
 // Define these to make EventUtils happy.
 let window = this;
 let parent = {};
 
 let EventUtils = {};
 Services.scriptloader.loadSubScript(
   "chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
   EventUtils
 );
 
+addMessageListener("handlePrompt", msg => {
+  handlePromptWhenItAppears(msg.action, msg.isTabModal, msg.isSelect);
+});
+
+function handlePromptWhenItAppears(action, isTabModal, isSelect) {
+  let interval = setInterval(() => {
+    if (handlePrompt(action, isTabModal, isSelect)) {
+      clearInterval(interval);
+    }
+  }, 100);
+}
+
+function handlePrompt(action, isTabModal, isSelect) {
+  let ui;
+
+  if (isTabModal) {
+    let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
+    let gBrowser = browserWin.gBrowser;
+    let promptManager = gBrowser.getTabModalPromptBox(gBrowser.selectedBrowser);
+    let prompts = promptManager.listPrompts();
+    if (!prompts.length) {
+      return false; // try again in a bit
+    }
+
+    ui = prompts[0].Dialog.ui;
+  } else {
+    let doc = getDialogDoc();
+    if (!doc) {
+      return false; // try again in a bit
+    }
+
+    if (isSelect)
+      ui = doc;
+    else
+      ui = doc.defaultView.Dialog.ui;
+
+  }
+
+  let promptState;
+  if (isSelect) {
+    promptState = getSelectState(ui);
+    dismissSelect(ui, action);
+  } else {
+    promptState = getPromptState(ui);
+    dismissPrompt(ui, action);
+  }
+  sendAsyncMessage("promptHandled", { promptState: promptState });
+  return true;
+}
+
+function getSelectState(ui) {
+  let listbox = ui.getElementById("list");
+
+  let state = {};
+  state.msg = ui.getElementById("info.txt").value;
+  state.selectedIndex = listbox.selectedIndex;
+  state.items = [];
+
+  for (let i = 0; i < listbox.itemCount; i++) {
+    let item = listbox.getItemAtIndex(i).label;
+    state.items.push(item);
+  }
+
+  return state;
+}
+
+function getPromptState(ui) {
+  let state = {};
+  state.msg         = ui.infoBody.textContent;
+  state.textHidden  = ui.loginContainer.hidden;
+  state.passHidden  = ui.password1Container.hidden;
+  state.checkHidden = ui.checkboxContainer.hidden;
+  state.checkMsg    = ui.checkbox.label;
+  state.checked     = ui.checkbox.checked;
+  // tab-modal prompts don't have an infoIcon
+  state.iconClass   = ui.infoIcon ? ui.infoIcon.className : null;
+  state.textValue   = ui.loginTextbox.getAttribute("value");
+  state.passValue   = ui.password1Textbox.getAttribute("value");
+
+  state.butt0Label  = ui.button0.label;
+  state.butt1Label  = ui.button1.label;
+  state.butt2Label  = ui.button2.label;
+
+  state.butt0Disabled = ui.button0.disabled;
+  state.butt1Disabled = ui.button1.disabled;
+  state.butt2Disabled = ui.button2.disabled;
+
+  function isDefaultButton(b) {
+      return (b.hasAttribute("default") &&
+              b.getAttribute("default") == "true");
+  }
+  state.defButton0 = isDefaultButton(ui.button0);
+  state.defButton1 = isDefaultButton(ui.button1);
+  state.defButton2 = isDefaultButton(ui.button2);
+
+  let fm = Cc["@mozilla.org/focus-manager;1"].
+           getService(Ci.nsIFocusManager);
+  let e = fm.focusedElement;
+
+  if (e == null) {
+    state.focused = null;
+  } else if (ui.button0.isSameNode(e)) {
+    state.focused = "button0";
+  } else if (ui.button1.isSameNode(e)) {
+    state.focused = "button1";
+  } else if (ui.button2.isSameNode(e)) {
+    state.focused = "button2";
+  } else if (ui.loginTextbox.inputField.isSameNode(e)) {
+    state.focused = "textField";
+  } else if (ui.password1Textbox.inputField.isSameNode(e)) {
+    state.focused = "passField";
+  } else if (ui.infoBody.isSameNode(e)) {
+    state.focused = "infoBody";
+  } else {
+    state.focused = "ERROR: unexpected element focused: " + (e ? e.localName : "<null>");
+  }
+
+  return state;
+}
+
+function dismissSelect(ui, action) {
+  let dialog = ui.getElementsByTagName("dialog")[0];
+  let listbox = ui.getElementById("list");
+
+  if (action.selectItem) {
+      listbox.selectedIndex = 1;
+  }
+
+  if (action.buttonClick == "ok") {
+      dialog.acceptDialog();
+  } else if (action.buttonClick == "cancel") {
+      dialog.cancelDialog();
+  }
+}
+
+function dismissPrompt(ui, action) {
+  if (action.setCheckbox) {
+    // Annoyingly, the prompt code is driven by oncommand.
+    ui.checkbox.setChecked(true);
+    ui.checkbox.doCommand();
+  }
+
+  if (action.textField) {
+    ui.loginTextbox.setAttribute("value", action.textField);
+  }
+
+  if (action.passField) {
+    ui.password1Textbox.setAttribute("value", action.passField);
+  }
+
+  switch (action.buttonClick) {
+    case "ok":
+    case 0:
+      ui.button0.click();
+      break;
+    case "cancel":
+    case 1:
+      ui.button1.click();
+      break;
+    case 2:
+      ui.button2.click();
+      break;
+    case "pollOK":
+      // Buttons are disabled at the moment, poll until they're reenabled.
+      // Can't use setInterval here, because the window's in a modal state
+      // and thus DOM events are suppressed.
+      let interval = setInterval(() => {
+        if (ui.button0.disabled)
+          return;
+        ui.button0.click();
+        clearInterval(interval);
+      }, 100);
+      break;
+
+    default:
+      throw "dismissPrompt action listed unknown button.";
+  }
+}
+
+
 addMessageListener("cancelPrompt", message => {
   cancelPromptWhenItAppears();
 });
 
 function cancelPromptWhenItAppears() {
   let interval = setInterval(() => {
     if (cancelPrompt()) {
       clearInterval(interval);
@@ -37,8 +218,44 @@ function cancelPrompt() {
         hidden: prompts[0].ui.infoTitle.getAttribute("hidden") == "true",
       },
     },
   });
   EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, browserWin);
   return true;
 }
 
+
+function getDialogDoc() {
+  // Trudge through all the open windows, until we find the one
+  // that has either commonDialog.xul or selectDialog.xul loaded.
+  var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
+           getService(Ci.nsIWindowMediator);
+  //var enumerator = wm.getEnumerator("navigator:browser");
+  var enumerator = wm.getXULWindowEnumerator(null);
+
+  while (enumerator.hasMoreElements()) {
+    var win = enumerator.getNext();
+    var windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
+
+    var containedDocShells = windowDocShell.getDocShellEnumerator(
+                                      Ci.nsIDocShellTreeItem.typeChrome,
+                                      Ci.nsIDocShell.ENUMERATE_FORWARDS);
+    while (containedDocShells.hasMoreElements()) {
+        // Get the corresponding document for this docshell
+        var childDocShell = containedDocShells.getNext();
+        // We don't want it if it's not done loading.
+        if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
+          continue;
+        var childDoc = childDocShell.QueryInterface(Ci.nsIDocShell)
+                                    .contentViewer
+                                    .DOMDocument;
+
+        //ok(true, "Got window: " + childDoc.location.href);
+        if (childDoc.location.href == "chrome://global/content/commonDialog.xul")
+          return childDoc;
+        if (childDoc.location.href == "chrome://global/content/selectDialog.xul")
+          return childDoc;
+    }
+  }
+
+  return null;
+}
--- a/toolkit/components/prompts/test/mochitest.ini
+++ b/toolkit/components/prompts/test/mochitest.ini
@@ -7,11 +7,11 @@ support-files =
   chromeScript.js
 
 [test_bug619644.html]
 [test_bug620145.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug625187.html]
 [test_bug861605.html]
 [test_modal_prompts.html]
-skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) || e10s #android: TIMED_OUT (For Linux : 950636)
+skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) #android: TIMED_OUT (For Linux : 950636)
 [test_modal_select.html]
-skip-if = toolkit == 'android' || e10s #android: TIMED_OUT
+skip-if = toolkit == 'android' #android: TIMED_OUT
--- a/toolkit/components/prompts/test/test_modal_prompts.html
+++ b/toolkit/components/prompts/test/test_modal_prompts.html
@@ -15,169 +15,74 @@ Prompter tests: modal prompts
   <iframe id="iframe"></iframe>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript;version=1.8">
 
 var isOSX = ("nsILocalFileMac" in SpecialPowers.Ci);
 var isLinux = ("@mozilla.org/gnome-gconf-service;1" in SpecialPowers.Cc);
-
-function pollDialog(okButton) {
-    if (okButton.disabled)
-        return;
+var isE10S = SpecialPowers.Services.appinfo.processType == 2;
 
-    ok(true, "dialog button is enabled now");
-    pollTimer.cancel();
-    pollTimer = null;
-    okButton.click();
-    didDialog = true;
-}
-
-function checkExpectedState(ui, state, action) {
-
+function checkPromptState(promptState, expectedState) {
     // XXX check title? OS X has title in content
-    is(ui.infoBody.textContent,       state.msg,         "Checking expected message");
-    is(ui.loginContainer.hidden,      state.textHidden,  "Checking textbox visibility");
-    is(ui.password1Container.hidden,  state.passHidden,  "Checking passbox visibility");
-    is(ui.checkboxContainer.hidden,   state.checkHidden, "Checking checkbox visibility");
-    is(ui.checkbox.label,             state.checkMsg,    "Checking checkbox label");
-    is(ui.checkbox.checked,           state.checked,     "Checking checkbox checked");
+    is(promptState.msg,         expectedState.msg,         "Checking expected message");
+    is(promptState.textHidden,  expectedState.textHidden,  "Checking textbox visibility");
+    is(promptState.passHidden,  expectedState.passHidden,  "Checking passbox visibility");
+    is(promptState.checkHidden, expectedState.checkHidden, "Checking checkbox visibility");
+    is(promptState.checkMsg,    expectedState.checkMsg,    "Checking checkbox label");
+    is(promptState.checked,     expectedState.checked,     "Checking checkbox checked");
     if (!isTabModal)
-      is(ui.infoIcon.className,       "spaced " + state.iconClass, "Checking expected icon CSS class");
-    is(ui.loginTextbox.getAttribute("value"),     state.textValue, "Checking textbox value");
-    is(ui.password1Textbox.getAttribute("value"), state.passValue, "Checking passbox value");
+      is(promptState.iconClass, "spaced " + expectedState.iconClass, "Checking expected icon CSS class");
+    is(promptState.textValue, expectedState.textValue, "Checking textbox value");
+    is(promptState.passValue, expectedState.passValue, "Checking passbox value");
 
-    if (state.butt0Label) {
-        is(ui.button0.label, state.butt0Label, "Checking accept-button label");
-    }
-    if (state.butt1Label) {
-        is(ui.button1.label, state.butt1Label, "Checking cancel-button label");
+    if (expectedState.butt0Label) {
+        is(promptState.butt0Label, expectedState.butt0Label, "Checking accept-button label");
     }
-    if (state.butt2Label) {
-        is(ui.button2.label, state.butt2Label, "Checking extra1-button label");
+    if (expectedState.butt1Label) {
+        is(promptState.butt1Label, expectedState.butt1Label, "Checking cancel-button label");
     }
-
-    function isDefaultButton(b) {
-        return (b.hasAttribute("default") &&
-                b.getAttribute("default") == "true");
+    if (expectedState.butt2Label) {
+        is(promptState.butt2Label, expectedState.butt2Label, "Checking extra1-button label");
     }
 
-    is(isDefaultButton(ui.button0), state.defButton == "button0", "checking button0 default");
-    is(isDefaultButton(ui.button1), state.defButton == "button1", "checking button1 default");
-    is(isDefaultButton(ui.button2), state.defButton == "button2", "checking button2 default");
-
-    let fm = Cc["@mozilla.org/focus-manager;1"].
-             getService(Ci.nsIFocusManager);
-    let e = fm.focusedElement;
-    ok(true, "focused element is a " + (e ? e.localName : "<null>"));
-    if (isLinux && !e) {
-        todo(false, "Focus seems missing on Linux");
-    } else if (isOSX && state.focused && state.focused.startsWith("button")) {
-        ok(SpecialPowers.compare(ui.infoBody, e), "buttons don't focus on OS X");
-    } else if (state.focused == null) {
-        is(e, null, "Not expecting a focused element");
-    } else {
-        let expectedElement;
-        switch (state.focused) {
-            case "button0":
-                expectedElement = ui.button0;
-                break;
-            case "button1":
-                expectedElement = ui.button1;
-                break;
-            case "button2":
-                expectedElement = ui.button2;
-                break;
-            case "textField":
-                expectedElement = ui.loginTextbox.inputField;
-                break;
-            case "passField":
-                expectedElement = ui.password1Textbox.inputField;
-                break;
-            case "infoBody":
-                expectedElement = ui.infoBody;
-                break;
-            default:
-                ok(false, "Test listed an unknown element as expecting to be focused!");
-                break;
-        }
-        ok(SpecialPowers.compare(expectedElement, e), "Checking focused element");
-    }
-
-
-    /* Actions */
-
-
-    if (action.setCheckbox) {
-        // Annoyingly, the prompt code is driven by oncommand.
-        ui.checkbox.setChecked(true);
-        ui.checkbox.doCommand();
+    // For prompts with a time-delay button.
+    if (expectedState.butt0Disabled) {
+        is(promptState.butt0Disabled, true,  "Checking accept-button is disabled");
+        is(promptState.butt1Disabled, false, "Checking cancel-button isn't disabled");
     }
 
-    if (action.textField) {
-        ui.loginTextbox.setAttribute("value", action.textField);
-    }
-
-    if (action.passField) {
-        ui.password1Textbox.setAttribute("value", action.passField);
-    }
-
-    if (action.buttonClick == "pollOk") {
-        is(ui.button0.disabled, true,  "Checking accept-button is disabled");
-        is(ui.button1.disabled, false, "Checking cancel-button isn't disabled ");
-    }
+    is(promptState.defButton0, expectedState.defButton == "button0", "checking button0 default");
+    is(promptState.defButton1, expectedState.defButton == "button1", "checking button1 default");
+    is(promptState.defButton2, expectedState.defButton == "button2", "checking button2 default");
 
-    switch (action.buttonClick) {
-        case "ok":
-        case 0:
-            ui.button0.click();
-            break;
-        case "cancel":
-        case 1:
-            ui.button1.click();
-            break;
-        case 2:
-            ui.button2.click();
-            break;
-        case "pollOK":
-            // Buttons are disabled at the moment, poll until they're reenabled.
-            // Can't use setInterval here, because the window's in a modal state
-            // and thus DOM events are suppressed.
-            pollTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-            pollTimer.initWithCallback(SpecialPowers.wrapCallback(function() {
-              pollDialog(ui.button0);
-            }),
-                                   100, Ci.nsITimer.TYPE_REPEATING_SLACK);
-            break;
-        default:
-            ok(false, "checkExpectedState action listed unknown button.")
+    if (isLinux && (!promptState.focused || isE10S)) {
+        todo(false, "Focus seems missing or wrong on Linux"); // bug 1265077
+    } else if (isOSX && expectedState.focused && expectedState.focused.startsWith("button")) {
+        is(promptState.focused, "infoBody", "buttons don't focus on OS X, but infoBody does instead");
+    } else {
+        is(promptState.focused, expectedState.focused, "Checking focused element");
     }
-
-    if (action.buttonClick != "pollOK")
-        didDialog = true;
-}
-
-
-/*
- * handleDialog
- *
- * Invoked a short period of time after calling startCallbackTimer(), and
- * allows testing the actual prompt dialog while it's being displayed. Tests
- * should call startCallbackTimer() each time the auth dialog is expected (the
- * timer is a one-shot).
- */
-function handleDialog(ui, testNum) {
-    ok(true, "--- handleDialog for test " + testNum +
-             " --- (isTabModal=" + isTabModal + ", usePromptService=" + usePromptService + ")");
-    checkExpectedState(ui, state, action);
 }
 
 
 function* runTests() {
+
+    function handlePrompt() {
+      return new Promise(resolve => {
+        gChromeScript.addMessageListener("promptHandled", function handled(msg) {
+          gChromeScript.removeMessageListener("promptHandled", handled);
+          checkPromptState(msg.promptState, state);
+          resolve(true);
+        });
+        gChromeScript.sendAsyncMessage("handlePrompt", { action: action, isTabModal: isTabModal});
+      });
+    }
+
     let ioService = Cc["@mozilla.org/network/io-service;1"].
                     getService(Ci.nsIIOService);
     ok(true, "Running tests (isTabModal=" + isTabModal + ", usePromptService=" + usePromptService + ")");
 
     let prompter, promptArgs;
     if (usePromptService) {
         prompter = Cc["@mozilla.org/embedcomp/prompt-service;1"].
                    getService(Ci.nsIPromptService2);
@@ -192,21 +97,18 @@ function* runTests() {
     }
 
     let checkVal  = {};
     let textVal   = {};
     let passVal   = {};
     let flags;
     let isOK, clickedButton;
 
-    testNum = 0;
-
-    // ===== test 1 =====
-    // Alert
-    testNum++;
+    // =====
+    info("Starting test: Alert");
     state = {
         msg   : "This is the alert text.",
         title : "TestTitle",
         iconClass   : "alert-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -214,29 +116,28 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     promptArgs = ["TestTitle", "This is the alert text."];
     if (usePromptService)
         promptArgs.unshift(window);
     prompter.alert.apply(null, promptArgs);
 
-    ok(didDialog, "handleDialog was invoked");
-
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 2 =====
-    // AlertCheck (null checkbox label, so it's hidden)
-    testNum++;
+    // =====
+    info("Starting test: AlertCheck (null checkbox label, so it's hidden)");
     state = {
         msg   : "This is the alertCheck text.",
         title : "TestTitle",
         iconClass   : "alert-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -244,28 +145,28 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     promptArgs = ["TestTitle", "This is the alertCheck text.", null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     prompter.alertCheck.apply(null, promptArgs);
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 3 =====
-    // AlertCheck
-    testNum++;
+    // =====
+    info("Starting test: AlertCheck");
     state = {
         msg   : "This is the alertCheck text.",
         title : "TestTitle",
         iconClass   : "alert-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : false,
         textValue   : "",
@@ -274,30 +175,30 @@ function* runTests() {
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the alertCheck text.", "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     prompter.alertCheck.apply(null, promptArgs);
-    ok(didDialog, "handleDialog was invoked");
     is(checkVal.value, true, "checkbox was checked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 4 =====
-    // Confirm (ok)
-    testNum++;
+    // =====
+    info("Starting test: Confirm (ok)");
     state = {
         msg   : "This is the confirm text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -305,29 +206,29 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     promptArgs = ["TestTitle", "This is the confirm text."];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.confirm.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 5 =====
-    // Confirm (cancel)
-    testNum++;
+    // =====
+    info("Starting test: Confirm (cancel)");
     state = {
         msg   : "This is the confirm text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -335,29 +236,29 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "cancel",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     promptArgs = ["TestTitle", "This is the confirm text."];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.confirm.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 6 =====
-    // ConfirmCheck (ok, null checkbox label)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmCheck (ok, null checkbox label)");
     state = {
         msg   : "This is the confirmCheck text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -365,29 +266,29 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     promptArgs = ["TestTitle", "This is the confirmCheck text.", null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.confirmCheck.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 7 =====
-    // ConfirmCheck (cancel, null checkbox label)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmCheck (cancel, null checkbox label)");
     state = {
         msg   : "This is the confirmCheck text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -395,29 +296,29 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "cancel",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     promptArgs = ["TestTitle", "This is the confirmCheck text.", null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.confirmCheck.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 8 =====
-    // ConfirmCheck (ok)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmCheck (ok)");
     state = {
         msg   : "This is the confirmCheck text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : false,
         textValue   : "",
@@ -426,31 +327,31 @@ function* runTests() {
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the confirmCheck text.", "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.confirmCheck.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 9 =====
-    // ConfirmCheck (cancel)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmCheck (cancel)");
     state = {
         msg   : "This is the confirmCheck text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : false,
         textValue   : "",
@@ -459,31 +360,31 @@ function* runTests() {
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "cancel",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the confirmCheck text.", "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.confirmCheck.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 10 =====
-    // Prompt (ok, no default text)
-    testNum++;
+    // =====
+    info("Starting test: Prompt (ok, no default text)");
     state = {
         msg   : "This is the prompt text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : false,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -492,31 +393,31 @@ function* runTests() {
         checked     : false,
         focused     : "textField",
         defButton   : "button0",
     };
     action = {
         buttonClick : "ok",
         textField   : "bacon",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value = "";
     promptArgs = ["TestTitle", "This is the prompt text.", textVal, null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.prompt.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
     is(textVal.value, "bacon", "checking expected text value");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 11 =====
-    // Prompt (ok, default text)
-    testNum++;
+    // =====
+    info("Starting test: Prompt (ok, default text)");
     state = {
         msg   : "This is the prompt text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : false,
         passHidden  : true,
         checkHidden : true,
         textValue   : "kittens",
@@ -524,31 +425,31 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "textField",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value = "kittens";
     promptArgs = ["TestTitle", "This is the prompt text.", textVal, null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.prompt.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
     is(textVal.value, "kittens", "checking expected text value");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 12 =====
-    // Prompt (cancel, default text)
-    testNum++;
+    // =====
+    info("Starting test: Prompt (cancel, default text)");
     state = {
         msg   : "This is the prompt text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : false,
         passHidden  : true,
         checkHidden : true,
         textValue   : "puppies",
@@ -556,31 +457,31 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "textField",
         defButton   : "button0",
     };
     action = {
         buttonClick: "cancel",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value = "puppies";
     promptArgs = ["TestTitle", "This is the prompt text.", textVal, null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.prompt.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
     is(textVal.value, "puppies", "checking expected text value");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 13 =====
-    // Prompt (cancel, default text modified)
-    testNum++;
+    // =====
+    info("Starting test: Prompt (cancel, default text modified)");
     state = {
         msg   : "This is the prompt text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : false,
         passHidden  : true,
         checkHidden : true,
         textValue   : "puppies",
@@ -589,31 +490,31 @@ function* runTests() {
         checked     : false,
         focused     : "textField",
         defButton   : "button0",
     };
     action = {
         buttonClick : "cancel",
         textField   : "bacon",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value = "puppies";
     promptArgs = ["TestTitle", "This is the prompt text.", textVal, null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.prompt.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
     is(textVal.value, "puppies", "checking expected text value");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 14 =====
-    // Prompt (ok, with checkbox)
-    testNum++;
+    // =====
+    info("Starting test: Prompt (ok, with checkbox)");
     state = {
         msg   : "This is the prompt text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : false,
         passHidden  : true,
         checkHidden : false,
         textValue   : "tribbles",
@@ -622,33 +523,33 @@ function* runTests() {
         checked     : false,
         focused     : "textField",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value  = "tribbles";
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the prompt text.", textVal, "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.prompt.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
     is(textVal.value, "tribbles", "checking expected text value");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 15 =====
-    // Prompt (cancel, with checkbox)
-    testNum++;
+    // =====
+    info("Starting test: Prompt (cancel, with checkbox)");
     state = {
         msg   : "This is the prompt text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : false,
         passHidden  : true,
         checkHidden : false,
         textValue   : "tribbles",
@@ -657,34 +558,34 @@ function* runTests() {
         checked     : false,
         focused     : "textField",
         defButton   : "button0",
     };
     action = {
         buttonClick: "cancel",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value  = "tribbles";
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the prompt text.", textVal, "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.prompt.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
     is(textVal.value, "tribbles", "checking expected text value");
     is(checkVal.value, false, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 16 =====
-    // PromptUsernameAndPassword (ok)
+    // =====
     // Just two tests for this, since password manager already tests this extensively.
-    testNum++;
+    info("Starting test: PromptUsernameAndPassword (ok)");
     state = {
         msg   : "This is the pUAP text.",
         title : "TestTitle",
         iconClass   : "authentication-icon question-icon",
         textHidden  : false,
         passHidden  : false,
         checkHidden : false,
         textValue   : "usr",
@@ -695,35 +596,35 @@ function* runTests() {
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
         setCheckbox: true,
         textField: "newusr",
         passField: "newssh",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value  = "usr";
     passVal.value  = "ssh";
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the pUAP text.", textVal, passVal, "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.promptUsernameAndPassword.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
     is(textVal.value, "newusr", "checking expected text value");
     is(passVal.value, "newssh", "checking expected pass value");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 17 =====
-    // PromptUsernameAndPassword (cancel)
-    testNum++;
+    // =====
+    info("Starting test: PromptUsernameAndPassword (cancel)");
     state = {
         msg   : "This is the pUAP text.",
         title : "TestTitle",
         iconClass   : "authentication-icon question-icon",
         textHidden  : false,
         passHidden  : false,
         checkHidden : false,
         textValue   : "usr",
@@ -734,35 +635,35 @@ function* runTests() {
         defButton   : "button0",
     };
     action = {
         buttonClick : "cancel",
         setCheckbox : true,
         textField   : "newusr",
         passField   : "newssh",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     textVal.value  = "usr";
     passVal.value  = "ssh";
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the pUAP text.", textVal, passVal, "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.promptUsernameAndPassword.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
     is(textVal.value, "usr", "checking expected text value");
     is(passVal.value, "ssh", "checking expected pass value");
     is(checkVal.value, false, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 18 =====
-    // PromptPassword (ok)
-    testNum++;
+    // =====
+    info("Starting test: PromptPassword (ok)");
     state = {
         msg   : "This is the promptPassword text.",
         title : "TestTitle",
         iconClass   : "authentication-icon question-icon",
         textHidden  : true,
         passHidden  : false,
         checkHidden : false,
         textValue   : "",
@@ -772,33 +673,33 @@ function* runTests() {
         focused     : "passField",
         defButton   : "button0",
     };
     action = {
         buttonClick : "ok",
         setCheckbox : true,
         passField   : "newssh",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     passVal.value  = "ssh";
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the promptPassword text.", passVal, "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.promptPassword.apply(null, promptArgs);
     is(isOK, true, "checked expected retval");
     is(passVal.value, "newssh", "checking expected pass value");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 19 =====
-    // PromptPassword (cancel)
-    testNum++;
+    // =====
+    info("Starting test: PromptPassword (cancel)");
     state = {
         msg   : "This is the promptPassword text.",
         title : "TestTitle",
         iconClass   : "authentication-icon question-icon",
         textHidden  : true,
         passHidden  : false,
         checkHidden : false,
         textValue   : "",
@@ -808,33 +709,33 @@ function* runTests() {
         focused     : "passField",
         defButton   : "button0",
     };
     action = {
         buttonClick : "cancel",
         setCheckbox : true,
         passField   : "newssh",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     passVal.value  = "ssh";
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the promptPassword text.", passVal, "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     isOK = prompter.promptPassword.apply(null, promptArgs);
     is(isOK, false, "checked expected retval");
     is(passVal.value, "ssh", "checking expected pass value");
     is(checkVal.value, false, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 20 =====
-    // ConfirmEx (ok/cancel, ok)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmEx (ok/cancel, ok)");
     state = {
         msg   : "This is the confirmEx text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -844,30 +745,30 @@ function* runTests() {
         focused     : "button0",
         defButton   : "button0",
         butt0Label  : "OK",
         butt1Label  : "Cancel",
     };
     action = {
         buttonClick: "ok",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     flags = Ci.nsIPromptService.STD_OK_CANCEL_BUTTONS;
     promptArgs = ["TestTitle", "This is the confirmEx text.", flags, null, null, null, null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     clickedButton = prompter.confirmEx.apply(null, promptArgs);
     is(clickedButton, 0, "checked expected button num click");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 21 =====
-    // ConfirmEx (yes/no, cancel)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmEx (yes/no, cancel)");
     state = {
         msg   : "This is the confirmEx text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -877,30 +778,30 @@ function* runTests() {
         focused     : "button0",
         defButton   : "button0",
         butt0Label  : "Yes",
         butt1Label  : "No",
     };
     action = {
         buttonClick: "cancel",
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     flags = Ci.nsIPromptService.STD_YES_NO_BUTTONS;
     promptArgs = ["TestTitle", "This is the confirmEx text.", flags, null, null, null, null, {}];
     if (usePromptService)
         promptArgs.unshift(window);
     clickedButton = prompter.confirmEx.apply(null, promptArgs);
     is(clickedButton, 1, "checked expected button num click");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 22 =====
-    // ConfirmEx (buttons from args, checkbox, ok)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmEx (buttons from args, checkbox, ok)");
     state = {
         msg   : "This is the confirmEx text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : false,
         textValue   : "",
@@ -912,36 +813,36 @@ function* runTests() {
         butt0Label  : "butt0",
         butt1Label  : "butt1",
         butt2Label  : "butt2",
     };
     action = {
         buttonClick: "ok",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     let b = Ci.nsIPromptService.BUTTON_TITLE_IS_STRING;
     flags = b * Ci.nsIPromptService.BUTTON_POS_2 +
             b * Ci.nsIPromptService.BUTTON_POS_1 +
             b * Ci.nsIPromptService.BUTTON_POS_0;
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the confirmEx text.", flags,
                   "butt0", "butt1", "butt2", "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     clickedButton = prompter.confirmEx.apply(null, promptArgs);
     is(clickedButton, 0, "checked expected button num click");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 23 =====
-    // ConfirmEx (buttons from args, checkbox, cancel)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmEx (buttons from args, checkbox, cancel)");
     state = {
         msg   : "This is the confirmEx text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : false,
         textValue   : "",
@@ -953,37 +854,37 @@ function* runTests() {
         butt0Label  : "butt0",
         butt1Label  : "butt1",
         butt2Label  : "butt2",
     };
     action = {
         buttonClick: "cancel",
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     b = Ci.nsIPromptService.BUTTON_TITLE_IS_STRING;
     flags = b * Ci.nsIPromptService.BUTTON_POS_2 +
             b * Ci.nsIPromptService.BUTTON_POS_1 +
             b * Ci.nsIPromptService.BUTTON_POS_0;
     flags ^= Ci.nsIPromptService.BUTTON_POS_1_DEFAULT;
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the confirmEx text.", flags,
                   "butt0", "butt1", "butt2", "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     clickedButton = prompter.confirmEx.apply(null, promptArgs);
     is(clickedButton, 1, "checked expected button num click");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 24 =====
-    // ConfirmEx (buttons from args, checkbox, button3)
-    testNum++;
+    // =====
+    info("Starting test: ConfirmEx (buttons from args, checkbox, button3)");
     state = {
         msg   : "This is the confirmEx text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : false,
         textValue   : "",
@@ -995,38 +896,38 @@ function* runTests() {
         butt0Label  : "butt0",
         butt1Label  : "butt1",
         butt2Label  : "butt2",
     };
     action = {
         buttonClick: 2,
         setCheckbox: true,
     };
-    startCallbackTimer();
+
+    promptDone = handlePrompt(action);
+
     b = Ci.nsIPromptService.BUTTON_TITLE_IS_STRING;
     flags = b * Ci.nsIPromptService.BUTTON_POS_2 +
             b * Ci.nsIPromptService.BUTTON_POS_1 +
             b * Ci.nsIPromptService.BUTTON_POS_0;
     flags ^= Ci.nsIPromptService.BUTTON_POS_2_DEFAULT;
     checkVal.value = false;
     promptArgs = ["TestTitle", "This is the confirmEx text.", flags,
                   "butt0", "butt1", "butt2", "Check me out!", checkVal];
     if (usePromptService)
         promptArgs.unshift(window);
     clickedButton = prompter.confirmEx.apply(null, promptArgs);
     is(clickedButton, 2, "checked expected button num click");
     is(checkVal.value, true, "expected checkbox setting");
-    ok(didDialog, "handleDialog was invoked");
 
-    yield Promise.resolve();
+    yield promptDone;
 
-    // ===== test 25  =====
-    // Alert, no window
-    // (skipped for tabmodal tests: window is required)
-    testNum++;
+    // =====
+    // (skipped for E10S and tabmodal tests: window is required)
+    info("Starting test: Alert, no window");
     state = {
         msg   : "This is the alert text.",
         title : "TestTitle",
         iconClass   : "alert-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
@@ -1034,65 +935,68 @@ function* runTests() {
         checkMsg    : "",
         checked     : false,
         focused     : "button0",
         defButton   : "button0",
     };
     action = {
         buttonClick: "ok",
     };
-    if (!isTabModal) {
-        startCallbackTimer();
-    promptArgs = ["TestTitle", "This is the alert text."];
-    if (usePromptService)
-        promptArgs.unshift(null);
+    if (!isTabModal && !isE10S) {
+        promptDone = handlePrompt(action);
+
+        promptArgs = ["TestTitle", "This is the alert text."];
+        if (usePromptService)
+          promptArgs.unshift(null);
         prompter.alert.apply(null, promptArgs);
-        ok(didDialog, "handleDialog was invoked");
+
+        yield promptDone;
     }
 
-    yield Promise.resolve();
 
-    // ===== test 26 =====
-    // ConfirmEx (delay, ok)
+    // =====
     // (skipped for tabmodal tests: delay not supported)
-    testNum++;
+    info("Starting test: ConfirmEx (delay, ok)");
     state = {
         msg   : "This is the confirmEx delay text.",
         title : "TestTitle",
         iconClass   : "question-icon",
         textHidden  : true,
         passHidden  : true,
         checkHidden : true,
         textValue   : "",
         passValue   : "",
         checkMsg    : "",
         checked     : false,
         focused     : null, // nothing focused until after delay fires
         defButton   : "button0",
         butt0Label  : "OK",
         butt1Label  : "Cancel",
+        butt0Disabled: true,
     };
 
     // OS X doesn't initially focus the button, but rather the infoBody.
     // The focus stays there even after the button-enable delay has fired.
     if (isOSX)
         state.focused = "infoBody";
 
     action = {
         buttonClick: "pollOK",
     };
     if (!isTabModal) {
-        startCallbackTimer();
+        promptDone = handlePrompt(action);
+
         flags = (Ci.nsIPromptService.STD_OK_CANCEL_BUTTONS | Ci.nsIPromptService.BUTTON_DELAY_ENABLE);
         promptArgs = ["TestTitle", "This is the confirmEx delay text.", flags, null, null, null, null, {}];
         if (usePromptService)
             promptArgs.unshift(window);
         clickedButton = prompter.confirmEx.apply(null, promptArgs);
         is(clickedButton, 0, "checked expected button num click");
-        ok(didDialog, "handleDialog was invoked");
+
+        yield promptDone;
     }
 
     // promptAuth already tested via password manager but do a few specific things here.
 
 
     var channel = ioService.newChannel2("http://example.com",
                                         null,
                                         null,
@@ -1106,22 +1010,20 @@ function* runTests() {
         username : "",
         password : "",
         domain   : "",
         flags : Ci.nsIAuthInformation.AUTH_HOST,
         authenticationScheme : "basic",
         realm : ""
     };
 
-    yield Promise.resolve();
 
-    // ===== test 100 =====
-    // promptAuth with empty realm
+    // =====
     // (promptAuth is only accessible from the prompt service)
-    testNum = 100;
+    info("Starting test: promptAuth with empty realm");
     state = {
         msg : 'Enter username and password for http://example.com',
         title : "TestTitle",
         iconClass   : "authentication-icon question-icon",
         textHidden  : false,
         passHidden  : false,
         checkHidden : false,
         textValue   : "",
@@ -1133,32 +1035,32 @@ function* runTests() {
     };
     action = {
         buttonClick : "ok",
         setCheckbox : true,
         textField   : "username",
         passField   : "password",
     };
     if (usePromptService) {
-        startCallbackTimer();
+        promptDone = handlePrompt(action);
+
         checkVal.value = false;
         isOK = prompter.promptAuth(window, channel, level, authinfo, "Check me out!", checkVal);
         is(isOK, true, "checked expected retval");
         is(authinfo.username, "username", "checking filled username");
         is(authinfo.password, "password", "checking filled password");
         is(checkVal.value, true, "expected checkbox setting");
-        ok(didDialog, "handleDialog was invoked");
+
+        yield promptDone;
     }
 
-    yield Promise.resolve();
 
-    // ===== test 101 =====
-    // promptAuth with long realm
+    // =====
     // (promptAuth is only accessible from the prompt service)
-    testNum++;
+    info("Starting test: promptAuth with long realm");
     state = {
         msg : 'A username and password are being requested by http://example.com. The site '  +
               'says: "abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi ' +
               'abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi abcdefghi '        +
               'abcdefghi \u2026"',
         title : "TestTitle",
         iconClass   : "authentication-icon question-icon",
         textHidden  : false,
@@ -1173,54 +1075,64 @@ function* runTests() {
     };
     action = {
         buttonClick : "ok",
         setCheckbox : true,
         textField   : "username",
         passField   : "password",
     };
     if (usePromptService) {
-        startCallbackTimer();
+        promptDone = handlePrompt(action);
+
         checkVal.value = false;
         var longString = "";
         for (var i = 0; i < 20; i++)
             longString += "abcdefghi "; // 200 chars long
         authinfo.realm = longString;
         authinfo.username = "";
         authinfo.password = "";
         isOK = prompter.promptAuth(window, channel, level, authinfo, "Check me out!", checkVal);
         is(isOK, true, "checked expected retval");
         is(authinfo.username, "username", "checking filled username");
         is(authinfo.password, "password", "checking filled password");
         is(checkVal.value, true, "expected checkbox setting");
-        ok(didDialog, "handleDialog was invoked");
+
+        yield promptDone;
     }
 }
 
-let testNum;
-let pollTimer;
+let gChromeScript;
 let state, action;
 
 /*
  * Run the body of the 3 times:
  * - 1st pass: with window-modal prompts, using nsIPromptService
  * - 2nd pass: still window-modal, using nsIPrompt directly (via nsIPromptFactory)
  * - 3rd pass: with tab-modal prompts. Can't opt into these via * nsIPromptService.
  */
 
 add_task(function* runPromptTests() {
+  info("Process type: " + SpecialPowers.Services.appinfo.processType);
+
+  let url = SimpleTest.getTestFileURL("chromeScript.js");
+  gChromeScript = SpecialPowers.loadChromeScript(url);
 
   isTabModal = false; usePromptService = true;
+  info("Running tests with: isTabModal=" + isTabModal + ", usePromptService=" + usePromptService);
   yield* runTests();
 
   isTabModal = false; usePromptService = false;
+  info("Running tests with: isTabModal=" + isTabModal + ", usePromptService=" + usePromptService);
   yield* runTests();
 
-  if (getTabModalPromptBox(window)) {
+  if (SpecialPowers.getBoolPref("prompts.tab_modal.enabled")) {
       isTabModal = true; usePromptService = false;
+      info("Running tests with: isTabModal=" + isTabModal + ", usePromptService=" + usePromptService);
       yield* runTests();
   }
+
+  gChromeScript.destroy();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/prompts/test/test_modal_select.html
+++ b/toolkit/components/prompts/test/test_modal_select.html
@@ -1,13 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Modal Prompts Test</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
   <script type="text/javascript" src="prompt_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Prompter tests: modal prompts
 <p id="display"></p>
 
 <div id="content" style="display: none">
@@ -15,150 +16,138 @@ Prompter tests: modal prompts
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript;version=1.8">
 
 let prompter = Cc["@mozilla.org/embedcomp/prompt-service;1"].
                getService(Ci.nsIPromptService2);
 
-function checkExpectedSelectState(doc, state, action) {
-    let msg = doc.getElementById("info.txt").value;
-    // XXX check title? OS X has title in content
-    let dialog = doc.getElementsByTagName("dialog")[0];
-    let listbox = doc.getElementById("list");
-
-    is(msg, state.msg, "Checking expected message");
+function checkPromptState(promptState, expectedState) {
     // XXX check title? OS X has title in content
-    // Compare listbox contents
-    let count = listbox.itemCount;
-    is(count, state.items.length, "Checking listbox length");
-    if (count)
-        is(listbox.selectedIndex, 0, "Checking selected index");
-
-    for (let i = 0; i < count; i++) {
-        let item = listbox.getItemAtIndex(i).label;
-        is(item, items[i], "Checking item #" + i + " label");
-    }
-
     // XXX check focused element
     // XXX check button labels?
 
-    /* Actions */
+    is(promptState.msg, state.msg, "Checking expected message");
 
-    if (action.selectItem) {
-        listbox.selectedIndex = 1;
-    }
+    // Compare listbox contents
+    is(promptState.items.length, expectedState.items.length, "Checking listbox length");
 
-    if (action.buttonClick == "ok") {
-        dialog.acceptDialog();
-    } else if (action.buttonClick == "cancel") {
-        dialog.cancelDialog();
+    if (promptState.items.length)
+      is(promptState.selectedIndex, 0, "Checking selected index");
+
+    for (let i = 0; i < promptState.items; i++) {
+        is(promptState.items[i], expectedState.items[i], "Checking list item #" + i);
     }
-
-    didDialog = true;
 }
 
-/*
- * handleDialog
- *
- * Invoked a short period of time after calling startCallbackTimer(), and
- * allows testing the actual prompt dialog while it's being displayed. Tests
- * should call startCallbackTimer() each time the auth dialog is expected (the
- * timer is a one-shot).
- */
-function handleDialog(doc, testNum) {
-    ok(true, "--- handleDialog for test " + testNum + " ---");
-
-    checkExpectedSelectState(doc, state, action);
-}
-
-let testNum   = 0;
 let selectVal = {};
 let isOK;
-let state, action;
+let gChromeScript, state, action;
 
-isSelectDialog = true;
-isTabModal = false;
-usePromptService = true;
+function handlePrompt() {
+  return new Promise(resolve => {
+    gChromeScript.addMessageListener("promptHandled", function handled(msg) {
+      gChromeScript.removeMessageListener("promptHandled", handled);
+      checkPromptState(msg.promptState, state);
+      resolve(true);
+    });
+    gChromeScript.sendAsyncMessage("handlePrompt", { action: action, isSelect: true});
+  });
+}
+
+let url = SimpleTest.getTestFileURL("chromeScript.js");
+gChromeScript = SpecialPowers.loadChromeScript(url);
 
-// ===== test 1 =====
-// Select (0 items, ok)
-testNum++;
-state = {
-    msg   : "This is the select text.",
-    title : "TestTitle",
-    items : [],
-};
-action = {
-    buttonClick: "ok",
-};
-startCallbackTimer();
-items = [];
-selectVal.value = null; // outparam, just making sure.
-isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
-is(isOK, true, "checked expected retval");
-is(selectVal.value, -1, "checking selected index");
-ok(didDialog, "handleDialog was invoked");
+// =====
+add_task(function* test_select_empty_list() {
+    info("Starting test: Select (0 items, ok)");
+    state = {
+        msg   : "This is the select text.",
+        title : "TestTitle",
+        items : [],
+    };
+    action = {
+        buttonClick: "ok",
+    };
+    promptDone = handlePrompt(action);
+    items = [];
+    selectVal.value = null; // outparam, just making sure.
+    isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
+    is(isOK, true, "checked expected retval");
+    is(selectVal.value, -1, "checking selected index");
+
+    yield promptDone;
+});
 
-// ===== test 2 =====
-// Select (3 items, ok)
-testNum++;
-state = {
-    msg   : "This is the select text.",
-    title : "TestTitle",
-    items : ["one", "two", "three"],
-};
-action = {
-    buttonClick: "ok",
-};
-startCallbackTimer();
-items = ["one", "two", "three"];
-selectVal.value = null; // outparam, just making sure.
-isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
-is(isOK, true, "checked expected retval");
-is(selectVal.value, 0, "checking selected index");
-ok(didDialog, "handleDialog was invoked");
+// =====
+add_task(function* test_select_ok() {
+    info("Starting test: Select (3 items, ok)");
+    state = {
+        msg   : "This is the select text.",
+        title : "TestTitle",
+        items : ["one", "two", "three"],
+    };
+    action = {
+        buttonClick: "ok",
+    };
+    promptDone = handlePrompt(action);
+    items = ["one", "two", "three"];
+    selectVal.value = null; // outparam, just making sure.
+    isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
+    is(isOK, true, "checked expected retval");
+    is(selectVal.value, 0, "checking selected index");
+
+    yield promptDone;
+});
 
-// ===== test 3 =====
-// Select (3 items, selection changed, ok)
-testNum++;
-state = {
-    msg   : "This is the select text.",
-    title : "TestTitle",
-    items : ["one", "two", "three"],
-};
-action = {
-    buttonClick: "ok",
-    selectItem: 1,
-};
-startCallbackTimer();
-items = ["one", "two", "three"];
-selectVal.value = null; // outparam, just making sure.
-isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
-is(isOK, true, "checked expected retval");
-is(selectVal.value, 1, "checking selected index");
-ok(didDialog, "handleDialog was invoked");
+// =====
+add_task(function* test_select_item() {
+    info("Starting test: Select (3 items, selection changed, ok)");
+    state = {
+        msg   : "This is the select text.",
+        title : "TestTitle",
+        items : ["one", "two", "three"],
+    };
+    action = {
+        buttonClick: "ok",
+        selectItem: 1,
+    };
+    promptDone = handlePrompt(action);
+    items = ["one", "two", "three"];
+    selectVal.value = null; // outparam, just making sure.
+    isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
+    is(isOK, true, "checked expected retval");
+    is(selectVal.value, 1, "checking selected index");
+
+    yield promptDone;
+});
 
-// ===== test 4 =====
-// Select (3 items, cancel)
-testNum++;
-state = {
-    msg   : "This is the select text.",
-    title : "TestTitle",
-    items : ["one", "two", "three"],
-};
-action = {
-    buttonClick: "cancel",
-};
-startCallbackTimer();
-items = ["one", "two", "three"];
-selectVal.value = null; // outparam, just making sure.
-isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
-is(isOK, false, "checked expected retval");
-is(selectVal.value, 0, "checking selected index");
-ok(didDialog, "handleDialog was invoked");
+// =====
+add_task(function* test_cancel_prompt() {
+    info("Starting test: Select (3 items, cancel)");
+    state = {
+        msg   : "This is the select text.",
+        title : "TestTitle",
+        items : ["one", "two", "three"],
+    };
+    action = {
+        buttonClick: "cancel",
+    };
+    promptDone = handlePrompt(action);
+    items = ["one", "two", "three"];
+    selectVal.value = null; // outparam, just making sure.
+    isOK = prompter.select(window, "TestTitle", "This is the select text.", items.length, items, selectVal);
+    is(isOK, false, "checked expected retval");
+    is(selectVal.value, 0, "checking selected index");
 
+    yield promptDone;
+});
+
+// =====
+add_task(function* cleanup() {
+    gChromeScript.destroy();
+});
 
 </script>
 </pre>
 </body>
 </html>