Bug 1266618 - Fix test_prompt.html for E10S by using a ChromeWindow. r=dolske draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Thu, 25 Aug 2016 00:06:51 -0700
changeset 405263 7fd38c3ad521d5d79442866bcf44901cf72d44bb
parent 405181 0bb96221333285aab696b40e5d02ed4b489276b3
child 405278 8b5e3ebd9f04869ed940575094939a7d003826e0
child 405281 c1a4c6ab4a533d1d603f9b75e489269e1363724c
push id27452
push usermozilla@noorenberghe.ca
push dateThu, 25 Aug 2016 07:11:43 +0000
reviewersdolske
bugs1266618
milestone51.0a1
Bug 1266618 - Fix test_prompt.html for E10S by using a ChromeWindow. r=dolske This switches getPrompt to use a ChromeWindow which matches what the shipping code does for e10s. MozReview-Commit-ID: DcVSr6JfmdK
toolkit/components/passwordmgr/test/mochitest/mochitest.ini
toolkit/components/passwordmgr/test/mochitest/test_prompt.html
toolkit/components/prompts/test/prompt_common.js
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
+++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
@@ -42,13 +42,13 @@ skip-if = toolkit == 'android' # autocom
 [test_formless_submit.html]
 [test_formless_submit_navigation.html]
 [test_formless_submit_navigation_negative.html]
 [test_input_events.html]
 [test_input_events_for_identical_values.html]
 [test_maxlength.html]
 [test_passwords_in_type_password.html]
 [test_prompt.html]
-skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
+skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_promptAuth.html]
 skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_recipe_login_fields.html]
 [test_xhr_2.html]
--- a/toolkit/components/passwordmgr/test/mochitest/test_prompt.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt.html
@@ -21,26 +21,36 @@ var state, action;
 var uname  = { value: null };
 var pword  = { value: null };
 var result = { value: null };
 var isOk;
 
 // Force parent to not look for tab-modal prompts, as they're not used for auth prompts.
 isTabModal = false;
 
-const Cc_promptFac= Cc["@mozilla.org/passwordmanager/authpromptfactory;1"];
-ok(Cc_promptFac != null, "Access Cc[@mozilla.org/passwordmanager/authpromptfactory;1]");
+let prompterParent = runInParent(() => {
+  const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+  const promptFac = Cc["@mozilla.org/passwordmanager/authpromptfactory;1"].
+                    getService(Ci.nsIPromptFactory);
+
+  Cu.import("resource://gre/modules/Services.jsm");
+  let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+  let prompter1 = promptFac.getPrompt(chromeWin, Ci.nsIAuthPrompt);
 
-const Ci_promptFac = Ci.nsIPromptFactory;
-ok(Ci_promptFac != null, "Access Ci.nsIPromptFactory");
+  addMessageListener("proxyPrompter", function onMessage(msg) {
+    let rv = prompter1[msg.methodName](...msg.args);
+    return {
+      rv,
+      // Send the args back to content so out/inout args can be checked.
+      args: msg.args,
+    };
+  });
+});
 
