PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/toolkit/components/passwordmgr/test/test_prompt.html

https://github.com/edwindotcom/gecko-dev
HTML | 1109 lines | 980 code | 129 blank | 0 comment | 0 complexity | ae67ff109e1f2a0375f2682e220b96e2 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-3.0, MIT, AGPL-1.0, MPL-2.0-no-copyleft-exception, MPL-2.0, GPL-2.0, JSON, 0BSD, LGPL-2.1, BSD-2-Clause
  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title>Test for Login Manager</title>
  5. <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  6. <script type="text/javascript" src="pwmgr_common.js"></script>
  7. <script type="text/javascript" src="prompt_common.js"></script>
  8. <script type="text/javascript" src="notification_common.js"></script>
  9. <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  10. </head>
  11. <body>
  12. Login Manager test: username/password prompts
  13. <p id="display"></p>
  14. <div id="content" style="display: none">
  15. <iframe id="iframe"></iframe>
  16. </div>
  17. <pre id="test">
  18. <script class="testbody" type="text/javascript">
  19. SimpleTest.waitForExplicitFinish();
  20. /** Test for Login Manager: username / password prompts. **/
  21. var pwmgr, ioService, observerService;
  22. var tmplogin, login1, login2A, login2B, login2C, login2D, login2E, login3A, login3B, login4, proxyLogin;
  23. var mozproxy, proxiedHost = "http://mochi.test:8888";
  24. var proxyChannel;
  25. var testNum = 1;
  26. function initLogins(pi) {
  27. observerService = Cc["@mozilla.org/observer-service;1"].
  28. getService(Ci.nsIObserverService);
  29. observerService.addObserver(storageObserver, "passwordmgr-storage-changed", false);
  30. pwmgr = Cc["@mozilla.org/login-manager;1"].
  31. getService(Ci.nsILoginManager);
  32. ioService = Cc["@mozilla.org/network/io-service;1"].
  33. getService(Ci.nsIIOService);
  34. mozproxy = "moz-proxy://" + SpecialPowers.wrap(pi).host + ":" +
  35. SpecialPowers.wrap(pi).port;
  36. tmpLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  37. createInstance(Ci.nsILoginInfo);
  38. login1 = Cc["@mozilla.org/login-manager/loginInfo;1"].
  39. createInstance(Ci.nsILoginInfo);
  40. login2A = Cc["@mozilla.org/login-manager/loginInfo;1"].
  41. createInstance(Ci.nsILoginInfo);
  42. login2B = Cc["@mozilla.org/login-manager/loginInfo;1"].
  43. createInstance(Ci.nsILoginInfo);
  44. login2C = Cc["@mozilla.org/login-manager/loginInfo;1"].
  45. createInstance(Ci.nsILoginInfo);
  46. login2D = Cc["@mozilla.org/login-manager/loginInfo;1"].
  47. createInstance(Ci.nsILoginInfo);
  48. login2E = Cc["@mozilla.org/login-manager/loginInfo;1"].
  49. createInstance(Ci.nsILoginInfo);
  50. login3A = Cc["@mozilla.org/login-manager/loginInfo;1"].
  51. createInstance(Ci.nsILoginInfo);
  52. login3B = Cc["@mozilla.org/login-manager/loginInfo;1"].
  53. createInstance(Ci.nsILoginInfo);
  54. login4 = Cc["@mozilla.org/login-manager/loginInfo;1"].
  55. createInstance(Ci.nsILoginInfo);
  56. proxyLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
  57. createInstance(Ci.nsILoginInfo);
  58. login1.init("http://example.com", null, "http://example.com",
  59. "", "examplepass", "", "");
  60. login2A.init("http://example2.com", null, "http://example2.com",
  61. "user1name", "user1pass", "", "");
  62. login2B.init("http://example2.com", null, "http://example2.com",
  63. "user2name", "user2pass", "", "");
  64. login2C.init("http://example2.com", null, "http://example2.com",
  65. "user3.name@host", "user3pass", "", "");
  66. login2D.init("http://example2.com", null, "http://example2.com",
  67. "100@beef", "user3pass", "", "");
  68. login2E.init("http://example2.com", null, "http://example2.com",
  69. "100%beef", "user3pass", "", "");
  70. login3A.init("http://mochi.test:8888", null, "mochitest",
  71. "mochiuser1", "mochipass1", "", "");
  72. login3B.init("http://mochi.test:8888", null, "mochitest2",
  73. "mochiuser2", "mochipass2", "", "");
  74. login4.init("http://mochi.test:8888", null, "mochitest3",
  75. "mochiuser3", "mochipass3-old", "", "");
  76. proxyLogin.init(mozproxy, null, "Proxy Realm",
  77. "proxuser", "proxpass", "", "");
  78. pwmgr.addLogin(login1);
  79. pwmgr.addLogin(login2A);
  80. pwmgr.addLogin(login2B);
  81. pwmgr.addLogin(login2C);
  82. pwmgr.addLogin(login2D);
  83. pwmgr.addLogin(login2E);
  84. pwmgr.addLogin(login3A);
  85. pwmgr.addLogin(login3B);
  86. pwmgr.addLogin(login4);
  87. pwmgr.addLogin(proxyLogin);
  88. }
  89. function finishTest() {
  90. try {
  91. ok(true, "finishTest removing testing logins...");
  92. observerService.removeObserver(storageObserver, "passwordmgr-storage-changed");
  93. dumpLogins(pwmgr);
  94. ok(true, "removing login 1...");
  95. pwmgr.removeLogin(login1);
  96. ok(true, "removing login 2A...");
  97. pwmgr.removeLogin(login2A);
  98. ok(true, "removing login 2B...");
  99. pwmgr.removeLogin(login2B);
  100. ok(true, "removing login 2C...");
  101. pwmgr.removeLogin(login2C);
  102. ok(true, "removing login 2D...");
  103. pwmgr.removeLogin(login2D);
  104. ok(true, "removing login 2E...");
  105. pwmgr.removeLogin(login2E);
  106. ok(true, "removing login 3A...");
  107. pwmgr.removeLogin(login3A);
  108. ok(true, "removing login 3B...");
  109. pwmgr.removeLogin(login3B);
  110. ok(true, "removing login 4...");
  111. pwmgr.removeLogin(login4);
  112. ok(true, "removing proxyLogin...");
  113. pwmgr.removeLogin(proxyLogin);
  114. } catch (e) {
  115. ok(false, "finishTest FAILED: " + e);
  116. }
  117. ok(true, "whee, done!");
  118. SimpleTest.finish();
  119. }
  120. function proxyChannelListener() { }
  121. proxyChannelListener.prototype = {
  122. onStartRequest: function(request, context) {
  123. doTests();
  124. },
  125. onStopRequest: function(request, context, status) { }
  126. };
  127. var resolveCallback = SpecialPowers.wrapCallbackObject({
  128. QueryInterface : function (iid) {
  129. const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports];
  130. if (!interfaces.some( function(v) { return iid.equals(v) } ))
  131. throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
  132. return this;
  133. },
  134. onProxyAvailable : function (req, uri, pi, status) {
  135. initLogins(pi);
  136. // I'm cheating a bit here... We should probably do some magic foo to get
  137. // something implementing nsIProxiedProtocolHandler and then call
  138. // NewProxiedChannel(), so we have something that's definately a proxied
  139. // channel. But Mochitests use a proxy for a number of hosts, so just
  140. // requesting a normal channel will give us a channel that's proxied.
  141. // The proxyChannel needs to move to at least on-modify-request to
  142. // have valid ProxyInfo, but we use OnStartRequest during startup()
  143. // for simplicity.
  144. proxyChannel = ioService.newChannel(proxiedHost, null, null);
  145. proxyChannel.asyncOpen(SpecialPowers.wrapCallbackObject(new proxyChannelListener()), null);
  146. }
  147. });
  148. function startup() {
  149. //need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy.
  150. var ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"].
  151. getService(SpecialPowers.Ci.nsIIOService);
  152. var pps = SpecialPowers.Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
  153. var uri = ios.newURI("http://example.com", null, null);
  154. pps.asyncResolve(uri, 0, resolveCallback);
  155. }
  156. function addNotificationCallback(cb) {
  157. storageObserver.notificationCallbacks.push(cb);
  158. }
  159. var storageObserver = SpecialPowers.wrapCallbackObject({
  160. notificationCallbacks: [],
  161. QueryInterface : function (iid) {
  162. const interfaces = [Ci.nsIObserver,
  163. Ci.nsISupports, Ci.nsISupportsWeakReference];
  164. if (!interfaces.some( function(v) { return iid.equals(v) } ))
  165. throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
  166. return this;
  167. },
  168. observe : function (subject, topic, data) {
  169. ok(true, ".");
  170. ok(true, "observer for " + topic + " / " + data);
  171. ok(true, "Time is " + (new Date()).toUTCString());
  172. var wrapped = SpecialPowers.wrap(subject);
  173. try {
  174. switch (data) {
  175. case "addLogin":
  176. ok(wrapped.QueryInterface(Ci.nsILoginInfo), "subject QI 1");
  177. ok(wrapped.QueryInterface(Ci.nsILoginMetaInfo), "subject QI 2");
  178. dumpLogin("added: ", subject);
  179. break;
  180. case "modifyLogin":
  181. var arr = wrapped.QueryInterface(Ci.nsIArray);
  182. ok(arr, "subject QI");
  183. is(arr.length, 2, "should be 2 items");
  184. var oldLogin = arr.queryElementAt(0, Ci.nsILoginInfo);
  185. var newLogin = arr.queryElementAt(1, Ci.nsILoginInfo);
  186. dumpLogin("oldLogin: ", oldLogin);
  187. dumpLogin("newLogin: ", newLogin);
  188. break;
  189. case "removeLogin":
  190. ok(wrapped.QueryInterface(Ci.nsILoginInfo), "subject QI 1");
  191. ok(wrapped.QueryInterface(Ci.nsILoginMetaInfo), "subject QI 2");
  192. dumpLogin("removed: ", subject);
  193. break;
  194. case "removeAllLogins":
  195. is(subject, null, "no subject");
  196. break;
  197. case "hostSavingEnabled":
  198. case "hostSavingDisabled":
  199. ok(subject instanceof Ci.nsISupportsString, "subject QI");
  200. ok(true, "state is: " + subject.data);
  201. break;
  202. default:
  203. do_throw("Unhandled notification: " + data + " / " + topic);
  204. }
  205. if (this.notificationCallbacks.length)
  206. this.notificationCallbacks.splice(0, 1)[0]();
  207. } catch (e) {
  208. ok(false, "OBSERVER FAILED: " + e);
  209. }
  210. }
  211. });
  212. /*
  213. * handleDialog
  214. *
  215. * Invoked a short period of time after calling startCallbackTimer(), and
  216. * allows testing the actual auth dialog while it's being displayed. Tests
  217. * should call startCallbackTimer() each time the auth dialog is expected (the
  218. * timer is a one-shot).
  219. */
  220. function handleDialog(doc, testNum) {
  221. ok(true, "."); // make it easier to see next line in logs.
  222. ok(true, "handleDialog running for test " + testNum);
  223. ok(true, "Time is " + (new Date()).toUTCString());
  224. dumpNotifications();
  225. var clickOK = true;
  226. var body = doc.getElementById("info.body");
  227. var userfield = doc.getElementById("loginTextbox");
  228. var passfield = doc.getElementById("password1Textbox");
  229. var username = userfield.getAttribute("value");
  230. var password = passfield.getAttribute("value");
  231. var dialog = doc.getElementById("commonDialog");
  232. switch(testNum) {
  233. case 1:
  234. is(username, "abc", "Checking provided username");
  235. userfield.setAttribute("value", "xyz");
  236. // Temporarily commented out because of Bug #718543
  237. // is(doc.activeElement, userfield.inputField, "focus correct for test" + testNum);
  238. // doc.commandDispatcher.rewindFocus();
  239. // is(doc.activeElement, body, "description focusable");
  240. break;
  241. case 2:
  242. clickOK = false;
  243. break;
  244. case 10:
  245. is(password, "inputpw", "Checking provided password");
  246. passfield.setAttribute("value", "secret");
  247. // Temporarily commented out because of Bug #718543
  248. // is(doc.activeElement, passfield.inputField, "focus correct for test" + testNum);
  249. break;
  250. case 11:
  251. is(password, "inputpw", "Checking provided password");
  252. clickOK = false;
  253. break;
  254. case 12:
  255. is(password, "", "Checking provided password");
  256. passfield.setAttribute("value", "secret");
  257. break;
  258. case 14:
  259. is(password, "", "Checking provided password");
  260. passfield.setAttribute("value", "secret");
  261. break;
  262. case 30:
  263. case 31:
  264. is(password, "", "Checking provided password");
  265. passfield.setAttribute("value", "fill2pass");
  266. break;
  267. case 100:
  268. is(username, "inuser", "Checking provided username");
  269. is(password, "inpass", "Checking provided password");
  270. userfield.setAttribute("value", "outuser");
  271. passfield.setAttribute("value", "outpass");
  272. break;
  273. case 101:
  274. clickOK = false;
  275. break;
  276. case 102:
  277. is(username, "", "Checking provided username");
  278. is(password, "examplepass", "Checking provided password");
  279. break;
  280. case 103:
  281. ok(username == "user1name" || username == "user2name", "Checking filled username");
  282. ok(password == "user1pass" || password == "user2pass", "Checking filled password");
  283. break;
  284. case 104:
  285. is(username, "user1name", "Checking filled username");
  286. is(password, "user1pass", "Checking filled password");
  287. break;
  288. case 105:
  289. is(username, "user2name", "Checking filled username");
  290. is(password, "user2pass", "Checking filled password");
  291. break;
  292. case 106:
  293. is(username, "user2name", "Checking filled username");
  294. is(password, "user2pass", "Checking filled password");
  295. passfield.setAttribute("value", "NEWuser2pass");
  296. break;
  297. case 107:
  298. is(username, "user2name", "Checking filled username");
  299. is(password, "NEWuser2pass", "Checking filled password");
  300. passfield.setAttribute("value", "user2pass");
  301. break;
  302. case 120:
  303. case 121:
  304. is(username, "", "Checking filled username");
  305. is(password, "", "Checking filled password");
  306. userfield.setAttribute("value", "fill2user");
  307. passfield.setAttribute("value", "fill2pass");
  308. break;
  309. case 500:
  310. is(username, "inuser", "Checking unfilled username");
  311. is(password, "inpass", "Checking unfilled password");
  312. userfield.setAttribute("value", "outuser");
  313. passfield.setAttribute("value", "outpass");
  314. break;
  315. case 501:
  316. clickOK = false;
  317. break;
  318. case 502:
  319. is(username, "", "Checking filled username");
  320. is(password, "examplepass", "Checking filled password");
  321. break;
  322. case 503:
  323. // either of the two logins might have been filled in
  324. ok(username == "user1name" || username == "user2name", "Checking filled username");
  325. ok(password == "user1pass" || password == "user2pass", "Checking filled password");
  326. break;
  327. case 504:
  328. // either of the two logins might have been filled in
  329. ok(username == "user1name" || username == "user2name", "Checking filled username");
  330. ok(password == "user1pass" || password == "user2pass", "Checking filled password");
  331. // enter one of the known logins, test 504+505 exercise the two possible states.
  332. userfield.setAttribute("value", "user1name");
  333. passfield.setAttribute("value", "user1pass");
  334. break;
  335. case 505:
  336. // either of the two logins might have been filled in
  337. ok(username == "user1name" || username == "user2name", "Checking filled username");
  338. ok(password == "user1pass" || password == "user2pass", "Checking filled password");
  339. // enter one of the known logins, test 504+505 exercise the two possible states.
  340. userfield.setAttribute("value", "user2name");
  341. passfield.setAttribute("value", "user2pass");
  342. break;
  343. case 506:
  344. // either of the two logins might have been filled in
  345. ok(username == "user1name" || username == "user2name", "Checking filled username");
  346. ok(password == "user1pass" || password == "user2pass", "Checking filled password");
  347. // force to user2, and change the password
  348. userfield.setAttribute("value", "user2name");
  349. passfield.setAttribute("value", "NEWuser2pass");
  350. break;
  351. case 507:
  352. // either of the two logins might have been filled in
  353. ok(username == "user1name" || username == "user2name", "Checking filled username");
  354. ok(password == "user1pass" || password == "user2pass", "Checking filled password");
  355. // force to user2, and change the password back
  356. userfield.setAttribute("value", "user2name");
  357. passfield.setAttribute("value", "user2pass");
  358. break;
  359. case 508:
  360. is(username, "proxuser", "Checking filled username");
  361. is(password, "proxpass", "Checking filled password");
  362. break;
  363. // No case 509, it's unprompted.
  364. case 510:
  365. is(username, "proxuser", "Checking filled username");
  366. is(password, "proxpass", "Checking filled password");
  367. break;
  368. case 511:
  369. is(username, "proxuser", "Checking filled username");
  370. is(password, "proxpass", "Checking filled password");
  371. break;
  372. case 1000:
  373. is(username, "mochiuser1", "Checking filled username");
  374. is(password, "mochipass1", "Checking filled password");
  375. break;
  376. case 1001:
  377. is(username, "mochiuser2", "Checking filled username");
  378. is(password, "mochipass2", "Checking filled password");
  379. break;
  380. // (1002 doesn't trigger a dialog)
  381. case 1003:
  382. is(username, "mochiuser1", "Checking filled username");
  383. is(password, "mochipass1", "Checking filled password");
  384. passfield.setAttribute("value", "mochipass1-new");
  385. break;
  386. case 1004:
  387. is(username, "mochiuser3", "Checking filled username");
  388. is(password, "mochipass3-old", "Checking filled password");
  389. passfield.setAttribute("value", "mochipass3-new");
  390. break;
  391. case 1005:
  392. is(username, "", "Checking filled username");
  393. is(password, "", "Checking filled password");
  394. userfield.setAttribute("value", "mochiuser3");
  395. passfield.setAttribute("value", "mochipass3-old");
  396. break;
  397. default:
  398. ok(false, "Uhh, unhandled switch for testNum #" + testNum);
  399. break;
  400. }
  401. if (clickOK)
  402. dialog.acceptDialog();
  403. else
  404. dialog.cancelDialog();
  405. ok(true, "handleDialog done");
  406. didDialog = true;
  407. }
  408. /*
  409. * handleLoad
  410. *
  411. * Called when a load event is fired at the subtest's iframe.
  412. */
  413. function handleLoad() {
  414. ok(true, "."); // make it easier to see next line in logs.
  415. ok(true, "handleLoad running for test " + testNum);
  416. ok(true, "Time is " + (new Date()).toUTCString());
  417. dumpNotifications();
  418. if (testNum != 1002)
  419. ok(didDialog, "handleDialog was invoked");
  420. // The server echos back the user/pass it received.
  421. var username = iframe.contentDocument.getElementById("user").textContent;
  422. var password = iframe.contentDocument.getElementById("pass").textContent;
  423. var authok = iframe.contentDocument.getElementById("ok").textContent;
  424. switch(testNum) {
  425. case 1000:
  426. testNum++;
  427. is(authok, "PASS", "Checking for successful authentication");
  428. is(username, "mochiuser1", "Checking for echoed username");
  429. is(password, "mochipass1", "Checking for echoed password");
  430. startCallbackTimer();
  431. // We've already authenticated to this host:port. For this next
  432. // request, the existing auth should be sent, we'll get a 401 reply,
  433. // and we should prompt for new auth.
  434. iframe.src = "authenticate.sjs?user=mochiuser2&pass=mochipass2&realm=mochitest2";
  435. break;
  436. case 1001:
  437. testNum++;
  438. is(authok, "PASS", "Checking for successful authentication");
  439. is(username, "mochiuser2", "Checking for echoed username");
  440. is(password, "mochipass2", "Checking for echoed password");
  441. // Now make a load that requests the realm from test 1000. It was
  442. // already provided there, so auth will *not* be prompted for -- the
  443. // networking layer already knows it!
  444. iframe.src = "authenticate.sjs?user=mochiuser1&pass=mochipass1";
  445. break;
  446. case 1002:
  447. testNum++;
  448. is(authok, "PASS", "Checking for successful authentication");
  449. is(username, "mochiuser1", "Checking for echoed username");
  450. is(password, "mochipass1", "Checking for echoed password");
  451. // Same realm we've already authenticated to, but with a different
  452. // expected password (to trigger an auth prompt, and change-password
  453. // popup notification).
  454. startCallbackTimer();
  455. iframe.src = "authenticate.sjs?user=mochiuser1&pass=mochipass1-new";
  456. break;
  457. case 1003:
  458. testNum++;
  459. is(authok, "PASS", "Checking for successful authentication");
  460. is(username, "mochiuser1", "Checking for echoed username");
  461. is(password, "mochipass1-new", "Checking for echoed password");
  462. // Housekeeping: change it back
  463. function resetIt() {
  464. tmpLogin.init("http://mochi.test:8888", null, "mochitest",
  465. "mochiuser1", "mochipass1-new", "", "");
  466. pwmgr.modifyLogin(tmpLogin, login3A);
  467. }
  468. addNotificationCallback(resetIt);
  469. // Check for the popup notification, and change the password.
  470. popupNotifications = getPopupNotifications(window.top);
  471. popup = getPopup(popupNotifications, "password-change");
  472. ok(popup, "got popup notification");
  473. clickPopupButton(popup, kChangeButton);
  474. popup.remove();
  475. // Same as last test, but for a realm we haven't already authenticated
  476. // to (but have an existing saved login for, so that we'll trigger
  477. // a change-password popup notification.
  478. startCallbackTimer();
  479. iframe.src = "authenticate.sjs?user=mochiuser3&pass=mochipass3-new&realm=mochitest3";
  480. break;
  481. case 1004:
  482. testNum++;
  483. is(authok, "PASS", "Checking for successful authentication");
  484. is(username, "mochiuser3", "Checking for echoed username");
  485. is(password, "mochipass3-new", "Checking for echoed password");
  486. // Housekeeping: change it back to the original login4. Actually,
  487. // just delete it and we'll re-add it as the next test.
  488. function clearIt() {
  489. ok(true, "1004's clearIt() called.");
  490. try {
  491. tmpLogin.init("http://mochi.test:8888", null, "mochitest3",
  492. "mochiuser3", "mochipass3-new", "", "");
  493. pwmgr.removeLogin(tmpLogin);
  494. // Trigger a new prompt, so we can test adding a new login.
  495. startCallbackTimer();
  496. iframe.src = "authenticate.sjs?user=mochiuser3&pass=mochipass3-old&realm=mochitest3";
  497. } catch (e) { ok(false, "clearIt GOT EXCEPTION: " + e); }
  498. }
  499. addNotificationCallback(clearIt);
  500. // Check for the popup notification, and change the password.
  501. popup = getPopup(popupNotifications, "password-change");
  502. ok(popup, "got popup notification");
  503. clickPopupButton(popup, kChangeButton);
  504. popup.remove();
  505. // Clear cached auth from this subtest, and avoid leaking due to bug 459620.
  506. var authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].
  507. getService(Ci.nsIHttpAuthManager);
  508. authMgr.clearAll();
  509. ok(true, "authMgr cleared cached auth");
  510. break;
  511. case 1005:
  512. testNum++;
  513. is(authok, "PASS", "Checking for successful authentication");
  514. is(username, "mochiuser3", "Checking for echoed username");
  515. is(password, "mochipass3-old", "Checking for echoed password");
  516. function finishIt() {
  517. finishTest();
  518. }
  519. addNotificationCallback(finishIt);
  520. // Check for the popup notification, and change the password.
  521. popup = getPopup(popupNotifications, "password-save");
  522. ok(popup, "got popup notification");
  523. clickPopupButton(popup, kRememberButton);
  524. popup.remove();
  525. break;
  526. default:
  527. ok(false, "Uhh, unhandled switch for testNum #" + testNum);
  528. break;
  529. }
  530. }
  531. startup();
  532. function doTests() {
  533. var authinfo = {
  534. username : "",
  535. password : "",
  536. domain : "",
  537. flags : Ci.nsIAuthInformation.AUTH_HOST,
  538. authenticationScheme : "basic",
  539. realm : ""
  540. };
  541. var proxyAuthinfo = {
  542. username : "",
  543. password : "",
  544. domain : "",
  545. flags : Ci.nsIAuthInformation.AUTH_PROXY,
  546. authenticationScheme : "basic",
  547. realm : ""
  548. };
  549. var prefs = Cc["@mozilla.org/preferences-service;1"].
  550. getService(Ci.nsIPrefBranch);
  551. const Cc_promptFac= Cc["@mozilla.org/passwordmanager/authpromptfactory;1"];
  552. ok(Cc_promptFac != null, "Access Cc[@mozilla.org/passwordmanager/authpromptfactory;1]");
  553. const Ci_promptFac = Ci.nsIPromptFactory;
  554. ok(Ci_promptFac != null, "Access Ci.nsIPromptFactory");
  555. const promptFac = Cc_promptFac.getService(Ci_promptFac);
  556. ok(promptFac != null, "promptFac getService()");
  557. var prompter1 = promptFac.getPrompt(window, Ci.nsIAuthPrompt);
  558. var prompter2 = promptFac.getPrompt(window, Ci.nsIAuthPrompt2);
  559. function dialogTitle() { return "nsILoginManagerPrompter test #" + testNum; }
  560. var dialogText = "This dialog should be modified and dismissed by the test.";
  561. var uname = { value : null };
  562. var pword = { value : null };
  563. var result = { value : null };
  564. var isOk;
  565. // popupNotifications (not *popup*) is a constant, per-tab container. So, we
  566. // only need to fetch it once.
  567. var popupNotifications = getPopupNotifications(window.top);
  568. ok(popupNotifications, "Got popupNotifications");
  569. // ===== test 1 =====
  570. testNum = 1;
  571. startCallbackTimer();
  572. isOk = prompter1.prompt(dialogTitle(), dialogText, "http://example.com",
  573. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result);
  574. ok(isOk, "Checking dialog return value (accept)");
  575. ok(didDialog, "handleDialog was invoked");
  576. is(result.value, "xyz", "Checking prompt() returned value");
  577. // ===== test 2 =====
  578. testNum++;
  579. startCallbackTimer();
  580. isOk = prompter1.prompt(dialogTitle(), dialogText, "http://example.com",
  581. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, "abc", result);
  582. ok(didDialog, "handleDialog was invoked");
  583. ok(!isOk, "Checking dialog return value (cancel)");
  584. // ===== test 10 =====
  585. // Default password provided, existing logins are ignored.
  586. testNum = 10;
  587. pword.value = "inputpw";
  588. startCallbackTimer();
  589. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example.com",
  590. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  591. ok(isOk, "Checking dialog return value (accept)");
  592. ok(didDialog, "handleDialog was invoked");
  593. is(pword.value, "secret", "Checking returned password");
  594. // ===== test 11 =====
  595. // Default password provided, existing logins are ignored.
  596. testNum++;
  597. pword.value = "inputpw";
  598. startCallbackTimer();
  599. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example.com",
  600. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  601. ok(!isOk, "Checking dialog return value (cancel)");
  602. ok(didDialog, "handleDialog was invoked");
  603. // ===== test 12 =====
  604. // No default password provided, realm does not match existing login.
  605. testNum++;
  606. pword.value = null;
  607. startCallbackTimer();
  608. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://nonexample.com",
  609. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  610. ok(isOk, "Checking dialog return value (accept)");
  611. ok(didDialog, "handleDialog was invoked");
  612. is(pword.value, "secret", "Checking returned password");
  613. // ===== test 13 =====
  614. // No default password provided, matching login is returned w/o prompting.
  615. testNum++;
  616. pword.value = null;
  617. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example.com",
  618. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  619. ok(isOk, "Checking dialog return value (accept)");
  620. is(pword.value, "examplepass", "Checking returned password");
  621. // ===== test 14 =====
  622. // No default password provided, none of the logins from this host are
  623. // password-only so the user is prompted.
  624. testNum++;
  625. pword.value = null;
  626. startCallbackTimer();
  627. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://example2.com",
  628. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  629. ok(isOk, "Checking dialog return value (accept)");
  630. ok(didDialog, "handleDialog was invoked");
  631. is(pword.value, "secret", "Checking returned password");
  632. // ===== test 15 =====
  633. // No default password provided, matching login is returned w/o prompting.
  634. testNum++;
  635. pword.value = null;
  636. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://user1name@example2.com",
  637. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  638. ok(isOk, "Checking dialog return value (accept)");
  639. is(pword.value, "user1pass", "Checking returned password");
  640. // ===== test 16 =====
  641. // No default password provided, matching login is returned w/o prompting.
  642. testNum++;
  643. pword.value = null;
  644. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://user2name@example2.com",
  645. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  646. ok(isOk, "Checking dialog return value (accept)");
  647. is(pword.value, "user2pass", "Checking returned password");
  648. // ===== test 17 =====
  649. // No default password provided, matching login is returned w/o prompting.
  650. testNum++;
  651. pword.value = null;
  652. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://user3%2Ename%40host@example2.com",
  653. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  654. ok(isOk, "Checking dialog return value (accept)");
  655. is(pword.value, "user3pass", "Checking returned password");
  656. // ===== test 18 =====
  657. // No default password provided, matching login is returned w/o prompting.
  658. testNum++;
  659. pword.value = null;
  660. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://100@beef@example2.com",
  661. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  662. ok(isOk, "Checking dialog return value (accept)");
  663. is(pword.value, "user3pass", "Checking returned password");
  664. // ===== test 19 =====
  665. // No default password provided, matching login is returned w/o prompting.
  666. testNum++;
  667. pword.value = null;
  668. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "http://100%25beef@example2.com",
  669. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  670. ok(isOk, "Checking dialog return value (accept)");
  671. is(pword.value, "user3pass", "Checking returned password");
  672. // XXX test saving a password with Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY
  673. // ===== test 30 =====
  674. // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
  675. testNum = 30;
  676. pword.value = null;
  677. startCallbackTimer();
  678. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
  679. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
  680. ok(isOk, "Checking dialog return value (accept)");
  681. ok(didDialog, "handleDialog was invoked");
  682. is(pword.value, "fill2pass", "Checking returned password");
  683. // ===== test 31 =====
  684. // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
  685. testNum++;
  686. pword.value = null;
  687. startCallbackTimer();
  688. isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
  689. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, pword);
  690. ok(isOk, "Checking dialog return value (accept)");
  691. ok(didDialog, "handleDialog was invoked");
  692. is(pword.value, "fill2pass", "Checking returned password");
  693. // ===== test 100 =====
  694. testNum = 100;
  695. uname.value = "inuser";
  696. pword.value = "inpass";
  697. startCallbackTimer();
  698. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://nonexample.com",
  699. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
  700. ok(isOk, "Checking dialog return value (accept)");
  701. ok(didDialog, "handleDialog was invoked");
  702. is(uname.value, "outuser", "Checking returned username");
  703. is(pword.value, "outpass", "Checking returned password");
  704. // ===== test 101 =====
  705. testNum++;
  706. uname.value = "inuser";
  707. pword.value = "inpass";
  708. startCallbackTimer();
  709. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://nonexample.com",
  710. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
  711. ok(!isOk, "Checking dialog return value (cancel)");
  712. ok(didDialog, "handleDialog was invoked");
  713. // ===== test 102 =====
  714. // test filling in existing password-only login
  715. testNum++;
  716. uname.value = null;
  717. pword.value = null;
  718. startCallbackTimer();
  719. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example.com",
  720. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  721. ok(isOk, "Checking dialog return value (accept)");
  722. ok(didDialog, "handleDialog was invoked");
  723. is(uname.value, "", "Checking returned username");
  724. is(pword.value, "examplepass", "Checking returned password");
  725. // ===== test 103 =====
  726. // test filling in existing login (undetermined from multiple selection)
  727. testNum++;
  728. uname.value = null;
  729. pword.value = null;
  730. startCallbackTimer();
  731. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
  732. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  733. ok(isOk, "Checking dialog return value (accept)");
  734. ok(didDialog, "handleDialog was invoked");
  735. ok(uname.value == "user1name" || uname.value == "user2name", "Checking returned username");
  736. ok(pword.value == "user1pass" || uname.value == "user2pass", "Checking returned password");
  737. // ===== test 104 =====
  738. // test filling in existing login (user1 from multiple selection)
  739. testNum++;
  740. uname.value = "user1name";
  741. pword.value = null;
  742. startCallbackTimer();
  743. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
  744. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  745. ok(isOk, "Checking dialog return value (accept)");
  746. ok(didDialog, "handleDialog was invoked");
  747. is(uname.value, "user1name", "Checking returned username");
  748. is(pword.value, "user1pass", "Checking returned password");
  749. // ===== test 105 =====
  750. // test filling in existing login (user2 from multiple selection)
  751. testNum++;
  752. uname.value = "user2name";
  753. pword.value = null;
  754. startCallbackTimer();
  755. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
  756. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  757. ok(isOk, "Checking dialog return value (accept)");
  758. ok(didDialog, "handleDialog was invoked");
  759. is(uname.value, "user2name", "Checking returned username");
  760. is(pword.value, "user2pass", "Checking returned password");
  761. // ===== test 106 =====
  762. // test changing password
  763. testNum++;
  764. uname.value = "user2name";
  765. pword.value = null;
  766. startCallbackTimer();
  767. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
  768. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  769. ok(isOk, "Checking dialog return value (accept)");
  770. ok(didDialog, "handleDialog was invoked");
  771. is(uname.value, "user2name", "Checking returned username");
  772. is(pword.value, "NEWuser2pass", "Checking returned password");
  773. // ===== test 107 =====
  774. // test changing password (back to original value)
  775. testNum++;
  776. uname.value = "user2name";
  777. pword.value = null;
  778. startCallbackTimer();
  779. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "http://example2.com",
  780. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  781. ok(isOk, "Checking dialog return value (accept)");
  782. ok(didDialog, "handleDialog was invoked");
  783. is(uname.value, "user2name", "Checking returned username");
  784. is(pword.value, "user2pass", "Checking returned password");
  785. // ===== test 120 =====
  786. // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
  787. testNum = 120;
  788. uname.value = null;
  789. pword.value = null;
  790. startCallbackTimer();
  791. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
  792. Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
  793. ok(isOk, "Checking dialog return value (accept)");
  794. ok(didDialog, "handleDialog was invoked");
  795. is(uname.value, "fill2user", "Checking returned username");
  796. is(pword.value, "fill2pass", "Checking returned password");
  797. // ===== test 121 =====
  798. // We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
  799. testNum++;
  800. uname.value = null;
  801. pword.value = null;
  802. startCallbackTimer();
  803. isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
  804. Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
  805. ok(isOk, "Checking dialog return value (accept)");
  806. ok(didDialog, "handleDialog was invoked");
  807. is(uname.value, "fill2user", "Checking returned username");
  808. is(pword.value, "fill2pass", "Checking returned password");
  809. var channel1 = ioService.newChannel("http://example.com", null, null);
  810. var channel2 = ioService.newChannel("http://example2.com", null, null);
  811. var level = Ci.nsIAuthPrompt2.LEVEL_NONE;
  812. // ===== test 500 =====
  813. testNum = 500;
  814. authinfo.username = "inuser";
  815. authinfo.password = "inpass";
  816. authinfo.realm = "some realm";
  817. startCallbackTimer();
  818. isOk = prompter2.promptAuth(channel1, level, authinfo);
  819. ok(isOk, "Checking dialog return value (accept)");
  820. ok(didDialog, "handleDialog was invoked");
  821. is(authinfo.username, "outuser", "Checking returned username");
  822. is(authinfo.password, "outpass", "Checking returned password");
  823. // ===== test 501 =====
  824. testNum++;
  825. startCallbackTimer();
  826. isOk = prompter2.promptAuth(channel1, level, authinfo);
  827. ok(!isOk, "Checking dialog return value (cancel)");
  828. ok(didDialog, "handleDialog was invoked");
  829. // ===== test 502 =====
  830. // test filling in password-only login
  831. testNum++;
  832. authinfo.username = "";
  833. authinfo.password = "";
  834. authinfo.realm = "http://example.com";
  835. startCallbackTimer();
  836. isOk = prompter2.promptAuth(channel1, level, authinfo);
  837. ok(isOk, "Checking dialog return value (accept)");
  838. ok(didDialog, "handleDialog was invoked");
  839. is(authinfo.username, "", "Checking returned username");
  840. is(authinfo.password, "examplepass", "Checking returned password");
  841. // ===== test 503 =====
  842. // test filling in existing login (undetermined from multiple selection)
  843. testNum++;
  844. authinfo.username = "";
  845. authinfo.password = "";
  846. authinfo.realm = "http://example2.com";
  847. startCallbackTimer();
  848. isOk = prompter2.promptAuth(channel2, level, authinfo);
  849. ok(isOk, "Checking dialog return value (accept)");
  850. ok(didDialog, "handleDialog was invoked");
  851. ok(authinfo.username == "user1name" || authinfo.username == "user2name", "Checking returned username");
  852. ok(authinfo.password == "user1pass" || authinfo.password == "user2pass", "Checking returned password");
  853. // ===== test 504 =====
  854. // test filling in existing login (undetermined --> user1)
  855. testNum++;
  856. authinfo.username = "";
  857. authinfo.password = "";
  858. authinfo.realm = "http://example2.com";
  859. startCallbackTimer();
  860. isOk = prompter2.promptAuth(channel2, level, authinfo);
  861. ok(isOk, "Checking dialog return value (accept)");
  862. ok(didDialog, "handleDialog was invoked");
  863. is(authinfo.username, "user1name", "Checking returned username");
  864. is(authinfo.password, "user1pass", "Checking returned password");
  865. // ===== test 505 =====
  866. // test filling in existing login (undetermined --> user2)
  867. testNum++;
  868. authinfo.username = "";
  869. authinfo.password = "";
  870. authinfo.realm = "http://example2.com";
  871. dumpNotifications();
  872. startCallbackTimer();
  873. isOk = prompter2.promptAuth(channel2, level, authinfo);
  874. ok(isOk, "Checking dialog return value (accept)");
  875. ok(didDialog, "handleDialog was invoked");
  876. is(authinfo.username, "user2name", "Checking returned username");
  877. is(authinfo.password, "user2pass", "Checking returned password");
  878. // ===== test 506 =====
  879. // test changing a password (undetermined --> user2 w/ newpass)
  880. testNum++;
  881. authinfo.username = "";
  882. authinfo.password = "";
  883. authinfo.realm = "http://example2.com";
  884. dumpNotifications();
  885. startCallbackTimer();
  886. isOk = prompter2.promptAuth(channel2, level, authinfo);
  887. ok(isOk, "Checking dialog return value (accept)");
  888. ok(didDialog, "handleDialog was invoked");
  889. is(authinfo.username, "user2name", "Checking returned username");
  890. is(authinfo.password, "NEWuser2pass", "Checking returned password");
  891. // ===== test 507 =====
  892. // test changing a password (undetermined --> user2 w/ origpass)
  893. testNum++;
  894. authinfo.username = "";
  895. authinfo.password = "";
  896. authinfo.realm = "http://example2.com";
  897. dumpNotifications();
  898. startCallbackTimer();
  899. isOk = prompter2.promptAuth(channel2, level, authinfo);
  900. ok(isOk, "Checking dialog return value (accept)");
  901. ok(didDialog, "handleDialog was invoked");
  902. is(authinfo.username, "user2name", "Checking returned username");
  903. is(authinfo.password, "user2pass", "Checking returned password");
  904. // ===== test 508 =====
  905. // test proxy login (default = no autologin), make sure it prompts.
  906. testNum++;
  907. proxyAuthinfo.username = "";
  908. proxyAuthinfo.password = "";
  909. proxyAuthinfo.realm = "Proxy Realm";
  910. proxyAuthinfo.flags = Ci.nsIAuthInformation.AUTH_PROXY;
  911. dumpNotifications();
  912. var time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
  913. startCallbackTimer();
  914. isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
  915. var time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
  916. ok(isOk, "Checking dialog return value (accept)");
  917. ok(didDialog, "handleDialog was invoked");
  918. isnot(time1, time2, "Checking that timeLastUsed was updated");
  919. is(proxyAuthinfo.username, "proxuser", "Checking returned username");
  920. is(proxyAuthinfo.password, "proxpass", "Checking returned password");
  921. // ===== test 509 =====
  922. // test proxy login (with autologin)
  923. testNum++;
  924. // Enable the autologin pref.
  925. prefs.setBoolPref("signon.autologin.proxy", true);
  926. proxyAuthinfo.username = "";
  927. proxyAuthinfo.password = "";
  928. proxyAuthinfo.realm = "Proxy Realm";
  929. proxyAuthinfo.flags = Ci.nsIAuthInformation.AUTH_PROXY;
  930. time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
  931. isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
  932. time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
  933. ok(isOk, "Checking dialog return value (accept)");
  934. isnot(time1, time2, "Checking that timeLastUsed was updated");
  935. is(proxyAuthinfo.username, "proxuser", "Checking returned username");
  936. is(proxyAuthinfo.password, "proxpass", "Checking returned password");
  937. // ===== test 510 =====
  938. // test proxy login (with autologin), ensure it prompts after a failed auth.
  939. testNum++;
  940. proxyAuthinfo.username = "";
  941. proxyAuthinfo.password = "";
  942. proxyAuthinfo.realm = "Proxy Realm";
  943. proxyAuthinfo.flags = (Ci.nsIAuthInformation.AUTH_PROXY | Ci.nsIAuthInformation.PREVIOUS_FAILED);
  944. time1 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
  945. startCallbackTimer();
  946. isOk = prompter2.promptAuth(proxyChannel, level, proxyAuthinfo);
  947. time2 = pwmgr.findLogins({}, mozproxy, null, "Proxy Realm")[0].QueryInterface(Ci.nsILoginMetaInfo).timeLastUsed;
  948. ok(isOk, "Checking dialog return value (accept)");
  949. ok(didDialog, "handleDialog was invoked");
  950. isnot(time1, time2, "Checking that timeLastUsed was updated");
  951. is(proxyAuthinfo.username, "proxuser", "Checking returned username");
  952. is(proxyAuthinfo.password, "proxpass", "Checking returned password");
  953. // ===== test 511 =====
  954. // test proxy login (with autologin), ensure it prompts in Private Browsing mode.
  955. testNum++;
  956. proxyAuthinfo.username = "";
  957. proxyAuthinfo.password = "";
  958. proxyAuthinfo.realm = "Proxy Realm";
  959. proxyAuthinfo.flags = Ci.nsIAuthInformation.AUTH_PROXY;
  960. prefs.clearUserPref("signon.autologin.proxy");
  961. // XXX check for and kill popup notification??
  962. // XXX check for checkbox / checkstate on old prompts?
  963. // XXX check NTLM domain stuff
  964. var iframe = document.getElementById("iframe");
  965. iframe.onload = handleLoad;
  966. // clear plain HTTP auth sessions before the test, to allow
  967. // running them more than once.
  968. var authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].
  969. getService(Ci.nsIHttpAuthManager);
  970. authMgr.clearAll();
  971. // ===== test 1000 =====
  972. testNum = 1000;
  973. startCallbackTimer();
  974. iframe.src = "authenticate.sjs?user=mochiuser1&pass=mochipass1";
  975. // ...remaining tests are driven by handleLoad()...
  976. }
  977. </script>
  978. </pre>
  979. </body>
  980. </html>