-const promptFac = Cc_promptFac.getService(Ci_promptFac);
-ok(promptFac != null, "promptFac getService()");
-
-var prompter1 = promptFac.getPrompt(window, Ci.nsIAuthPrompt);
+let prompter1 = new PrompterProxy(prompterParent);
 
 const defaultTitle = "the title";
 const defaultMsg = "the message";
 
 function initLogins() {
   const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
   var login1, login2A, login2B, login2C, login2D, login2E;
   var pwmgr = Cc["@mozilla.org/login-manager;1"].
@@ -79,17 +89,17 @@ function initLogins() {
   pwmgr.addLogin(login2D);
   pwmgr.addLogin(login2E);
 }
 
 add_task(function* setup() {
   runInParent(initLogins);
 });
 
-add_task(function* test_1() {
+add_task(function* test_prompt_accept() {
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "abc",
     passValue   : "",
     iconClass   : "question-icon",
     titleHidden : true,
     textHidden  : false,
@@ -108,17 +118,17 @@ add_task(function* test_1() {
   isOk = prompter1.prompt(defaultTitle, defaultMsg, "http://example.com",
                           Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result);
   yield promptDone;
 
   ok(isOk, "Checking dialog return value (accept)");
   is(result.value, "xyz", "Checking prompt() returned value");
 });
 
-add_task(function* test_2() {
+add_task(function* test_prompt_cancel() {
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "abc",
     passValue   : "",
     iconClass   : "question-icon",
     titleHidden : true,
     textHidden  : false,
@@ -134,17 +144,17 @@ add_task(function* test_2() {
   };
   promptDone = handlePrompt(state, action);
   isOk = prompter1.prompt(defaultTitle, defaultMsg, "http://example.com",
                           Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result);
   yield promptDone;
   ok(!isOk, "Checking dialog return value (cancel)");
 });
 
-add_task(function* test_10() {
+add_task(function* test_promptPassword_defaultAccept() {
   // Default password provided, existing logins are ignored.
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "inputpw",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -164,17 +174,17 @@ add_task(function* test_10() {
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://example.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "secret", "Checking returned password");
 });
 
-add_task(function* test_11() {
+add_task(function* test_promptPassword_defaultCancel() {
   // Default password provided, existing logins are ignored.
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "inputpw",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -192,17 +202,17 @@ add_task(function* test_11() {
   pword.value = "inputpw";
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://example.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   yield promptDone;
   ok(!isOk, "Checking dialog return value (cancel)");
 });
 
-add_task(function* test_12() {
+add_task(function* test_promptPassword_emptyAccept() {
   // No default password provided, realm does not match existing login.
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -222,26 +232,26 @@ add_task(function* test_12() {
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://nonexample.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "secret", "Checking returned password");
 });
 
-add_task(function* test_13() {
+add_task(function* test_promptPassword_saved() {
   // No default password provided, matching login is returned w/o prompting.
   pword.value = null;
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://example.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "examplepass", "Checking returned password");
 });
 
-add_task(function* test_14() {
+add_task(function* test_promptPassword_noMatchingPasswordForEmptyUN() {
   // No default password provided, none of the logins from this host are
   // password-only so the user is prompted.
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "",
     iconClass   : "authentication-icon question-icon",
@@ -262,64 +272,64 @@ add_task(function* test_14() {
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://example2.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "secret", "Checking returned password");
 });
 
-add_task(function* test_15() {
+add_task(function* test_promptPassword_matchingPWForUN() {
   // No default password provided, matching login is returned w/o prompting.
   pword.value = null;
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://user1name@example2.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "user1pass", "Checking returned password");
 });
 
-add_task(function* test_16() {
+add_task(function* test_promptPassword_matchingPWForUN2() {
   // No default password provided, matching login is returned w/o prompting.
   pword.value = null;
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://user2name@example2.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "user2pass", "Checking returned password");
 });
 
-add_task(function* test_17() {
+add_task(function* test_promptPassword_matchingPWForUN3() {
   // No default password provided, matching login is returned w/o prompting.
   pword.value = null;
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://user3%2Ename%40host@example2.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "user3pass", "Checking returned password");
 });
 
-add_task(function* test_18() {
+add_task(function* test_promptPassword_extraAt() {
   // No default password provided, matching login is returned w/o prompting.
   pword.value = null;
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://100@beef@example2.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "user3pass", "Checking returned password");
 });
 
-add_task(function* test_19() {
+add_task(function* test_promptPassword_usernameEncoding() {
   // No default password provided, matching login is returned w/o prompting.
   pword.value = null;
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "http://100%25beef@example2.com",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "user3pass", "Checking returned password");
 
   // XXX test saving a password with  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY
 });
 
-add_task(function* test_30() {
+add_task(function* test_promptPassword_realm() {
   // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -339,17 +349,17 @@ add_task(function* test_30() {
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "example2.com:80 (somerealm)",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "fill2pass", "Checking returned password");
 });
 
-add_task(function* test_31() {
+add_task(function* test_promptPassword_realm2() {
   // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -369,17 +379,17 @@ add_task(function* test_31() {
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptPassword(defaultTitle, defaultMsg, "example2.com:80 (somerealm)",
                                   Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(pword.value, "fill2pass", "Checking returned password");
 });
 
-add_task(function* test_100() {
+add_task(function* test_promptUsernameAndPassword_accept() {
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "inuser",
     passValue   : "inpass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
     textHidden  : false,
@@ -394,24 +404,24 @@ add_task(function* test_100() {
     buttonClick : "ok",
     textField   : "outuser",
     passField   : "outpass",
   };
   uname.value = "inuser";
   pword.value = "inpass";
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://nonexample.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "outuser", "Checking returned username");
   is(pword.value, "outpass", "Checking returned password");
 });
 
-add_task(function* test_101() {
+add_task(function* test_promptUsernameAndPassword_cancel() {
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "inuser",
     passValue   : "inpass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
     textHidden  : false,
@@ -424,22 +434,22 @@ add_task(function* test_101() {
   };
   action = {
     buttonClick : "cancel",
   };
   uname.value = "inuser";
   pword.value = "inpass";
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://nonexample.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
   yield promptDone;
   ok(!isOk, "Checking dialog return value (cancel)");
 });
 
-add_task(function* test_102() {
+add_task(function* test_promptUsernameAndPassword_autofill() {
   // test filling in existing password-only login
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "examplepass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -453,24 +463,24 @@ add_task(function* test_102() {
   };
   action = {
     buttonClick : "ok",
   };
   uname.value = null;
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://example.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "", "Checking returned username");
   is(pword.value, "examplepass", "Checking returned password");
 });
 
-add_task(function* test_103() {
+add_task(function* test_promptUsernameAndPassword_multipleExisting() {
   // test filling in existing login (undetermined from multiple selection)
   // user2name/user2pass would also be valid to fill here.
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "user1name",
     passValue   : "user1pass",
     iconClass   : "authentication-icon question-icon",
@@ -485,24 +495,24 @@ add_task(function* test_103() {
   };
   action = {
     buttonClick : "ok",
   };
   uname.value = null;
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://example2.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   ok(uname.value == "user1name" || uname.value == "user2name", "Checking returned username");
   ok(pword.value == "user1pass" || uname.value == "user2pass", "Checking returned password");
 });
 
-add_task(function* test_104() {
+add_task(function* test_promptUsernameAndPassword_multipleExisting1() {
   // test filling in existing login (user1 from multiple selection)
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "user1name",
     passValue   : "user1pass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -516,24 +526,24 @@ add_task(function* test_104() {
   };
   action = {
     buttonClick : "ok",
   };
   uname.value = "user1name";
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://example2.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "user1name", "Checking returned username");
   is(pword.value, "user1pass", "Checking returned password");
 });
 
-add_task(function* test_105() {
+add_task(function* test_promptUsernameAndPassword_multipleExisting2() {
   // test filling in existing login (user2 from multiple selection)
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "user2name",
     passValue   : "user2pass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -547,24 +557,24 @@ add_task(function* test_105() {
   };
   action = {
     buttonClick : "ok",
   };
   uname.value = "user2name";
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://example2.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "user2name", "Checking returned username");
   is(pword.value, "user2pass", "Checking returned password");
 });
 
-add_task(function* test_106() {
+add_task(function* test_promptUsernameAndPassword_passwordChange() {
   // test changing password
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "user2name",
     passValue   : "user2pass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -579,24 +589,24 @@ add_task(function* test_106() {
   action = {
     buttonClick : "ok",
     passField   : "NEWuser2pass",
   };
   uname.value = "user2name";
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://example2.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "user2name", "Checking returned username");
   is(pword.value, "NEWuser2pass", "Checking returned password");
 });
 
-add_task(function* test_107() {
+add_task(function* test_promptUsernameAndPassword_changePasswordBack() {
   // test changing password (back to original value)
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "user2name",
     passValue   : "NEWuser2pass",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -611,24 +621,24 @@ add_task(function* test_107() {
   action = {
     buttonClick : "ok",
     passField   : "user2pass",
   };
   uname.value = "user2name";
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "http://example2.com",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "user2name", "Checking returned username");
   is(pword.value, "user2pass", "Checking returned password");
 });
 
-add_task(function* test_120() {
+add_task(function* test_promptUsernameAndPassword_realm() {
   // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -644,24 +654,24 @@ add_task(function* test_120() {
     buttonClick : "ok",
     textField   : "fill2user",
     passField   : "fill2pass",
   };
   uname.value = null;
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "example2.com:80 (somerealm)",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "fill2user", "Checking returned username");
   is(pword.value, "fill2pass", "Checking returned password");
 });
 
-add_task(function* test_121() {
+add_task(function* test_promptUsernameAndPassword_realm2() {
   // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
   state = {
     msg         : "the message",
     title       : "the title",
     textValue   : "",
     passValue   : "",
     iconClass   : "authentication-icon question-icon",
     titleHidden : true,
@@ -677,17 +687,17 @@ add_task(function* test_121() {
     buttonClick : "ok",
     textField   : "fill2user",
     passField   : "fill2pass",
   };
   uname.value = null;
   pword.value = null;
   promptDone = handlePrompt(state, action);
   isOk = prompter1.promptUsernameAndPassword(defaultTitle, defaultMsg, "example2.com:80 (somerealm)",
-                                  Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
+                                             Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
   yield promptDone;
   ok(isOk, "Checking dialog return value (accept)");
   is(uname.value, "fill2user", "Checking returned username");
   is(pword.value, "fill2pass", "Checking returned password");
 });
 
 </script>
 </pre>
--- a/toolkit/components/prompts/test/prompt_common.js
+++ b/toolkit/components/prompts/test/prompt_common.js
@@ -88,8 +88,62 @@ function checkEchoedAuthInfo(expectedSta
     let username = doc.getElementById("user").textContent;
     let password = doc.getElementById("pass").textContent;
     let authok = doc.getElementById("ok").textContent;
 
     is(authok, "PASS", "Checking for successful authentication");
     is(username, expectedState.user, "Checking for echoed username");
     is(password, expectedState.pass, "Checking for echoed password");
 }
+
+/**
+ * Create a Proxy to relay method calls on an nsIAuthPrompt prompter to a chrome script which can
+ * perform the calls in the parent. Out and inout params will be copied back from the parent to
+ * content.
+ *
+ * @param chromeScript The reference to the chrome script that will listen to `proxyPrompter`
+ *                     messages in the parent and call the `methodName` method.
+ *                     The return value from the message handler should be an object with properties:
+ * `rv` - containing the return value of the method call.
+ * `args` - containing the array of arguments passed to the method since out or inout ones could have
+ *          been modified.
+ */
+function PrompterProxy(chromeScript) {
+  return new Proxy({}, {
+    get(target, prop, receiver) {
+      return (...args) => {
+        // Array of indices of out/inout params to copy from the parent back to the caller.
+        let outParams = [];
+
+        switch (prop) {
+          case "prompt": {
+            outParams = [/* result */ 5];
+            break;
+          }
+          case "promptPassword": {
+            outParams = [/* pwd */ 4];
+            break;
+          }
+          case "promptUsernameAndPassword": {
+            outParams = [/* user */ 4, /* pwd */ 5];
+            break;
+          }
+          default: {
+            throw new Error("Unknown nsIAuthPrompt method");
+            break;
+          }
+        }
+
+        let result = chromeScript.sendSyncMessage("proxyPrompter", {
+          args,
+          methodName: prop,
+        })[0][0];
+
+        for (let outParam of outParams) {
+          // Copy the out or inout param value over the original
+          args[outParam].value = result.args[outParam].value;
+        }
+
+        return result.rv;
+      };
+    },
+  });
+}