mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-16 04:07:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
13
toolkit/components/passwordmgr/test/.eslintrc.js
Normal file
13
toolkit/components/passwordmgr/test/.eslintrc.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = { // eslint-disable-line no-undef
|
||||
"extends": [
|
||||
"../../../../testing/mochitest/mochitest.eslintrc.js",
|
||||
"../../../../testing/mochitest/chrome.eslintrc.js"
|
||||
],
|
||||
"rules": {
|
||||
"brace-style": "off",
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
},
|
||||
};
|
||||
295
toolkit/components/passwordmgr/test/LoginTestUtils.jsm
Normal file
295
toolkit/components/passwordmgr/test/LoginTestUtils.jsm
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* Shared functions generally available for testing login components.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"LoginTestUtils",
|
||||
];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
Cu.import("resource://testing-common/TestUtils.jsm");
|
||||
|
||||
const LoginInfo =
|
||||
Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
"nsILoginInfo", "init");
|
||||
|
||||
// For now, we need consumers to provide a reference to Assert.jsm.
|
||||
var Assert = null;
|
||||
|
||||
this.LoginTestUtils = {
|
||||
set Assert(assert) {
|
||||
Assert = assert; // eslint-disable-line no-native-reassign
|
||||
},
|
||||
|
||||
/**
|
||||
* Forces the storage module to save all data, and the Login Manager service
|
||||
* to replace the storage module with a newly initialized instance.
|
||||
*/
|
||||
* reloadData() {
|
||||
Services.obs.notifyObservers(null, "passwordmgr-storage-replace", null);
|
||||
yield TestUtils.topicObserved("passwordmgr-storage-replace-complete");
|
||||
},
|
||||
|
||||
/**
|
||||
* Erases all the data stored by the Login Manager service.
|
||||
*/
|
||||
clearData() {
|
||||
Services.logins.removeAllLogins();
|
||||
for (let hostname of Services.logins.getAllDisabledHosts()) {
|
||||
Services.logins.setLoginSavingEnabled(hostname, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks that the currently stored list of nsILoginInfo matches the provided
|
||||
* array. The comparison uses the "equals" method of nsILoginInfo, that does
|
||||
* not include nsILoginMetaInfo properties in the test.
|
||||
*/
|
||||
checkLogins(expectedLogins) {
|
||||
this.assertLoginListsEqual(Services.logins.getAllLogins(), expectedLogins);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks that the two provided arrays of nsILoginInfo have the same length,
|
||||
* and every login in "expected" is also found in "actual". The comparison
|
||||
* uses the "equals" method of nsILoginInfo, that does not include
|
||||
* nsILoginMetaInfo properties in the test.
|
||||
*/
|
||||
assertLoginListsEqual(actual, expected) {
|
||||
Assert.equal(expected.length, actual.length);
|
||||
Assert.ok(expected.every(e => actual.some(a => a.equals(e))));
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks that the two provided arrays of strings contain the same values,
|
||||
* maybe in a different order, case-sensitively.
|
||||
*/
|
||||
assertDisabledHostsEqual(actual, expected) {
|
||||
Assert.deepEqual(actual.sort(), expected.sort());
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the given time, expressed as the number of milliseconds
|
||||
* since January 1, 1970, 00:00:00 UTC, falls within 30 seconds of now.
|
||||
*/
|
||||
assertTimeIsAboutNow(timeMs) {
|
||||
Assert.ok(Math.abs(timeMs - Date.now()) < 30000);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This object contains functions that return new instances of nsILoginInfo for
|
||||
* every call. The returned instances can be compared using their "equals" or
|
||||
* "matches" methods, or modified for the needs of the specific test being run.
|
||||
*
|
||||
* Any modification to the test data requires updating the tests accordingly, in
|
||||
* particular the search tests.
|
||||
*/
|
||||
this.LoginTestUtils.testData = {
|
||||
/**
|
||||
* Returns a new nsILoginInfo for use with form submits.
|
||||
*
|
||||
* @param modifications
|
||||
* Each property of this object replaces the property of the same name
|
||||
* in the returned nsILoginInfo or nsILoginMetaInfo.
|
||||
*/
|
||||
formLogin(modifications) {
|
||||
let loginInfo = new LoginInfo("http://www3.example.com",
|
||||
"http://www.example.com", null,
|
||||
"the username", "the password",
|
||||
"form_field_username", "form_field_password");
|
||||
loginInfo.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
if (modifications) {
|
||||
for (let [name, value] of Object.entries(modifications)) {
|
||||
loginInfo[name] = value;
|
||||
}
|
||||
}
|
||||
return loginInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a new nsILoginInfo for use with HTTP authentication.
|
||||
*
|
||||
* @param modifications
|
||||
* Each property of this object replaces the property of the same name
|
||||
* in the returned nsILoginInfo or nsILoginMetaInfo.
|
||||
*/
|
||||
authLogin(modifications) {
|
||||
let loginInfo = new LoginInfo("http://www.example.org", null,
|
||||
"The HTTP Realm", "the username",
|
||||
"the password", "", "");
|
||||
loginInfo.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
if (modifications) {
|
||||
for (let [name, value] of Object.entries(modifications)) {
|
||||
loginInfo[name] = value;
|
||||
}
|
||||
}
|
||||
return loginInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of typical nsILoginInfo that could be stored in the
|
||||
* database.
|
||||
*/
|
||||
loginList() {
|
||||
return [
|
||||
// --- Examples of form logins (subdomains of example.com) ---
|
||||
|
||||
// Simple form login with named fields for username and password.
|
||||
new LoginInfo("http://www.example.com", "http://www.example.com", null,
|
||||
"the username", "the password for www.example.com",
|
||||
"form_field_username", "form_field_password"),
|
||||
|
||||
// Different schemes are treated as completely different sites.
|
||||
new LoginInfo("https://www.example.com", "https://www.example.com", null,
|
||||
"the username", "the password for https",
|
||||
"form_field_username", "form_field_password"),
|
||||
|
||||
// Subdomains are treated as completely different sites.
|
||||
new LoginInfo("https://example.com", "https://example.com", null,
|
||||
"the username", "the password for example.com",
|
||||
"form_field_username", "form_field_password"),
|
||||
|
||||
// Forms found on the same host, but with different hostnames in the
|
||||
// "action" attribute, are handled independently.
|
||||
new LoginInfo("http://www3.example.com", "http://www.example.com", null,
|
||||
"the username", "the password",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://www3.example.com", "https://www.example.com", null,
|
||||
"the username", "the password",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://www3.example.com", "http://example.com", null,
|
||||
"the username", "the password",
|
||||
"form_field_username", "form_field_password"),
|
||||
|
||||
// It is not possible to store multiple passwords for the same username,
|
||||
// however multiple passwords can be stored when the usernames differ.
|
||||
// An empty username is a valid case and different from the others.
|
||||
new LoginInfo("http://www4.example.com", "http://www4.example.com", null,
|
||||
"username one", "password one",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://www4.example.com", "http://www4.example.com", null,
|
||||
"username two", "password two",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://www4.example.com", "http://www4.example.com", null,
|
||||
"", "password three",
|
||||
"form_field_username", "form_field_password"),
|
||||
|
||||
// Username and passwords fields in forms may have no "name" attribute.
|
||||
new LoginInfo("http://www5.example.com", "http://www5.example.com", null,
|
||||
"multi username", "multi password", "", ""),
|
||||
|
||||
// Forms with PIN-type authentication will typically have no username.
|
||||
new LoginInfo("http://www6.example.com", "http://www6.example.com", null,
|
||||
"", "12345", "", "form_field_password"),
|
||||
|
||||
// --- Examples of authentication logins (subdomains of example.org) ---
|
||||
|
||||
// Simple HTTP authentication login.
|
||||
new LoginInfo("http://www.example.org", null, "The HTTP Realm",
|
||||
"the username", "the password", "", ""),
|
||||
|
||||
// Simple FTP authentication login.
|
||||
new LoginInfo("ftp://ftp.example.org", null, "ftp://ftp.example.org",
|
||||
"the username", "the password", "", ""),
|
||||
|
||||
// Multiple HTTP authentication logins can be stored for different realms.
|
||||
new LoginInfo("http://www2.example.org", null, "The HTTP Realm",
|
||||
"the username", "the password", "", ""),
|
||||
new LoginInfo("http://www2.example.org", null, "The HTTP Realm Other",
|
||||
"the username other", "the password other", "", ""),
|
||||
|
||||
// --- Both form and authentication logins (example.net) ---
|
||||
|
||||
new LoginInfo("http://example.net", "http://example.net", null,
|
||||
"the username", "the password",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://example.net", "http://www.example.net", null,
|
||||
"the username", "the password",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://example.net", "http://www.example.net", null,
|
||||
"username two", "the password",
|
||||
"form_field_username", "form_field_password"),
|
||||
new LoginInfo("http://example.net", null, "The HTTP Realm",
|
||||
"the username", "the password", "", ""),
|
||||
new LoginInfo("http://example.net", null, "The HTTP Realm Other",
|
||||
"username two", "the password", "", ""),
|
||||
new LoginInfo("ftp://example.net", null, "ftp://example.net",
|
||||
"the username", "the password", "", ""),
|
||||
|
||||
// --- Examples of logins added by extensions (chrome scheme) ---
|
||||
|
||||
new LoginInfo("chrome://example_extension", null, "Example Login One",
|
||||
"the username", "the password one", "", ""),
|
||||
new LoginInfo("chrome://example_extension", null, "Example Login Two",
|
||||
"the username", "the password two", "", ""),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
this.LoginTestUtils.recipes = {
|
||||
getRecipeParent() {
|
||||
let { LoginManagerParent } = Cu.import("resource://gre/modules/LoginManagerParent.jsm", {});
|
||||
if (!LoginManagerParent.recipeParentPromise) {
|
||||
return null;
|
||||
}
|
||||
return LoginManagerParent.recipeParentPromise.then((recipeParent) => {
|
||||
return recipeParent;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
this.LoginTestUtils.masterPassword = {
|
||||
masterPassword: "omgsecret!",
|
||||
|
||||
_set(enable) {
|
||||
let oldPW, newPW;
|
||||
if (enable) {
|
||||
oldPW = "";
|
||||
newPW = this.masterPassword;
|
||||
} else {
|
||||
oldPW = this.masterPassword;
|
||||
newPW = "";
|
||||
}
|
||||
|
||||
let secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
|
||||
.getService(Ci.nsIPKCS11ModuleDB);
|
||||
let slot = secmodDB.findSlotByName("");
|
||||
if (!slot) {
|
||||
throw new Error("Can't find slot");
|
||||
}
|
||||
|
||||
// Set master password. Note that this does not log you in, so the next
|
||||
// invocation of pwmgr can trigger a MP prompt.
|
||||
let pk11db = Cc["@mozilla.org/security/pk11tokendb;1"]
|
||||
.getService(Ci.nsIPK11TokenDB);
|
||||
let token = pk11db.findTokenByName("");
|
||||
if (slot.status == Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED) {
|
||||
dump("MP initialized to " + newPW + "\n");
|
||||
token.initPassword(newPW);
|
||||
} else {
|
||||
token.checkPassword(oldPW);
|
||||
dump("MP change from " + oldPW + " to " + newPW + "\n");
|
||||
token.changePassword(oldPW, newPW);
|
||||
}
|
||||
},
|
||||
|
||||
enable() {
|
||||
this._set(true);
|
||||
},
|
||||
|
||||
disable() {
|
||||
this._set(false);
|
||||
},
|
||||
};
|
||||
228
toolkit/components/passwordmgr/test/authenticate.sjs
Normal file
228
toolkit/components/passwordmgr/test/authenticate.sjs
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
try {
|
||||
reallyHandleRequest(request, response);
|
||||
} catch (e) {
|
||||
response.setStatusLine("1.0", 200, "AlmostOK");
|
||||
response.write("Error handling request: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function reallyHandleRequest(request, response) {
|
||||
var match;
|
||||
var requestAuth = true, requestProxyAuth = true;
|
||||
|
||||
// Allow the caller to drive how authentication is processed via the query.
|
||||
// Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
|
||||
// The extra ? allows the user/pass/realm checks to succeed if the name is
|
||||
// at the beginning of the query string.
|
||||
var query = "?" + request.queryString;
|
||||
|
||||
var expected_user = "", expected_pass = "", realm = "mochitest";
|
||||
var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy";
|
||||
var huge = false, plugin = false, anonymous = false, formauth = false;
|
||||
var authHeaderCount = 1;
|
||||
// user=xxx
|
||||
match = /[^_]user=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
expected_user = match[1];
|
||||
|
||||
// pass=xxx
|
||||
match = /[^_]pass=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
expected_pass = match[1];
|
||||
|
||||
// realm=xxx
|
||||
match = /[^_]realm=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
realm = match[1];
|
||||
|
||||
// proxy_user=xxx
|
||||
match = /proxy_user=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
proxy_expected_user = match[1];
|
||||
|
||||
// proxy_pass=xxx
|
||||
match = /proxy_pass=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
proxy_expected_pass = match[1];
|
||||
|
||||
// proxy_realm=xxx
|
||||
match = /proxy_realm=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
proxy_realm = match[1];
|
||||
|
||||
// huge=1
|
||||
match = /huge=1/.exec(query);
|
||||
if (match)
|
||||
huge = true;
|
||||
|
||||
// plugin=1
|
||||
match = /plugin=1/.exec(query);
|
||||
if (match)
|
||||
plugin = true;
|
||||
|
||||
// multiple=1
|
||||
match = /multiple=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
authHeaderCount = match[1]+0;
|
||||
|
||||
// anonymous=1
|
||||
match = /anonymous=1/.exec(query);
|
||||
if (match)
|
||||
anonymous = true;
|
||||
|
||||
// formauth=1
|
||||
match = /formauth=1/.exec(query);
|
||||
if (match)
|
||||
formauth = true;
|
||||
|
||||
// Look for an authentication header, if any, in the request.
|
||||
//
|
||||
// EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
//
|
||||
// This test only supports Basic auth. The value sent by the client is
|
||||
// "username:password", obscured with base64 encoding.
|
||||
|
||||
var actual_user = "", actual_pass = "", authHeader, authPresent = false;
|
||||
if (request.hasHeader("Authorization")) {
|
||||
authPresent = true;
|
||||
authHeader = request.getHeader("Authorization");
|
||||
match = /Basic (.+)/.exec(authHeader);
|
||||
if (match.length != 2)
|
||||
throw new Error("Couldn't parse auth header: " + authHeader);
|
||||
|
||||
var userpass = base64ToString(match[1]); // no atob() :-(
|
||||
match = /(.*):(.*)/.exec(userpass);
|
||||
if (match.length != 3)
|
||||
throw new Error("Couldn't decode auth header: " + userpass);
|
||||
actual_user = match[1];
|
||||
actual_pass = match[2];
|
||||
}
|
||||
|
||||
var proxy_actual_user = "", proxy_actual_pass = "";
|
||||
if (request.hasHeader("Proxy-Authorization")) {
|
||||
authHeader = request.getHeader("Proxy-Authorization");
|
||||
match = /Basic (.+)/.exec(authHeader);
|
||||
if (match.length != 2)
|
||||
throw new Error("Couldn't parse auth header: " + authHeader);
|
||||
|
||||
var userpass = base64ToString(match[1]); // no atob() :-(
|
||||
match = /(.*):(.*)/.exec(userpass);
|
||||
if (match.length != 3)
|
||||
throw new Error("Couldn't decode auth header: " + userpass);
|
||||
proxy_actual_user = match[1];
|
||||
proxy_actual_pass = match[2];
|
||||
}
|
||||
|
||||
// Don't request authentication if the credentials we got were what we
|
||||
// expected.
|
||||
if (expected_user == actual_user &&
|
||||
expected_pass == actual_pass) {
|
||||
requestAuth = false;
|
||||
}
|
||||
if (proxy_expected_user == proxy_actual_user &&
|
||||
proxy_expected_pass == proxy_actual_pass) {
|
||||
requestProxyAuth = false;
|
||||
}
|
||||
|
||||
if (anonymous) {
|
||||
if (authPresent) {
|
||||
response.setStatusLine("1.0", 400, "Unexpected authorization header found");
|
||||
} else {
|
||||
response.setStatusLine("1.0", 200, "Authorization header not found");
|
||||
}
|
||||
} else {
|
||||
if (requestProxyAuth) {
|
||||
response.setStatusLine("1.0", 407, "Proxy authentication required");
|
||||
for (i = 0; i < authHeaderCount; ++i)
|
||||
response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
|
||||
} else if (requestAuth) {
|
||||
if (formauth && authPresent)
|
||||
response.setStatusLine("1.0", 403, "Form authentication required");
|
||||
else
|
||||
response.setStatusLine("1.0", 401, "Authentication required");
|
||||
for (i = 0; i < authHeaderCount; ++i)
|
||||
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
|
||||
} else {
|
||||
response.setStatusLine("1.0", 200, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
response.setHeader("Content-Type", "application/xhtml+xml", false);
|
||||
response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
|
||||
response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
|
||||
response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n");
|
||||
response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
|
||||
response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
|
||||
response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n");
|
||||
|
||||
if (huge) {
|
||||
response.write("<div style='display: none'>");
|
||||
for (i = 0; i < 100000; i++) {
|
||||
response.write("123456789\n");
|
||||
}
|
||||
response.write("</div>");
|
||||
response.write("<span id='footnote'>This is a footnote after the huge content fill</span>");
|
||||
}
|
||||
|
||||
if (plugin) {
|
||||
response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " +
|
||||
"type='application/x-test'></embed>\n");
|
||||
}
|
||||
|
||||
response.write("</html>");
|
||||
}
|
||||
|
||||
|
||||
// base64 decoder
|
||||
//
|
||||
// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa()
|
||||
// doesn't seem to exist. :-(
|
||||
/* Convert Base64 data to a string */
|
||||
const toBinaryTable = [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
];
|
||||
const base64Pad = '=';
|
||||
|
||||
function base64ToString(data) {
|
||||
|
||||
var result = '';
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
|
||||
// Convert one by one.
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||
var padding = (data[i] == base64Pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c == -1) continue;
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding)
|
||||
result += String.fromCharCode((leftdata >> leftbits) & 0xff);
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits)
|
||||
throw Components.Exception('Corrupted base64 string');
|
||||
|
||||
return result;
|
||||
}
|
||||
8
toolkit/components/passwordmgr/test/blank.html
Normal file
8
toolkit/components/passwordmgr/test/blank.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
7
toolkit/components/passwordmgr/test/browser/.eslintrc.js
Normal file
7
toolkit/components/passwordmgr/test/browser/.eslintrc.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"../../../../../testing/mochitest/browser.eslintrc.js"
|
||||
]
|
||||
};
|
||||
110
toolkit/components/passwordmgr/test/browser/authenticate.sjs
Normal file
110
toolkit/components/passwordmgr/test/browser/authenticate.sjs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
var match;
|
||||
var requestAuth = true;
|
||||
|
||||
// Allow the caller to drive how authentication is processed via the query.
|
||||
// Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
|
||||
// The extra ? allows the user/pass/realm checks to succeed if the name is
|
||||
// at the beginning of the query string.
|
||||
var query = "?" + request.queryString;
|
||||
|
||||
var expected_user = "test", expected_pass = "testpass", realm = "mochitest";
|
||||
|
||||
// Look for an authentication header, if any, in the request.
|
||||
//
|
||||
// EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
//
|
||||
// This test only supports Basic auth. The value sent by the client is
|
||||
// "username:password", obscured with base64 encoding.
|
||||
|
||||
var actual_user = "", actual_pass = "", authHeader, authPresent = false;
|
||||
if (request.hasHeader("Authorization")) {
|
||||
authPresent = true;
|
||||
authHeader = request.getHeader("Authorization");
|
||||
match = /Basic (.+)/.exec(authHeader);
|
||||
if (match.length != 2)
|
||||
throw new Error("Couldn't parse auth header: " + authHeader);
|
||||
|
||||
var userpass = base64ToString(match[1]); // no atob() :-(
|
||||
match = /(.*):(.*)/.exec(userpass);
|
||||
if (match.length != 3)
|
||||
throw new Error("Couldn't decode auth header: " + userpass);
|
||||
actual_user = match[1];
|
||||
actual_pass = match[2];
|
||||
}
|
||||
|
||||
// Don't request authentication if the credentials we got were what we
|
||||
// expected.
|
||||
if (expected_user == actual_user &&
|
||||
expected_pass == actual_pass) {
|
||||
requestAuth = false;
|
||||
}
|
||||
|
||||
if (requestAuth) {
|
||||
response.setStatusLine("1.0", 401, "Authentication required");
|
||||
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
|
||||
} else {
|
||||
response.setStatusLine("1.0", 200, "OK");
|
||||
}
|
||||
|
||||
response.setHeader("Content-Type", "application/xhtml+xml", false);
|
||||
response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
|
||||
response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
|
||||
response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
|
||||
response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
|
||||
response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n");
|
||||
response.write("</html>");
|
||||
}
|
||||
|
||||
|
||||
// base64 decoder
|
||||
//
|
||||
// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa()
|
||||
// doesn't seem to exist. :-(
|
||||
/* Convert Base64 data to a string */
|
||||
const toBinaryTable = [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
];
|
||||
const base64Pad = '=';
|
||||
|
||||
function base64ToString(data) {
|
||||
|
||||
var result = '';
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
|
||||
// Convert one by one.
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||
var padding = (data[i] == base64Pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c == -1) continue;
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding)
|
||||
result += String.fromCharCode((leftdata >> leftbits) & 0xff);
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits)
|
||||
throw Components.Exception('Corrupted base64 string');
|
||||
|
||||
return result;
|
||||
}
|
||||
72
toolkit/components/passwordmgr/test/browser/browser.ini
Normal file
72
toolkit/components/passwordmgr/test/browser/browser.ini
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
../formsubmit.sjs
|
||||
authenticate.sjs
|
||||
form_basic.html
|
||||
form_basic_iframe.html
|
||||
formless_basic.html
|
||||
form_same_origin_action.html
|
||||
form_cross_origin_secure_action.html
|
||||
head.js
|
||||
insecure_test.html
|
||||
insecure_test_subframe.html
|
||||
multiple_forms.html
|
||||
streamConverter_content.sjs
|
||||
|
||||
[browser_autocomplete_insecure_warning.js]
|
||||
support-files =
|
||||
form_cross_origin_insecure_action.html
|
||||
[browser_capture_doorhanger.js]
|
||||
support-files =
|
||||
subtst_notifications_1.html
|
||||
subtst_notifications_2.html
|
||||
subtst_notifications_2pw_0un.html
|
||||
subtst_notifications_2pw_1un_1text.html
|
||||
subtst_notifications_3.html
|
||||
subtst_notifications_4.html
|
||||
subtst_notifications_5.html
|
||||
subtst_notifications_6.html
|
||||
subtst_notifications_8.html
|
||||
subtst_notifications_9.html
|
||||
subtst_notifications_10.html
|
||||
subtst_notifications_change_p.html
|
||||
[browser_capture_doorhanger_httpsUpgrade.js]
|
||||
support-files =
|
||||
subtst_notifications_1.html
|
||||
subtst_notifications_8.html
|
||||
[browser_capture_doorhanger_window_open.js]
|
||||
support-files =
|
||||
subtst_notifications_11.html
|
||||
subtst_notifications_11_popup.html
|
||||
skip-if = os == "linux" # Bug 1312981, bug 1313136
|
||||
[browser_context_menu_autocomplete_interaction.js]
|
||||
[browser_username_select_dialog.js]
|
||||
support-files =
|
||||
subtst_notifications_change_p.html
|
||||
[browser_DOMFormHasPassword.js]
|
||||
[browser_DOMInputPasswordAdded.js]
|
||||
[browser_exceptions_dialog.js]
|
||||
[browser_formless_submit_chrome.js]
|
||||
[browser_hasInsecureLoginForms.js]
|
||||
[browser_hasInsecureLoginForms_streamConverter.js]
|
||||
[browser_http_autofill.js]
|
||||
[browser_insecurePasswordConsoleWarning.js]
|
||||
support-files =
|
||||
form_cross_origin_insecure_action.html
|
||||
[browser_master_password_autocomplete.js]
|
||||
[browser_notifications.js]
|
||||
[browser_notifications_username.js]
|
||||
[browser_notifications_password.js]
|
||||
[browser_notifications_2.js]
|
||||
skip-if = os == "linux" # Bug 1272849 Main action button disabled state intermittent
|
||||
[browser_passwordmgr_editing.js]
|
||||
skip-if = os == "linux"
|
||||
[browser_context_menu.js]
|
||||
[browser_context_menu_iframe.js]
|
||||
[browser_passwordmgr_contextmenu.js]
|
||||
subsuite = clipboard
|
||||
[browser_passwordmgr_fields.js]
|
||||
[browser_passwordmgr_observers.js]
|
||||
[browser_passwordmgr_sort.js]
|
||||
[browser_passwordmgr_switchtab.js]
|
||||
[browser_passwordmgrdlg.js]
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
const ids = {
|
||||
INPUT_ID: "input1",
|
||||
FORM1_ID: "form1",
|
||||
FORM2_ID: "form2",
|
||||
CHANGE_INPUT_ID: "input2",
|
||||
};
|
||||
|
||||
function task(contentIds) {
|
||||
let resolve;
|
||||
let promise = new Promise(r => { resolve = r; });
|
||||
|
||||
function unexpectedContentEvent(evt) {
|
||||
ok(false, "Received a " + evt.type + " event on content");
|
||||
}
|
||||
|
||||
var gDoc = null;
|
||||
|
||||
addEventListener("load", tabLoad, true);
|
||||
|
||||
function tabLoad() {
|
||||
if (content.location.href == "about:blank")
|
||||
return;
|
||||
removeEventListener("load", tabLoad, true);
|
||||
|
||||
gDoc = content.document;
|
||||
gDoc.addEventListener("DOMFormHasPassword", unexpectedContentEvent, false);
|
||||
gDoc.defaultView.setTimeout(test_inputAdd, 0);
|
||||
}
|
||||
|
||||
function test_inputAdd() {
|
||||
addEventListener("DOMFormHasPassword", test_inputAddHandler, false);
|
||||
let input = gDoc.createElementNS("http://www.w3.org/1999/xhtml", "input");
|
||||
input.setAttribute("type", "password");
|
||||
input.setAttribute("id", contentIds.INPUT_ID);
|
||||
input.setAttribute("data-test", "unique-attribute");
|
||||
gDoc.getElementById(contentIds.FORM1_ID).appendChild(input);
|
||||
}
|
||||
|
||||
function test_inputAddHandler(evt) {
|
||||
removeEventListener(evt.type, test_inputAddHandler, false);
|
||||
is(evt.target.id, contentIds.FORM1_ID,
|
||||
evt.type + " event targets correct form element (added password element)");
|
||||
gDoc.defaultView.setTimeout(test_inputChangeForm, 0);
|
||||
}
|
||||
|
||||
function test_inputChangeForm() {
|
||||
addEventListener("DOMFormHasPassword", test_inputChangeFormHandler, false);
|
||||
let input = gDoc.getElementById(contentIds.INPUT_ID);
|
||||
input.setAttribute("form", contentIds.FORM2_ID);
|
||||
}
|
||||
|
||||
function test_inputChangeFormHandler(evt) {
|
||||
removeEventListener(evt.type, test_inputChangeFormHandler, false);
|
||||
is(evt.target.id, contentIds.FORM2_ID,
|
||||
evt.type + " event targets correct form element (changed form)");
|
||||
gDoc.defaultView.setTimeout(test_inputChangesType, 0);
|
||||
}
|
||||
|
||||
function test_inputChangesType() {
|
||||
addEventListener("DOMFormHasPassword", test_inputChangesTypeHandler, false);
|
||||
let input = gDoc.getElementById(contentIds.CHANGE_INPUT_ID);
|
||||
input.setAttribute("type", "password");
|
||||
}
|
||||
|
||||
function test_inputChangesTypeHandler(evt) {
|
||||
removeEventListener(evt.type, test_inputChangesTypeHandler, false);
|
||||
is(evt.target.id, contentIds.FORM1_ID,
|
||||
evt.type + " event targets correct form element (changed type)");
|
||||
gDoc.defaultView.setTimeout(finish, 0);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
gDoc.removeEventListener("DOMFormHasPassword", unexpectedContentEvent, false);
|
||||
resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
let promise = ContentTask.spawn(tab.linkedBrowser, ids, task);
|
||||
tab.linkedBrowser.loadURI("data:text/html;charset=utf-8," +
|
||||
"<html><body>" +
|
||||
"<form id='" + ids.FORM1_ID + "'>" +
|
||||
"<input id='" + ids.CHANGE_INPUT_ID + "'></form>" +
|
||||
"<form id='" + ids.FORM2_ID + "'></form>" +
|
||||
"</body></html>");
|
||||
yield promise;
|
||||
|
||||
ok(true, "Test completed");
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
const consts = {
|
||||
HTML_NS: "http://www.w3.org/1999/xhtml",
|
||||
|
||||
INPUT_ID: "input1",
|
||||
FORM1_ID: "form1",
|
||||
FORM2_ID: "form2",
|
||||
CHANGE_INPUT_ID: "input2",
|
||||
BODY_INPUT_ID: "input3",
|
||||
};
|
||||
|
||||
function task(contentConsts) {
|
||||
let resolve;
|
||||
let promise = new Promise(r => { resolve = r; });
|
||||
|
||||
function unexpectedContentEvent(evt) {
|
||||
Assert.ok(false, "Received a " + evt.type + " event on content");
|
||||
}
|
||||
|
||||
var gDoc = null;
|
||||
|
||||
addEventListener("load", tabLoad, true);
|
||||
|
||||
function tabLoad() {
|
||||
removeEventListener("load", tabLoad, true);
|
||||
gDoc = content.document;
|
||||
// These events shouldn't escape to content.
|
||||
gDoc.addEventListener("DOMInputPasswordAdded", unexpectedContentEvent, false);
|
||||
gDoc.defaultView.setTimeout(test_inputAdd, 0);
|
||||
}
|
||||
|
||||
function test_inputAdd() {
|
||||
addEventListener("DOMInputPasswordAdded", test_inputAddHandler, false);
|
||||
let input = gDoc.createElementNS(contentConsts.HTML_NS, "input");
|
||||
input.setAttribute("type", "password");
|
||||
input.setAttribute("id", contentConsts.INPUT_ID);
|
||||
input.setAttribute("data-test", "unique-attribute");
|
||||
gDoc.getElementById(contentConsts.FORM1_ID).appendChild(input);
|
||||
info("Done appending the input element");
|
||||
}
|
||||
|
||||
function test_inputAddHandler(evt) {
|
||||
removeEventListener(evt.type, test_inputAddHandler, false);
|
||||
Assert.equal(evt.target.id, contentConsts.INPUT_ID,
|
||||
evt.type + " event targets correct input element (added password element)");
|
||||
gDoc.defaultView.setTimeout(test_inputAddOutsideForm, 0);
|
||||
}
|
||||
|
||||
function test_inputAddOutsideForm() {
|
||||
addEventListener("DOMInputPasswordAdded", test_inputAddOutsideFormHandler, false);
|
||||
let input = gDoc.createElementNS(contentConsts.HTML_NS, "input");
|
||||
input.setAttribute("type", "password");
|
||||
input.setAttribute("id", contentConsts.BODY_INPUT_ID);
|
||||
input.setAttribute("data-test", "unique-attribute");
|
||||
gDoc.body.appendChild(input);
|
||||
info("Done appending the input element to the body");
|
||||
}
|
||||
|
||||
function test_inputAddOutsideFormHandler(evt) {
|
||||
removeEventListener(evt.type, test_inputAddOutsideFormHandler, false);
|
||||
Assert.equal(evt.target.id, contentConsts.BODY_INPUT_ID,
|
||||
evt.type + " event targets correct input element (added password element outside form)");
|
||||
gDoc.defaultView.setTimeout(test_inputChangesType, 0);
|
||||
}
|
||||
|
||||
function test_inputChangesType() {
|
||||
addEventListener("DOMInputPasswordAdded", test_inputChangesTypeHandler, false);
|
||||
let input = gDoc.getElementById(contentConsts.CHANGE_INPUT_ID);
|
||||
input.setAttribute("type", "password");
|
||||
}
|
||||
|
||||
function test_inputChangesTypeHandler(evt) {
|
||||
removeEventListener(evt.type, test_inputChangesTypeHandler, false);
|
||||
Assert.equal(evt.target.id, contentConsts.CHANGE_INPUT_ID,
|
||||
evt.type + " event targets correct input element (changed type)");
|
||||
gDoc.defaultView.setTimeout(completeTest, 0);
|
||||
}
|
||||
|
||||
function completeTest() {
|
||||
Assert.ok(true, "Test completed");
|
||||
gDoc.removeEventListener("DOMInputPasswordAdded", unexpectedContentEvent, false);
|
||||
resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
let promise = ContentTask.spawn(tab.linkedBrowser, consts, task);
|
||||
tab.linkedBrowser.loadURI("data:text/html;charset=utf-8," +
|
||||
"<html><body>" +
|
||||
"<form id='" + consts.FORM1_ID + "'>" +
|
||||
"<input id='" + consts.CHANGE_INPUT_ID + "'></form>" +
|
||||
"<form id='" + consts.FORM2_ID + "'></form>" +
|
||||
"</body></html>");
|
||||
yield promise;
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
"use strict";
|
||||
|
||||
const EXPECTED_SUPPORT_URL = Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
||||
"insecure-password";
|
||||
|
||||
add_task(function* test_clickInsecureFieldWarning() {
|
||||
let url = "https://example.com" + DIRECTORY_PATH + "form_cross_origin_insecure_action.html";
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url,
|
||||
}, function*(browser) {
|
||||
let popup = document.getElementById("PopupAutoComplete");
|
||||
ok(popup, "Got popup");
|
||||
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
|
||||
yield SimpleTest.promiseFocus(browser);
|
||||
info("content window focused");
|
||||
|
||||
// Focus the username field to open the popup.
|
||||
yield ContentTask.spawn(browser, null, function openAutocomplete() {
|
||||
content.document.getElementById("form-basic-username").focus();
|
||||
});
|
||||
|
||||
yield promiseShown;
|
||||
ok(promiseShown, "autocomplete shown");
|
||||
|
||||
let warningItem = document.getAnonymousElementByAttribute(popup, "type", "insecureWarning");
|
||||
ok(warningItem, "Got warning richlistitem");
|
||||
|
||||
yield BrowserTestUtils.waitForCondition(() => !warningItem.collapsed, "Wait for warning to show");
|
||||
|
||||
info("Clicking on warning");
|
||||
let supportTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, EXPECTED_SUPPORT_URL);
|
||||
EventUtils.synthesizeMouseAtCenter(warningItem, {});
|
||||
let supportTab = yield supportTabPromise;
|
||||
ok(supportTab, "Support tab opened");
|
||||
yield BrowserTestUtils.removeTab(supportTab);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,600 @@
|
|||
/*
|
||||
* Test capture popup notifications
|
||||
*/
|
||||
|
||||
const BRAND_BUNDLE = Services.strings.createBundle("chrome://branding/locale/brand.properties");
|
||||
const BRAND_SHORT_NAME = BRAND_BUNDLE.GetStringFromName("brandShortName");
|
||||
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login1 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"notifyu1", "notifyp1", "user", "pass");
|
||||
let login2 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"", "notifyp1", "", "pass");
|
||||
let login1B = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"notifyu1B", "notifyp1B", "user", "pass");
|
||||
let login2B = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"", "notifyp1B", "", "pass");
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(function* setup() {
|
||||
// Load recipes for this test.
|
||||
let recipeParent = yield LoginManagerParent.recipeParentPromise;
|
||||
yield recipeParent.load({
|
||||
siteRecipes: [{
|
||||
hosts: ["example.org"],
|
||||
usernameSelector: "#user",
|
||||
passwordSelector: "#pass",
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_remember_opens() {
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_clickNever() {
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
is(true, Services.logins.getLoginSavingEnabled("http://example.com"),
|
||||
"Checking for login saving enabled");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
|
||||
clickDoorhangerButton(notif, NEVER_BUTTON);
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
|
||||
info("Make sure Never took effect");
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
is(false, Services.logins.getLoginSavingEnabled("http://example.com"),
|
||||
"Checking for login saving disabled");
|
||||
Services.logins.setLoginSavingEnabled("http://example.com", true);
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_clickRemember() {
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
|
||||
clickDoorhangerButton(notif, REMEMBER_BUTTON);
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username used on the new entry");
|
||||
is(login.password, "notifyp1", "Check the password used on the new entry");
|
||||
is(login.timesUsed, 1, "Check times used on new entry");
|
||||
|
||||
info("Make sure Remember took effect and we don't prompt for an existing login");
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
});
|
||||
|
||||
logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username used");
|
||||
is(login.password, "notifyp1", "Check the password used");
|
||||
is(login.timesUsed, 2, "Check times used incremented");
|
||||
|
||||
checkOnlyLoginWasUsedTwice({ justChanged: false });
|
||||
|
||||
// remove that login
|
||||
Services.logins.removeLogin(login1);
|
||||
});
|
||||
|
||||
/* signons.rememberSignons pref tests... */
|
||||
|
||||
add_task(function* test_rememberSignonsFalse() {
|
||||
info("Make sure we don't prompt with rememberSignons=false");
|
||||
Services.prefs.setBoolPref("signon.rememberSignons", false);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_rememberSignonsTrue() {
|
||||
info("Make sure we prompt with rememberSignons=true");
|
||||
Services.prefs.setBoolPref("signon.rememberSignons", true);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
/* autocomplete=off tests... */
|
||||
|
||||
add_task(function* test_autocompleteOffUsername() {
|
||||
info("Check for notification popup when autocomplete=off present on username");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "checking for notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_autocompleteOffPassword() {
|
||||
info("Check for notification popup when autocomplete=off present on password");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_3.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "checking for notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_autocompleteOffForm() {
|
||||
info("Check for notification popup when autocomplete=off present on form");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_4.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "checking for notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_noPasswordField() {
|
||||
info("Check for no notification popup when no password field present");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_5.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "null", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_pwOnlyLoginMatchesForm() {
|
||||
info("Check for update popup when existing pw-only login matches form.");
|
||||
Services.logins.addLogin(login2);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "checking for notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "", "Check the username");
|
||||
is(login.password, "notifyp1", "Check the password");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login2);
|
||||
});
|
||||
|
||||
add_task(function* test_pwOnlyFormMatchesLogin() {
|
||||
info("Check for no notification popup when pw-only form matches existing login.");
|
||||
Services.logins.addLogin(login1);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_6.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username");
|
||||
is(login.password, "notifyp1", "Check the password");
|
||||
is(login.timesUsed, 2, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login1);
|
||||
});
|
||||
|
||||
add_task(function* test_pwOnlyFormDoesntMatchExisting() {
|
||||
info("Check for notification popup when pw-only form doesn't match existing login.");
|
||||
Services.logins.addLogin(login1B);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_6.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1B", "Check the username unchanged");
|
||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login1B);
|
||||
});
|
||||
|
||||
add_task(function* test_changeUPLoginOnUPForm_dont() {
|
||||
info("Check for change-password popup, u+p login on u+p form. (not changed)");
|
||||
Services.logins.addLogin(login1);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
|
||||
clickDoorhangerButton(notif, DONT_CHANGE_BUTTON);
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "notifyp1", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login1);
|
||||
});
|
||||
|
||||
add_task(function* test_changeUPLoginOnUPForm_change() {
|
||||
info("Check for change-password popup, u+p login on u+p form.");
|
||||
Services.logins.addLogin(login1);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
|
||||
clickDoorhangerButton(notif, CHANGE_BUTTON);
|
||||
|
||||
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "pass2", "Check the password changed");
|
||||
is(login.timesUsed, 2, "Check times used");
|
||||
|
||||
checkOnlyLoginWasUsedTwice({ justChanged: true });
|
||||
|
||||
// cleanup
|
||||
login1.password = "pass2";
|
||||
Services.logins.removeLogin(login1);
|
||||
login1.password = "notifyp1";
|
||||
});
|
||||
|
||||
add_task(function* test_changePLoginOnUPForm() {
|
||||
info("Check for change-password popup, p-only login on u+p form.");
|
||||
Services.logins.addLogin(login2);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_9.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("", "pass2");
|
||||
clickDoorhangerButton(notif, CHANGE_BUTTON);
|
||||
|
||||
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "", "Check the username unchanged");
|
||||
is(login.password, "pass2", "Check the password changed");
|
||||
is(login.timesUsed, 2, "Check times used");
|
||||
|
||||
// no cleanup -- saved password to be used in the next test.
|
||||
});
|
||||
|
||||
add_task(function* test_changePLoginOnPForm() {
|
||||
info("Check for change-password popup, p-only login on p-only form.");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_10.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("", "notifyp1");
|
||||
clickDoorhangerButton(notif, CHANGE_BUTTON);
|
||||
|
||||
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "", "Check the username unchanged");
|
||||
is(login.password, "notifyp1", "Check the password changed");
|
||||
is(login.timesUsed, 3, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login2);
|
||||
});
|
||||
|
||||
add_task(function* test_checkUPSaveText() {
|
||||
info("Check text on a user+pass notification popup");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
// Check the text, which comes from the localized saveLoginText string.
|
||||
let notificationText = notif.message;
|
||||
let expectedText = "Would you like " + BRAND_SHORT_NAME + " to remember this login?";
|
||||
is(expectedText, notificationText, "Checking text: " + notificationText);
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_checkPSaveText() {
|
||||
info("Check text on a pass-only notification popup");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_6.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
// Check the text, which comes from the localized saveLoginTextNoUser string.
|
||||
let notificationText = notif.message;
|
||||
let expectedText = "Would you like " + BRAND_SHORT_NAME + " to remember this password?";
|
||||
is(expectedText, notificationText, "Checking text: " + notificationText);
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_capture2pw0un() {
|
||||
info("Check for notification popup when a form with 2 password fields (no username) " +
|
||||
"is submitted and there are no saved logins.");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2pw_0un.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
|
||||
});
|
||||
|
||||
add_task(function* test_change2pw0unExistingDifferentUP() {
|
||||
info("Check for notification popup when a form with 2 password fields (no username) " +
|
||||
"is submitted and there is a saved login with a username and different password.");
|
||||
|
||||
Services.logins.addLogin(login1B);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2pw_0un.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1B", "Check the username unchanged");
|
||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login1B);
|
||||
});
|
||||
|
||||
add_task(function* test_change2pw0unExistingDifferentP() {
|
||||
info("Check for notification popup when a form with 2 password fields (no username) " +
|
||||
"is submitted and there is a saved login with no username and different password.");
|
||||
|
||||
Services.logins.addLogin(login2B);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2pw_0un.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
notif.remove();
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "", "Check the username unchanged");
|
||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
Services.logins.removeLogin(login2B);
|
||||
});
|
||||
|
||||
add_task(function* test_change2pw0unExistingWithSameP() {
|
||||
info("Check for no notification popup when a form with 2 password fields (no username) " +
|
||||
"is submitted and there is a saved login with a username and the same password.");
|
||||
|
||||
Services.logins.addLogin(login2);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2pw_0un.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "", "Check the username unchanged");
|
||||
is(login.password, "notifyp1", "Check the password unchanged");
|
||||
is(login.timesUsed, 2, "Check times used incremented");
|
||||
|
||||
checkOnlyLoginWasUsedTwice({ justChanged: false });
|
||||
|
||||
Services.logins.removeLogin(login2);
|
||||
});
|
||||
|
||||
add_task(function* test_changeUPLoginOnPUpdateForm() {
|
||||
info("Check for change-password popup, u+p login on password update form.");
|
||||
Services.logins.addLogin(login1);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_change_p.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
|
||||
clickDoorhangerButton(notif, CHANGE_BUTTON);
|
||||
|
||||
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "pass2", "Check the password changed");
|
||||
is(login.timesUsed, 2, "Check times used");
|
||||
|
||||
checkOnlyLoginWasUsedTwice({ justChanged: true });
|
||||
|
||||
// cleanup
|
||||
login1.password = "pass2";
|
||||
Services.logins.removeLogin(login1);
|
||||
login1.password = "notifyp1";
|
||||
});
|
||||
|
||||
add_task(function* test_recipeCaptureFields_NewLogin() {
|
||||
info("Check that we capture the proper fields when a field recipe is in use.");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2pw_1un_1text.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
// Sanity check, no logins should exist yet.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 0, "Should not have any logins yet");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
|
||||
clickDoorhangerButton(notif, REMEMBER_BUTTON);
|
||||
|
||||
}, "http://example.org"); // The recipe is for example.org
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "notifyp1", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
});
|
||||
|
||||
add_task(function* test_recipeCaptureFields_ExistingLogin() {
|
||||
info("Check that we capture the proper fields when a field recipe is in use " +
|
||||
"and there is a matching login");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_2pw_1un_1text.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
}, "http://example.org");
|
||||
|
||||
checkOnlyLoginWasUsedTwice({ justChanged: false });
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "notifyp1", "Check the password unchanged");
|
||||
is(login.timesUsed, 2, "Check times used incremented");
|
||||
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
add_task(function* test_noShowPasswordOnDismissal() {
|
||||
info("Check for no Show Password field when the doorhanger is dismissed");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
info("Opening popup");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
let { panel } = PopupNotifications;
|
||||
|
||||
info("Hiding popup.");
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(panel, "popuphidden");
|
||||
panel.hidePopup();
|
||||
yield promiseHidden;
|
||||
|
||||
info("Clicking on anchor to reshow popup.");
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(panel, "popupshown");
|
||||
notif.anchorElement.click();
|
||||
yield promiseShown;
|
||||
|
||||
let passwordVisiblityToggle = panel.querySelector("#password-notification-visibilityToggle");
|
||||
is(passwordVisiblityToggle.hidden, true, "Check that the Show Password field is Hidden");
|
||||
});
|
||||
});
|
||||
|
||||
// TODO:
|
||||
// * existing login test, form has different password --> change password, no save prompt
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Test capture popup notifications with HTTPS upgrades
|
||||
*/
|
||||
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login1 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"notifyu1", "notifyp1", "user", "pass");
|
||||
let login1HTTPS = new nsLoginInfo("https://example.com", "https://example.com", null,
|
||||
"notifyu1", "notifyp1", "user", "pass");
|
||||
|
||||
add_task(function* test_httpsUpgradeCaptureFields_noChange() {
|
||||
info("Check that we don't prompt to remember when capturing an upgraded login with no change");
|
||||
Services.logins.addLogin(login1);
|
||||
// Sanity check the HTTP login exists.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should have the HTTP login");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
}, "https://example.com"); // This is HTTPS whereas the saved login is HTTP
|
||||
|
||||
logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login still");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.hostname, "http://example.com", "Check the hostname is unchanged");
|
||||
is(login.username, "notifyu1", "Check the username is unchanged");
|
||||
is(login.password, "notifyp1", "Check the password is unchanged");
|
||||
is(login.timesUsed, 2, "Check times used increased");
|
||||
|
||||
Services.logins.removeLogin(login1);
|
||||
});
|
||||
|
||||
add_task(function* test_httpsUpgradeCaptureFields_changePW() {
|
||||
info("Check that we prompt to change when capturing an upgraded login with a new PW");
|
||||
Services.logins.addLogin(login1);
|
||||
// Sanity check the HTTP login exists.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should have the HTTP login");
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-change");
|
||||
ok(notif, "checking for a change popup");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
|
||||
clickDoorhangerButton(notif, CHANGE_BUTTON);
|
||||
|
||||
ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
|
||||
}, "https://example.com"); // This is HTTPS whereas the saved login is HTTP
|
||||
|
||||
checkOnlyLoginWasUsedTwice({ justChanged: true });
|
||||
logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login still");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.hostname, "https://example.com", "Check the hostname is upgraded");
|
||||
is(login.formSubmitURL, "https://example.com", "Check the formSubmitURL is upgraded");
|
||||
is(login.username, "notifyu1", "Check the username is unchanged");
|
||||
is(login.password, "pass2", "Check the password changed");
|
||||
is(login.timesUsed, 2, "Check times used increased");
|
||||
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
add_task(function* test_httpsUpgradeCaptureFields_captureMatchingHTTP() {
|
||||
info("Capture a new HTTP login which matches a stored HTTPS one.");
|
||||
Services.logins.addLogin(login1HTTPS);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
is(Services.logins.getAllLogins().length, 1, "Should only have the HTTPS login");
|
||||
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
|
||||
clickDoorhangerButton(notif, REMEMBER_BUTTON);
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 2, "Should have both HTTP and HTTPS logins");
|
||||
for (let login of logins) {
|
||||
login = login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username used on the new entry");
|
||||
is(login.password, "notifyp1", "Check the password used on the new entry");
|
||||
is(login.timesUsed, 1, "Check times used on entry");
|
||||
}
|
||||
|
||||
info("Make sure Remember took effect and we don't prompt for an existing HTTP login");
|
||||
yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "notifyu1", "Checking submitted username");
|
||||
is(fieldValues.password, "notifyp1", "Checking submitted password");
|
||||
let notif = getCaptureDoorhanger("password-save");
|
||||
ok(!notif, "checking for no notification popup");
|
||||
});
|
||||
|
||||
logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 2, "Should have both HTTP and HTTPS still");
|
||||
|
||||
let httpsLogins = LoginHelper.searchLoginsWithObject({
|
||||
hostname: "https://example.com",
|
||||
});
|
||||
is(httpsLogins.length, 1, "Check https logins count");
|
||||
let httpsLogin = httpsLogins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
ok(httpsLogin.equals(login1HTTPS), "Check HTTPS login didn't change");
|
||||
is(httpsLogin.timesUsed, 1, "Check times used");
|
||||
|
||||
let httpLogins = LoginHelper.searchLoginsWithObject({
|
||||
hostname: "http://example.com",
|
||||
});
|
||||
is(httpLogins.length, 1, "Check http logins count");
|
||||
let httpLogin = httpLogins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
ok(httpLogin.equals(login1), "Check HTTP login is as expected");
|
||||
is(httpLogin.timesUsed, 2, "Check times used increased");
|
||||
|
||||
Services.logins.removeLogin(login1);
|
||||
Services.logins.removeLogin(login1HTTPS);
|
||||
});
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Test capture popup notifications in content opened by window.open
|
||||
*/
|
||||
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login1 = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
||||
"notifyu1", "notifyp1", "user", "pass");
|
||||
let login2 = new nsLoginInfo("http://mochi.test:8888", "http://mochi.test:8888", null,
|
||||
"notifyu2", "notifyp2", "user", "pass");
|
||||
|
||||
|
||||
function withTestTabUntilStorageChange(aPageFile, aTaskFn) {
|
||||
function storageChangedObserved(subject, data) {
|
||||
// Watch for actions triggered from a doorhanger (not cleanup tasks with removeLogin)
|
||||
if (data == "removeLogin") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let storageChangedPromised = TestUtils.topicObserved("passwordmgr-storage-changed",
|
||||
storageChangedObserved);
|
||||
return BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "http://mochi.test:8888" + DIRECTORY_PATH + aPageFile,
|
||||
}, function*(browser) {
|
||||
ok(true, "loaded " + aPageFile);
|
||||
info("running test case task");
|
||||
yield* aTaskFn();
|
||||
info("waiting for storage change");
|
||||
yield storageChangedPromised;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SimpleTest.promiseFocus(window);
|
||||
});
|
||||
|
||||
add_task(function* test_saveChromeHiddenAutoClose() {
|
||||
let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
|
||||
// query arguments are: username, password, features, auto-close (delimited by '|')
|
||||
let url = "subtst_notifications_11.html?notifyu1|notifyp1|" +
|
||||
"menubar=no,toolbar=no,location=no|autoclose";
|
||||
yield withTestTabUntilStorageChange(url, function*() {
|
||||
info("waiting for popupshown");
|
||||
yield notifShownPromise;
|
||||
// the popup closes and the doorhanger should appear in the opener
|
||||
let popup = getCaptureDoorhanger("password-save");
|
||||
ok(popup, "got notification popup");
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
|
||||
// Sanity check, no logins should exist yet.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 0, "Should not have any logins yet");
|
||||
|
||||
clickDoorhangerButton(popup, REMEMBER_BUTTON);
|
||||
});
|
||||
// Check result of clicking Remember
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.timesUsed, 1, "Check times used on new entry");
|
||||
is(login.username, "notifyu1", "Check the username used on the new entry");
|
||||
is(login.password, "notifyp1", "Check the password used on the new entry");
|
||||
});
|
||||
|
||||
add_task(function* test_changeChromeHiddenAutoClose() {
|
||||
let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
|
||||
let url = "subtst_notifications_11.html?notifyu1|pass2|menubar=no,toolbar=no,location=no|autoclose";
|
||||
yield withTestTabUntilStorageChange(url, function*() {
|
||||
info("waiting for popupshown");
|
||||
yield notifShownPromise;
|
||||
let popup = getCaptureDoorhanger("password-change");
|
||||
ok(popup, "got notification popup");
|
||||
yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
|
||||
clickDoorhangerButton(popup, CHANGE_BUTTON);
|
||||
});
|
||||
|
||||
// Check to make sure we updated the password, timestamps and use count for
|
||||
// the login being changed with this form.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username");
|
||||
is(login.password, "pass2", "Check password changed");
|
||||
is(login.timesUsed, 2, "check .timesUsed incremented on change");
|
||||
ok(login.timeCreated < login.timeLastUsed, "timeLastUsed bumped");
|
||||
ok(login.timeLastUsed == login.timePasswordChanged, "timeUsed == timeChanged");
|
||||
|
||||
login1.password = "pass2";
|
||||
Services.logins.removeLogin(login1);
|
||||
login1.password = "notifyp1";
|
||||
});
|
||||
|
||||
add_task(function* test_saveChromeVisibleSameWindow() {
|
||||
// This test actually opens a new tab in the same window with default browser settings.
|
||||
let url = "subtst_notifications_11.html?notifyu2|notifyp2||";
|
||||
let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
|
||||
yield withTestTabUntilStorageChange(url, function*() {
|
||||
yield notifShownPromise;
|
||||
let popup = getCaptureDoorhanger("password-save");
|
||||
ok(popup, "got notification popup");
|
||||
yield* checkDoorhangerUsernamePassword("notifyu2", "notifyp2");
|
||||
clickDoorhangerButton(popup, REMEMBER_BUTTON);
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
// Check result of clicking Remember
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login now");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu2", "Check the username used on the new entry");
|
||||
is(login.password, "notifyp2", "Check the password used on the new entry");
|
||||
is(login.timesUsed, 1, "Check times used on new entry");
|
||||
});
|
||||
|
||||
add_task(function* test_changeChromeVisibleSameWindow() {
|
||||
let url = "subtst_notifications_11.html?notifyu2|pass2||";
|
||||
let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
|
||||
yield withTestTabUntilStorageChange(url, function*() {
|
||||
yield notifShownPromise;
|
||||
let popup = getCaptureDoorhanger("password-change");
|
||||
ok(popup, "got notification popup");
|
||||
yield* checkDoorhangerUsernamePassword("notifyu2", "pass2");
|
||||
clickDoorhangerButton(popup, CHANGE_BUTTON);
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
// Check to make sure we updated the password, timestamps and use count for
|
||||
// the login being changed with this form.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should have 1 login");
|
||||
let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu2", "Check the username");
|
||||
is(login.password, "pass2", "Check password changed");
|
||||
is(login.timesUsed, 2, "check .timesUsed incremented on change");
|
||||
ok(login.timeCreated < login.timeLastUsed, "timeLastUsed bumped");
|
||||
ok(login.timeLastUsed == login.timePasswordChanged, "timeUsed == timeChanged");
|
||||
|
||||
// cleanup
|
||||
login2.password = "pass2";
|
||||
Services.logins.removeLogin(login2);
|
||||
login2.password = "notifyp2";
|
||||
});
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* Test the password manager context menu.
|
||||
*/
|
||||
|
||||
/* eslint no-shadow:"off" */
|
||||
|
||||
"use strict";
|
||||
|
||||
// The hostname for the test URIs.
|
||||
const TEST_HOSTNAME = "https://example.com";
|
||||
const MULTIPLE_FORMS_PAGE_PATH = "/browser/toolkit/components/passwordmgr/test/browser/multiple_forms.html";
|
||||
|
||||
const CONTEXT_MENU = document.getElementById("contentAreaContextMenu");
|
||||
const POPUP_HEADER = document.getElementById("fill-login");
|
||||
|
||||
/**
|
||||
* Initialize logins needed for the tests and disable autofill
|
||||
* for login forms for easier testing of manual fill.
|
||||
*/
|
||||
add_task(function* test_initialize() {
|
||||
Services.prefs.setBoolPref("signon.autofillForms", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("signon.autofillForms");
|
||||
Services.prefs.clearUserPref("signon.schemeUpgrades");
|
||||
});
|
||||
for (let login of loginList()) {
|
||||
Services.logins.addLogin(login);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the context menu is populated with the right
|
||||
* menuitems for the target password input field.
|
||||
*/
|
||||
add_task(function* test_context_menu_populate_password_noSchemeUpgrades() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", false);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
|
||||
}, function* (browser) {
|
||||
yield openPasswordContextMenu(browser, "#test-password-1");
|
||||
|
||||
// Check the content of the password manager popup
|
||||
let popupMenu = document.getElementById("fill-login-popup");
|
||||
checkMenu(popupMenu, 2);
|
||||
|
||||
CONTEXT_MENU.hidePopup();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the context menu is populated with the right
|
||||
* menuitems for the target password input field.
|
||||
*/
|
||||
add_task(function* test_context_menu_populate_password_schemeUpgrades() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", true);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
|
||||
}, function* (browser) {
|
||||
yield openPasswordContextMenu(browser, "#test-password-1");
|
||||
|
||||
// Check the content of the password manager popup
|
||||
let popupMenu = document.getElementById("fill-login-popup");
|
||||
checkMenu(popupMenu, 3);
|
||||
|
||||
CONTEXT_MENU.hidePopup();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the context menu is populated with the right menuitems
|
||||
* for the target username field with a password field present.
|
||||
*/
|
||||
add_task(function* test_context_menu_populate_username_with_password_noSchemeUpgrades() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", false);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + "/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/multiple_forms.html",
|
||||
}, function* (browser) {
|
||||
yield openPasswordContextMenu(browser, "#test-username-2");
|
||||
|
||||
// Check the content of the password manager popup
|
||||
let popupMenu = document.getElementById("fill-login-popup");
|
||||
checkMenu(popupMenu, 2);
|
||||
|
||||
CONTEXT_MENU.hidePopup();
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Check if the context menu is populated with the right menuitems
|
||||
* for the target username field with a password field present.
|
||||
*/
|
||||
add_task(function* test_context_menu_populate_username_with_password_schemeUpgrades() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", true);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + "/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/multiple_forms.html",
|
||||
}, function* (browser) {
|
||||
yield openPasswordContextMenu(browser, "#test-username-2");
|
||||
|
||||
// Check the content of the password manager popup
|
||||
let popupMenu = document.getElementById("fill-login-popup");
|
||||
checkMenu(popupMenu, 3);
|
||||
|
||||
CONTEXT_MENU.hidePopup();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the password field is correctly filled when one
|
||||
* login menuitem is clicked.
|
||||
*/
|
||||
add_task(function* test_context_menu_password_fill() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", true);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
|
||||
}, function* (browser) {
|
||||
let formDescriptions = yield ContentTask.spawn(browser, {}, function*() {
|
||||
let forms = Array.from(content.document.getElementsByClassName("test-form"));
|
||||
return forms.map((f) => f.getAttribute("description"));
|
||||
});
|
||||
|
||||
for (let description of formDescriptions) {
|
||||
info("Testing form: " + description);
|
||||
|
||||
let passwordInputIds = yield ContentTask.spawn(browser, {description}, function*({description}) {
|
||||
let formElement = content.document.querySelector(`[description="${description}"]`);
|
||||
let passwords = Array.from(formElement.querySelectorAll("input[type='password']"));
|
||||
return passwords.map((p) => p.id);
|
||||
});
|
||||
|
||||
for (let inputId of passwordInputIds) {
|
||||
info("Testing password field: " + inputId);
|
||||
|
||||
// Synthesize a right mouse click over the username input element.
|
||||
yield openPasswordContextMenu(browser, "#" + inputId, function*() {
|
||||
let inputDisabled = yield ContentTask
|
||||
.spawn(browser, {inputId}, function*({inputId}) {
|
||||
let input = content.document.getElementById(inputId);
|
||||
return input.disabled || input.readOnly;
|
||||
});
|
||||
|
||||
// If the password field is disabled or read-only, we want to see
|
||||
// the disabled Fill Password popup header.
|
||||
if (inputDisabled) {
|
||||
Assert.ok(!POPUP_HEADER.hidden, "Popup menu is not hidden.");
|
||||
Assert.ok(POPUP_HEADER.disabled, "Popup menu is disabled.");
|
||||
CONTEXT_MENU.hidePopup();
|
||||
}
|
||||
|
||||
return !inputDisabled;
|
||||
});
|
||||
|
||||
if (CONTEXT_MENU.state != "open") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The only field affected by the password fill
|
||||
// should be the target password field itself.
|
||||
yield assertContextMenuFill(browser, description, null, inputId, 1);
|
||||
yield ContentTask.spawn(browser, {inputId}, function*({inputId}) {
|
||||
let passwordField = content.document.getElementById(inputId);
|
||||
Assert.equal(passwordField.value, "password1", "Check upgraded login was actually used");
|
||||
});
|
||||
|
||||
CONTEXT_MENU.hidePopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the form is correctly filled when one
|
||||
* username context menu login menuitem is clicked.
|
||||
*/
|
||||
add_task(function* test_context_menu_username_login_fill() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", true);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + MULTIPLE_FORMS_PAGE_PATH,
|
||||
}, function* (browser) {
|
||||
|
||||
let formDescriptions = yield ContentTask.spawn(browser, {}, function*() {
|
||||
let forms = Array.from(content.document.getElementsByClassName("test-form"));
|
||||
return forms.map((f) => f.getAttribute("description"));
|
||||
});
|
||||
|
||||
for (let description of formDescriptions) {
|
||||
info("Testing form: " + description);
|
||||
let usernameInputIds = yield ContentTask
|
||||
.spawn(browser, {description}, function*({description}) {
|
||||
let formElement = content.document.querySelector(`[description="${description}"]`);
|
||||
let inputs = Array.from(formElement.querySelectorAll("input[type='text']"));
|
||||
return inputs.map((p) => p.id);
|
||||
});
|
||||
|
||||
for (let inputId of usernameInputIds) {
|
||||
info("Testing username field: " + inputId);
|
||||
|
||||
// Synthesize a right mouse click over the username input element.
|
||||
yield openPasswordContextMenu(browser, "#" + inputId, function*() {
|
||||
let headerHidden = POPUP_HEADER.hidden;
|
||||
let headerDisabled = POPUP_HEADER.disabled;
|
||||
|
||||
let data = {description, inputId, headerHidden, headerDisabled};
|
||||
let shouldContinue = yield ContentTask.spawn(browser, data, function*(data) {
|
||||
let {description, inputId, headerHidden, headerDisabled} = data;
|
||||
let formElement = content.document.querySelector(`[description="${description}"]`);
|
||||
let usernameField = content.document.getElementById(inputId);
|
||||
// We always want to check if the first password field is filled,
|
||||
// since this is the current behavior from the _fillForm function.
|
||||
let passwordField = formElement.querySelector("input[type='password']");
|
||||
|
||||
// If we don't want to see the actual popup menu,
|
||||
// check if the popup is hidden or disabled.
|
||||
if (!passwordField || usernameField.disabled || usernameField.readOnly ||
|
||||
passwordField.disabled || passwordField.readOnly) {
|
||||
if (!passwordField) {
|
||||
Assert.ok(headerHidden, "Popup menu is hidden.");
|
||||
} else {
|
||||
Assert.ok(!headerHidden, "Popup menu is not hidden.");
|
||||
Assert.ok(headerDisabled, "Popup menu is disabled.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!shouldContinue) {
|
||||
CONTEXT_MENU.hidePopup();
|
||||
}
|
||||
|
||||
return shouldContinue;
|
||||
});
|
||||
|
||||
if (CONTEXT_MENU.state != "open") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let passwordFieldId = yield ContentTask
|
||||
.spawn(browser, {description}, function*({description}) {
|
||||
let formElement = content.document.querySelector(`[description="${description}"]`);
|
||||
return formElement.querySelector("input[type='password']").id;
|
||||
});
|
||||
|
||||
// We shouldn't change any field that's not the target username field or the first password field
|
||||
yield assertContextMenuFill(browser, description, inputId, passwordFieldId, 1);
|
||||
|
||||
yield ContentTask.spawn(browser, {passwordFieldId}, function*({passwordFieldId}) {
|
||||
let passwordField = content.document.getElementById(passwordFieldId);
|
||||
if (!passwordField.hasAttribute("expectedFail")) {
|
||||
Assert.equal(passwordField.value, "password1", "Check upgraded login was actually used");
|
||||
}
|
||||
});
|
||||
|
||||
CONTEXT_MENU.hidePopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Synthesize mouse clicks to open the password manager context menu popup
|
||||
* for a target password input element.
|
||||
*
|
||||
* assertCallback should return true if we should continue or else false.
|
||||
*/
|
||||
function* openPasswordContextMenu(browser, passwordInput, assertCallback = null) {
|
||||
// Synthesize a right mouse click over the password input element.
|
||||
let contextMenuShownPromise = BrowserTestUtils.waitForEvent(CONTEXT_MENU, "popupshown");
|
||||
let eventDetails = {type: "contextmenu", button: 2};
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(passwordInput, eventDetails, browser);
|
||||
yield contextMenuShownPromise;
|
||||
|
||||
if (assertCallback) {
|
||||
let shouldContinue = yield assertCallback();
|
||||
if (!shouldContinue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Synthesize a mouse click over the fill login menu header.
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(POPUP_HEADER, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(POPUP_HEADER, {});
|
||||
yield popupShownPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that only the expected form fields are filled.
|
||||
*/
|
||||
function* assertContextMenuFill(browser, formId, usernameFieldId, passwordFieldId, loginIndex) {
|
||||
let popupMenu = document.getElementById("fill-login-popup");
|
||||
let unchangedSelector = `[description="${formId}"] input:not(#${passwordFieldId})`;
|
||||
|
||||
if (usernameFieldId) {
|
||||
unchangedSelector += `:not(#${usernameFieldId})`;
|
||||
}
|
||||
|
||||
yield ContentTask.spawn(browser, {unchangedSelector}, function*({unchangedSelector}) {
|
||||
let unchangedFields = content.document.querySelectorAll(unchangedSelector);
|
||||
|
||||
// Store the value of fields that should remain unchanged.
|
||||
if (unchangedFields.length) {
|
||||
for (let field of unchangedFields) {
|
||||
field.setAttribute("original-value", field.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Execute the default command of the specified login menuitem found in the context menu.
|
||||
let loginItem = popupMenu.getElementsByClassName("context-login-item")[loginIndex];
|
||||
|
||||
// Find the used login by it's username (Use only unique usernames in this test).
|
||||
let {username, password} = getLoginFromUsername(loginItem.label);
|
||||
|
||||
let data = {username, password, usernameFieldId, passwordFieldId, formId, unchangedSelector};
|
||||
let continuePromise = ContentTask.spawn(browser, data, function*(data) {
|
||||
let {username, password, usernameFieldId, passwordFieldId, formId, unchangedSelector} = data;
|
||||
let form = content.document.querySelector(`[description="${formId}"]`);
|
||||
yield ContentTaskUtils.waitForEvent(form, "input", "Username input value changed");
|
||||
|
||||
if (usernameFieldId) {
|
||||
let usernameField = content.document.getElementById(usernameFieldId);
|
||||
|
||||
// If we have an username field, check if it's correctly filled
|
||||
if (usernameField.getAttribute("expectedFail") == null) {
|
||||
Assert.equal(username, usernameField.value, "Username filled and correct.");
|
||||
}
|
||||
}
|
||||
|
||||
if (passwordFieldId) {
|
||||
let passwordField = content.document.getElementById(passwordFieldId);
|
||||
|
||||
// If we have a password field, check if it's correctly filled
|
||||
if (passwordField && passwordField.getAttribute("expectedFail") == null) {
|
||||
Assert.equal(password, passwordField.value, "Password filled and correct.");
|
||||
}
|
||||
}
|
||||
|
||||
let unchangedFields = content.document.querySelectorAll(unchangedSelector);
|
||||
|
||||
// Check that all fields that should not change have the same value as before.
|
||||
if (unchangedFields.length) {
|
||||
Assert.ok(() => {
|
||||
for (let field of unchangedFields) {
|
||||
if (field.value != field.getAttribute("original-value")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, "Other fields were not changed.");
|
||||
}
|
||||
});
|
||||
|
||||
loginItem.doCommand();
|
||||
|
||||
return continuePromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if every login that matches the page hostname are available at the context menu.
|
||||
* @param {Element} contextMenu
|
||||
* @param {Number} expectedCount - Number of logins expected in the context menu. Used to ensure
|
||||
* we continue testing something useful.
|
||||
*/
|
||||
function checkMenu(contextMenu, expectedCount) {
|
||||
let logins = loginList().filter(login => {
|
||||
return LoginHelper.isOriginMatching(login.hostname, TEST_HOSTNAME, {
|
||||
schemeUpgrades: Services.prefs.getBoolPref("signon.schemeUpgrades"),
|
||||
});
|
||||
});
|
||||
// Make an array of menuitems for easier comparison.
|
||||
let menuitems = [...CONTEXT_MENU.getElementsByClassName("context-login-item")];
|
||||
Assert.equal(menuitems.length, expectedCount, "Expected number of menu items");
|
||||
Assert.ok(logins.every(l => menuitems.some(m => l.username == m.label)), "Every login have an item at the menu.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a login by it's username.
|
||||
*
|
||||
* Only unique login/hostname combinations should be used at this test.
|
||||
*/
|
||||
function getLoginFromUsername(username) {
|
||||
return loginList().find(login => login.username == username);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of logins used for the test.
|
||||
*
|
||||
* We should only use unique usernames in this test,
|
||||
* because we need to search logins by username. There is one duplicate u+p combo
|
||||
* in order to test de-duping in the menu.
|
||||
*/
|
||||
function loginList() {
|
||||
return [
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username",
|
||||
password: "password",
|
||||
}),
|
||||
// Same as above but HTTP in order to test de-duping.
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.com",
|
||||
formSubmitURL: "http://example.com",
|
||||
username: "username",
|
||||
password: "password",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.com",
|
||||
formSubmitURL: "http://example.com",
|
||||
username: "username1",
|
||||
password: "password1",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username2",
|
||||
password: "password2",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.org",
|
||||
formSubmitURL: "http://example.org",
|
||||
username: "username-cross-origin",
|
||||
password: "password-cross-origin",
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Test the password manager context menu interaction with autocomplete.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_HOSTNAME = "https://example.com";
|
||||
const BASIC_FORM_PAGE_PATH = DIRECTORY_PATH + "form_basic.html";
|
||||
|
||||
var gUnexpectedIsTODO = false;
|
||||
|
||||
/**
|
||||
* Initialize logins needed for the tests and disable autofill
|
||||
* for login forms for easier testing of manual fill.
|
||||
*/
|
||||
add_task(function* test_initialize() {
|
||||
let autocompletePopup = document.getElementById("PopupAutoComplete");
|
||||
Services.prefs.setBoolPref("signon.autofillForms", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("signon.autofillForms");
|
||||
autocompletePopup.removeEventListener("popupshowing", autocompleteUnexpectedPopupShowing);
|
||||
});
|
||||
for (let login of loginList()) {
|
||||
Services.logins.addLogin(login);
|
||||
}
|
||||
autocompletePopup.addEventListener("popupshowing", autocompleteUnexpectedPopupShowing);
|
||||
});
|
||||
|
||||
add_task(function* test_context_menu_username() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + BASIC_FORM_PAGE_PATH,
|
||||
}, function* (browser) {
|
||||
yield openContextMenu(browser, "#form-basic-username");
|
||||
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
Assert.equal(contextMenu.state, "open", "Context menu opened");
|
||||
contextMenu.hidePopup();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_context_menu_password() {
|
||||
gUnexpectedIsTODO = true;
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + BASIC_FORM_PAGE_PATH,
|
||||
}, function* (browser) {
|
||||
yield openContextMenu(browser, "#form-basic-password");
|
||||
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
Assert.equal(contextMenu.state, "open", "Context menu opened");
|
||||
contextMenu.hidePopup();
|
||||
});
|
||||
});
|
||||
|
||||
function autocompleteUnexpectedPopupShowing(event) {
|
||||
if (gUnexpectedIsTODO) {
|
||||
todo(false, "Autocomplete shouldn't appear");
|
||||
} else {
|
||||
Assert.ok(false, "Autocomplete shouldn't appear");
|
||||
}
|
||||
event.target.hidePopup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize mouse clicks to open the context menu popup
|
||||
* for a target login input element.
|
||||
*/
|
||||
function* openContextMenu(browser, loginInput) {
|
||||
// First synthesize a mousedown. We need this to get the focus event with the "contextmenu" event.
|
||||
let eventDetails1 = {type: "mousedown", button: 2};
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(loginInput, eventDetails1, browser);
|
||||
|
||||
// Then synthesize the contextmenu click over the input element.
|
||||
let contextMenuShownPromise = BrowserTestUtils.waitForEvent(window, "popupshown");
|
||||
let eventDetails = {type: "contextmenu", button: 2};
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(loginInput, eventDetails, browser);
|
||||
yield contextMenuShownPromise;
|
||||
|
||||
// Wait to see which popups are shown.
|
||||
yield new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
function loginList() {
|
||||
return [
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username",
|
||||
password: "password",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username2",
|
||||
password: "password2",
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Test the password manager context menu.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_HOSTNAME = "https://example.com";
|
||||
|
||||
// Test with a page that only has a form within an iframe, not in the top-level document
|
||||
const IFRAME_PAGE_PATH = "/browser/toolkit/components/passwordmgr/test/browser/form_basic_iframe.html";
|
||||
|
||||
/**
|
||||
* Initialize logins needed for the tests and disable autofill
|
||||
* for login forms for easier testing of manual fill.
|
||||
*/
|
||||
add_task(function* test_initialize() {
|
||||
Services.prefs.setBoolPref("signon.autofillForms", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("signon.autofillForms");
|
||||
Services.prefs.clearUserPref("signon.schemeUpgrades");
|
||||
});
|
||||
for (let login of loginList()) {
|
||||
Services.logins.addLogin(login);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the password field is correctly filled when it's in an iframe.
|
||||
*/
|
||||
add_task(function* test_context_menu_iframe_fill() {
|
||||
Services.prefs.setBoolPref("signon.schemeUpgrades", true);
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: TEST_HOSTNAME + IFRAME_PAGE_PATH
|
||||
}, function* (browser) {
|
||||
function getPasswordInput() {
|
||||
let frame = content.document.getElementById("test-iframe");
|
||||
return frame.contentDocument.getElementById("form-basic-password");
|
||||
}
|
||||
|
||||
let contextMenuShownPromise = BrowserTestUtils.waitForEvent(window, "popupshown");
|
||||
let eventDetails = {type: "contextmenu", button: 2};
|
||||
|
||||
// To click at the right point we have to take into account the iframe offset.
|
||||
// Synthesize a right mouse click over the password input element.
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(getPasswordInput, eventDetails, browser);
|
||||
yield contextMenuShownPromise;
|
||||
|
||||
// Synthesize a mouse click over the fill login menu header.
|
||||
let popupHeader = document.getElementById("fill-login");
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(popupHeader, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(popupHeader, {});
|
||||
yield popupShownPromise;
|
||||
|
||||
let popupMenu = document.getElementById("fill-login-popup");
|
||||
|
||||
// Stores the original value of username
|
||||
function promiseFrameInputValue(name) {
|
||||
return ContentTask.spawn(browser, name, function(inputname) {
|
||||
let iframe = content.document.getElementById("test-iframe");
|
||||
let input = iframe.contentDocument.getElementById(inputname);
|
||||
return input.value;
|
||||
});
|
||||
}
|
||||
let usernameOriginalValue = yield promiseFrameInputValue("form-basic-username");
|
||||
|
||||
// Execute the command of the first login menuitem found at the context menu.
|
||||
let passwordChangedPromise = ContentTask.spawn(browser, null, function* () {
|
||||
let frame = content.document.getElementById("test-iframe");
|
||||
let passwordInput = frame.contentDocument.getElementById("form-basic-password");
|
||||
yield ContentTaskUtils.waitForEvent(passwordInput, "input");
|
||||
});
|
||||
|
||||
let firstLoginItem = popupMenu.getElementsByClassName("context-login-item")[0];
|
||||
firstLoginItem.doCommand();
|
||||
|
||||
yield passwordChangedPromise;
|
||||
|
||||
// Find the used login by it's username.
|
||||
let login = getLoginFromUsername(firstLoginItem.label);
|
||||
let passwordValue = yield promiseFrameInputValue("form-basic-password");
|
||||
is(login.password, passwordValue, "Password filled and correct.");
|
||||
|
||||
let usernameNewValue = yield promiseFrameInputValue("form-basic-username");
|
||||
is(usernameOriginalValue,
|
||||
usernameNewValue,
|
||||
"Username value was not changed.");
|
||||
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
contextMenu.hidePopup();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Search for a login by it's username.
|
||||
*
|
||||
* Only unique login/hostname combinations should be used at this test.
|
||||
*/
|
||||
function getLoginFromUsername(username) {
|
||||
return loginList().find(login => login.username == username);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of logins used for the test.
|
||||
*
|
||||
* We should only use unique usernames in this test,
|
||||
* because we need to search logins by username. There is one duplicate u+p combo
|
||||
* in order to test de-duping in the menu.
|
||||
*/
|
||||
function loginList() {
|
||||
return [
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username",
|
||||
password: "password",
|
||||
}),
|
||||
// Same as above but HTTP in order to test de-duping.
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.com",
|
||||
formSubmitURL: "http://example.com",
|
||||
username: "username",
|
||||
password: "password",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.com",
|
||||
formSubmitURL: "http://example.com",
|
||||
username: "username1",
|
||||
password: "password1",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username2",
|
||||
password: "password2",
|
||||
}),
|
||||
LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.org",
|
||||
formSubmitURL: "http://example.org",
|
||||
username: "username-cross-origin",
|
||||
password: "password-cross-origin",
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const LOGIN_HOST = "http://example.com";
|
||||
|
||||
function openExceptionsDialog() {
|
||||
return window.openDialog(
|
||||
"chrome://browser/content/preferences/permissions.xul",
|
||||
"Toolkit:PasswordManagerExceptions", "",
|
||||
{
|
||||
blockVisible: true,
|
||||
sessionVisible: false,
|
||||
allowVisible: false,
|
||||
hideStatusColumn: true,
|
||||
prefilledHost: "",
|
||||
permissionType: "login-saving"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function countDisabledHosts(dialog) {
|
||||
let doc = dialog.document;
|
||||
let rejectsTree = doc.getElementById("permissionsTree");
|
||||
|
||||
return rejectsTree.view.rowCount;
|
||||
}
|
||||
|
||||
function promiseStorageChanged(expectedData) {
|
||||
function observer(subject, data) {
|
||||
return data == expectedData && subject.QueryInterface(Ci.nsISupportsString).data == LOGIN_HOST;
|
||||
}
|
||||
|
||||
return TestUtils.topicObserved("passwordmgr-storage-changed", observer);
|
||||
}
|
||||
|
||||
add_task(function* test_disable() {
|
||||
let dialog = openExceptionsDialog();
|
||||
let promiseChanged = promiseStorageChanged("hostSavingDisabled");
|
||||
|
||||
yield BrowserTestUtils.waitForEvent(dialog, "load");
|
||||
Services.logins.setLoginSavingEnabled(LOGIN_HOST, false);
|
||||
yield promiseChanged;
|
||||
is(countDisabledHosts(dialog), 1, "Verify disabled host added");
|
||||
yield BrowserTestUtils.closeWindow(dialog);
|
||||
});
|
||||
|
||||
add_task(function* test_enable() {
|
||||
let dialog = openExceptionsDialog();
|
||||
let promiseChanged = promiseStorageChanged("hostSavingEnabled");
|
||||
|
||||
yield BrowserTestUtils.waitForEvent(dialog, "load");
|
||||
Services.logins.setLoginSavingEnabled(LOGIN_HOST, true);
|
||||
yield promiseChanged;
|
||||
is(countDisabledHosts(dialog), 0, "Verify disabled host removed");
|
||||
yield BrowserTestUtils.closeWindow(dialog);
|
||||
});
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Test that browser chrome UI interactions don't trigger a capture doorhanger.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
function* fillTestPage(aBrowser) {
|
||||
yield ContentTask.spawn(aBrowser, null, function*() {
|
||||
content.document.getElementById("form-basic-username").value = "my_username";
|
||||
content.document.getElementById("form-basic-password").value = "my_password";
|
||||
});
|
||||
info("fields filled");
|
||||
}
|
||||
|
||||
function* withTestPage(aTaskFn) {
|
||||
return BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com" + DIRECTORY_PATH + "formless_basic.html",
|
||||
}, function*(aBrowser) {
|
||||
info("tab opened");
|
||||
yield fillTestPage(aBrowser);
|
||||
yield* aTaskFn(aBrowser);
|
||||
|
||||
// Give a chance for the doorhanger to appear
|
||||
yield new Promise(resolve => SimpleTest.executeSoon(resolve));
|
||||
ok(!getCaptureDoorhanger("any"), "No doorhanger should be present");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SimpleTest.promiseFocus(window);
|
||||
});
|
||||
|
||||
add_task(function* test_urlbar_new_URL() {
|
||||
yield withTestPage(function*(aBrowser) {
|
||||
gURLBar.value = "";
|
||||
let focusPromise = BrowserTestUtils.waitForEvent(gURLBar, "focus");
|
||||
gURLBar.focus();
|
||||
yield focusPromise;
|
||||
info("focused");
|
||||
EventUtils.sendString("http://mochi.test:8888/");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
yield BrowserTestUtils.browserLoaded(aBrowser, false, "http://mochi.test:8888/");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_urlbar_fragment_enter() {
|
||||
yield withTestPage(function*(aBrowser) {
|
||||
gURLBar.focus();
|
||||
EventUtils.synthesizeKey("VK_RIGHT", {});
|
||||
EventUtils.sendString("#fragment");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_backButton_forwardButton() {
|
||||
yield withTestPage(function*(aBrowser) {
|
||||
// Load a new page in the tab so we can test going back
|
||||
aBrowser.loadURI("https://example.com" + DIRECTORY_PATH + "formless_basic.html?second");
|
||||
yield BrowserTestUtils.browserLoaded(aBrowser, false,
|
||||
"https://example.com" + DIRECTORY_PATH +
|
||||
"formless_basic.html?second");
|
||||
yield fillTestPage(aBrowser);
|
||||
|
||||
let forwardButton = document.getElementById("forward-button");
|
||||
// We need to wait for the forward button transition to complete before we
|
||||
// can click it, so we hook up a listener to wait for it to be ready.
|
||||
let forwardTransitionPromise = BrowserTestUtils.waitForEvent(forwardButton, "transitionend");
|
||||
|
||||
let backPromise = BrowserTestUtils.browserStopped(aBrowser);
|
||||
EventUtils.synthesizeMouseAtCenter(document.getElementById("back-button"), {});
|
||||
yield backPromise;
|
||||
|
||||
// Give a chance for the doorhanger to appear
|
||||
yield new Promise(resolve => SimpleTest.executeSoon(resolve));
|
||||
ok(!getCaptureDoorhanger("any"), "No doorhanger should be present");
|
||||
|
||||
// Now go forward again after filling
|
||||
yield fillTestPage(aBrowser);
|
||||
|
||||
yield forwardTransitionPromise;
|
||||
info("transition done");
|
||||
yield BrowserTestUtils.waitForCondition(() => {
|
||||
return forwardButton.disabled == false;
|
||||
});
|
||||
let forwardPromise = BrowserTestUtils.browserStopped(aBrowser);
|
||||
info("click the forward button");
|
||||
EventUtils.synthesizeMouseAtCenter(forwardButton, {});
|
||||
yield forwardPromise;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_reloadButton() {
|
||||
yield withTestPage(function*(aBrowser) {
|
||||
let reloadButton = document.getElementById("urlbar-reload-button");
|
||||
let loadPromise = BrowserTestUtils.browserLoaded(aBrowser, false,
|
||||
"https://example.com" + DIRECTORY_PATH +
|
||||
"formless_basic.html");
|
||||
|
||||
yield BrowserTestUtils.waitForCondition(() => {
|
||||
return reloadButton.disabled == false;
|
||||
});
|
||||
EventUtils.synthesizeMouseAtCenter(reloadButton, {});
|
||||
yield loadPromise;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_back_keyboard_shortcut() {
|
||||
if (Services.prefs.getIntPref("browser.backspace_action") != 0) {
|
||||
ok(true, "Skipped testing backspace to go back since it's disabled");
|
||||
return;
|
||||
}
|
||||
yield withTestPage(function*(aBrowser) {
|
||||
// Load a new page in the tab so we can test going back
|
||||
aBrowser.loadURI("https://example.com" + DIRECTORY_PATH + "formless_basic.html?second");
|
||||
yield BrowserTestUtils.browserLoaded(aBrowser, false,
|
||||
"https://example.com" + DIRECTORY_PATH +
|
||||
"formless_basic.html?second");
|
||||
yield fillTestPage(aBrowser);
|
||||
|
||||
let backPromise = BrowserTestUtils.browserStopped(aBrowser);
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
yield backPromise;
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/LoginManagerParent.jsm", this);
|
||||
|
||||
const testUrlPath =
|
||||
"://example.com/browser/toolkit/components/passwordmgr/test/browser/";
|
||||
|
||||
/**
|
||||
* Waits for the given number of occurrences of InsecureLoginFormsStateChange
|
||||
* on the given browser element.
|
||||
*/
|
||||
function waitForInsecureLoginFormsStateChange(browser, count) {
|
||||
return BrowserTestUtils.waitForEvent(browser, "InsecureLoginFormsStateChange",
|
||||
false, () => --count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that hasInsecureLoginForms is true for a simple HTTP page and false
|
||||
* for a simple HTTPS page.
|
||||
*/
|
||||
add_task(function* test_simple() {
|
||||
for (let scheme of ["http", "https"]) {
|
||||
let tab = gBrowser.addTab(scheme + testUrlPath + "form_basic.html");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// One event is triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 2),
|
||||
]);
|
||||
|
||||
Assert.equal(LoginManagerParent.hasInsecureLoginForms(browser),
|
||||
scheme == "http");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks that hasInsecureLoginForms is true if a password field is present in
|
||||
* an HTTP page loaded as a subframe of a top-level HTTPS page, when mixed
|
||||
* active content blocking is disabled.
|
||||
*
|
||||
* When the subframe is navigated to an HTTPS page, hasInsecureLoginForms should
|
||||
* be set to false.
|
||||
*
|
||||
* Moving back in history should set hasInsecureLoginForms to true again.
|
||||
*/
|
||||
add_task(function* test_subframe_navigation() {
|
||||
yield new Promise(resolve => SpecialPowers.pushPrefEnv({
|
||||
"set": [["security.mixed_content.block_active_content", false]],
|
||||
}, resolve));
|
||||
|
||||
// Load the page with the subframe in a new tab.
|
||||
let tab = gBrowser.addTab("https" + testUrlPath + "insecure_test.html");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// Two events are triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 3),
|
||||
]);
|
||||
|
||||
Assert.ok(LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
// Navigate the subframe to a secure page.
|
||||
let promiseSubframeReady = Promise.all([
|
||||
BrowserTestUtils.browserLoaded(browser, true),
|
||||
// One event is triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 2),
|
||||
]);
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
content.document.getElementById("test-iframe")
|
||||
.contentDocument.getElementById("test-link").click();
|
||||
});
|
||||
yield promiseSubframeReady;
|
||||
|
||||
Assert.ok(!LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
// Navigate back to the insecure page. We only have to wait for the
|
||||
// InsecureLoginFormsStateChange event that is triggered by pageshow.
|
||||
let promise = waitForInsecureLoginFormsStateChange(browser, 1);
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
content.document.getElementById("test-iframe")
|
||||
.contentWindow.history.back();
|
||||
});
|
||||
yield promise;
|
||||
|
||||
Assert.ok(LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/LoginManagerParent.jsm", this);
|
||||
|
||||
function* registerConverter() {
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", this);
|
||||
|
||||
/**
|
||||
* Converts the "test/content" MIME type, served by the test over HTTP, to an
|
||||
* HTML viewer page containing the "form_basic.html" code. The viewer is
|
||||
* served from a "resource:" URI while keeping the "resource:" principal.
|
||||
*/
|
||||
function TestStreamConverter() {}
|
||||
|
||||
TestStreamConverter.prototype = {
|
||||
classID: Components.ID("{5f01d6ef-c090-45a4-b3e5-940d64713eb7}"),
|
||||
contractID: "@mozilla.org/streamconv;1?from=test/content&to=*/*",
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIRequestObserver,
|
||||
Ci.nsIStreamListener,
|
||||
Ci.nsIStreamConverter,
|
||||
]),
|
||||
|
||||
// nsIStreamConverter
|
||||
convert() {},
|
||||
|
||||
// nsIStreamConverter
|
||||
asyncConvertData(aFromType, aToType, aListener, aCtxt) {
|
||||
this.listener = aListener;
|
||||
},
|
||||
|
||||
// nsIRequestObserver
|
||||
onStartRequest(aRequest, aContext) {
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: "resource://testing-common/form_basic.html",
|
||||
loadUsingSystemPrincipal: true,
|
||||
});
|
||||
channel.originalURI = aRequest.QueryInterface(Ci.nsIChannel).URI;
|
||||
channel.loadGroup = aRequest.loadGroup;
|
||||
channel.owner = Services.scriptSecurityManager
|
||||
.createCodebasePrincipal(channel.URI, {});
|
||||
// In this test, we pass the new channel to the listener but don't fire a
|
||||
// redirect notification, even if it would be required. This keeps the
|
||||
// test code simpler and doesn't impact the principal check we're testing.
|
||||
channel.asyncOpen2(this.listener);
|
||||
},
|
||||
|
||||
// nsIRequestObserver
|
||||
onStopRequest() {},
|
||||
|
||||
// nsIStreamListener
|
||||
onDataAvailable() {},
|
||||
};
|
||||
|
||||
let factory = XPCOMUtils._getFactory(TestStreamConverter);
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(TestStreamConverter.prototype.classID, "",
|
||||
TestStreamConverter.prototype.contractID, factory);
|
||||
this.cleanupFunction = function () {
|
||||
registrar.unregisterFactory(TestStreamConverter.prototype.classID, factory);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the given number of occurrences of InsecureLoginFormsStateChange
|
||||
* on the given browser element.
|
||||
*/
|
||||
function waitForInsecureLoginFormsStateChange(browser, count) {
|
||||
return BrowserTestUtils.waitForEvent(browser, "InsecureLoginFormsStateChange",
|
||||
false, () => --count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that hasInsecureLoginForms is false for a viewer served internally
|
||||
* using a "resource:" URI.
|
||||
*/
|
||||
add_task(function* test_streamConverter() {
|
||||
let originalBrowser = gBrowser.selectedTab.linkedBrowser;
|
||||
|
||||
yield ContentTask.spawn(originalBrowser, null, registerConverter);
|
||||
|
||||
let tab = gBrowser.addTab("http://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/streamConverter_content.sjs",
|
||||
{ relatedBrowser: originalBrowser.linkedBrowser });
|
||||
let browser = tab.linkedBrowser;
|
||||
yield Promise.all([
|
||||
BrowserTestUtils.switchTab(gBrowser, tab),
|
||||
BrowserTestUtils.browserLoaded(browser),
|
||||
// One event is triggered by pageshow and one by DOMFormHasPassword.
|
||||
waitForInsecureLoginFormsStateChange(browser, 2),
|
||||
]);
|
||||
|
||||
Assert.ok(!LoginManagerParent.hasInsecureLoginForms(browser));
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
yield ContentTask.spawn(originalBrowser, null, function* () {
|
||||
this.cleanupFunction();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
const TEST_URL_PATH = "://example.org/browser/toolkit/components/passwordmgr/test/browser/";
|
||||
|
||||
add_task(function* setup() {
|
||||
let login = LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.org",
|
||||
formSubmitURL: "http://example.org",
|
||||
username: "username",
|
||||
password: "password",
|
||||
});
|
||||
Services.logins.addLogin(login);
|
||||
login = LoginTestUtils.testData.formLogin({
|
||||
hostname: "http://example.org",
|
||||
formSubmitURL: "http://another.domain",
|
||||
username: "username",
|
||||
password: "password",
|
||||
});
|
||||
Services.logins.addLogin(login);
|
||||
yield SpecialPowers.pushPrefEnv({ "set": [["signon.autofillForms.http", false]] });
|
||||
});
|
||||
|
||||
add_task(function* test_http_autofill() {
|
||||
for (let scheme of ["http", "https"]) {
|
||||
let tab = yield BrowserTestUtils
|
||||
.openNewForegroundTab(gBrowser, `${scheme}${TEST_URL_PATH}form_basic.html`);
|
||||
|
||||
let [username, password] = yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
|
||||
let doc = content.document;
|
||||
let contentUsername = doc.getElementById("form-basic-username").value;
|
||||
let contentPassword = doc.getElementById("form-basic-password").value;
|
||||
return [contentUsername, contentPassword];
|
||||
});
|
||||
|
||||
is(username, scheme == "http" ? "" : "username", "Username filled correctly");
|
||||
is(password, scheme == "http" ? "" : "password", "Password filled correctly");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_iframe_in_http_autofill() {
|
||||
for (let scheme of ["http", "https"]) {
|
||||
let tab = yield BrowserTestUtils
|
||||
.openNewForegroundTab(gBrowser, `${scheme}${TEST_URL_PATH}form_basic_iframe.html`);
|
||||
|
||||
let [username, password] = yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
|
||||
let doc = content.document;
|
||||
let iframe = doc.getElementById("test-iframe");
|
||||
let contentUsername = iframe.contentWindow.document.getElementById("form-basic-username").value;
|
||||
let contentPassword = iframe.contentWindow.document.getElementById("form-basic-password").value;
|
||||
return [contentUsername, contentPassword];
|
||||
});
|
||||
|
||||
is(username, scheme == "http" ? "" : "username", "Username filled correctly");
|
||||
is(password, scheme == "http" ? "" : "password", "Password filled correctly");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_http_action_autofill() {
|
||||
for (let type of ["insecure", "secure"]) {
|
||||
let tab = yield BrowserTestUtils
|
||||
.openNewForegroundTab(gBrowser, `https${TEST_URL_PATH}form_cross_origin_${type}_action.html`);
|
||||
|
||||
let [username, password] = yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
|
||||
let doc = content.document;
|
||||
let contentUsername = doc.getElementById("form-basic-username").value;
|
||||
let contentPassword = doc.getElementById("form-basic-password").value;
|
||||
return [contentUsername, contentPassword];
|
||||
});
|
||||
|
||||
is(username, type == "insecure" ? "" : "username", "Username filled correctly");
|
||||
is(password, type == "insecure" ? "" : "password", "Password filled correctly");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
"use strict";
|
||||
|
||||
const WARNING_PATTERN = [{
|
||||
key: "INSECURE_FORM_ACTION",
|
||||
msg: 'JavaScript Warning: "Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen."'
|
||||
}, {
|
||||
key: "INSECURE_PAGE",
|
||||
msg: 'JavaScript Warning: "Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen."'
|
||||
}];
|
||||
|
||||
add_task(function* testInsecurePasswordWarning() {
|
||||
let warningPatternHandler;
|
||||
|
||||
function messageHandler(msgObj) {
|
||||
function findWarningPattern(msg) {
|
||||
return WARNING_PATTERN.find(patternPair => {
|
||||
return msg.indexOf(patternPair.msg) !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
let warning = findWarningPattern(msgObj.message);
|
||||
|
||||
// Only handle the insecure password related warning messages.
|
||||
if (warning) {
|
||||
// Prevent any unexpected or redundant matched warning message coming after
|
||||
// the test case is ended.
|
||||
ok(warningPatternHandler, "Invoke a valid warning message handler");
|
||||
warningPatternHandler(warning, msgObj.message);
|
||||
}
|
||||
}
|
||||
Services.console.registerListener(messageHandler);
|
||||
registerCleanupFunction(function() {
|
||||
Services.console.unregisterListener(messageHandler);
|
||||
});
|
||||
|
||||
for (let [origin, testFile, expectWarnings] of [
|
||||
["http://127.0.0.1", "form_basic.html", []],
|
||||
["http://127.0.0.1", "formless_basic.html", []],
|
||||
["http://example.com", "form_basic.html", ["INSECURE_PAGE"]],
|
||||
["http://example.com", "formless_basic.html", ["INSECURE_PAGE"]],
|
||||
["https://example.com", "form_basic.html", []],
|
||||
["https://example.com", "formless_basic.html", []],
|
||||
|
||||
// For a form with customized action link in the same origin.
|
||||
["http://127.0.0.1", "form_same_origin_action.html", []],
|
||||
["http://example.com", "form_same_origin_action.html", ["INSECURE_PAGE"]],
|
||||
["https://example.com", "form_same_origin_action.html", []],
|
||||
|
||||
// For a form with an insecure (http) customized action link.
|
||||
["http://127.0.0.1", "form_cross_origin_insecure_action.html", ["INSECURE_FORM_ACTION"]],
|
||||
["http://example.com", "form_cross_origin_insecure_action.html", ["INSECURE_PAGE"]],
|
||||
["https://example.com", "form_cross_origin_insecure_action.html", ["INSECURE_FORM_ACTION"]],
|
||||
|
||||
// For a form with a secure (https) customized action link.
|
||||
["http://127.0.0.1", "form_cross_origin_secure_action.html", []],
|
||||
["http://example.com", "form_cross_origin_secure_action.html", ["INSECURE_PAGE"]],
|
||||
["https://example.com", "form_cross_origin_secure_action.html", []],
|
||||
]) {
|
||||
let testURL = origin + DIRECTORY_PATH + testFile;
|
||||
let promiseConsoleMessages = new Promise(resolve => {
|
||||
warningPatternHandler = function (warning, originMessage) {
|
||||
ok(warning, "Handling a warning pattern");
|
||||
let fullMessage = `[${warning.msg} {file: "${testURL}" line: 0 column: 0 source: "0"}]`;
|
||||
is(originMessage, fullMessage, "Message full matched:" + originMessage);
|
||||
|
||||
let index = expectWarnings.indexOf(warning.key);
|
||||
isnot(index, -1, "Found warning: " + warning.key + " for URL:" + testURL);
|
||||
if (index !== -1) {
|
||||
// Remove the shown message.
|
||||
expectWarnings.splice(index, 1);
|
||||
}
|
||||
if (expectWarnings.length === 0) {
|
||||
info("All warnings are shown for URL:" + testURL);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: testURL
|
||||
}, function*() {
|
||||
if (expectWarnings.length === 0) {
|
||||
info("All warnings are shown for URL:" + testURL);
|
||||
return Promise.resolve();
|
||||
}
|
||||
return promiseConsoleMessages;
|
||||
});
|
||||
|
||||
// Remove warningPatternHandler to stop handling the matched warning pattern
|
||||
// and the task should not get any warning anymore.
|
||||
warningPatternHandler = null;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
const HOST = "https://example.com";
|
||||
const URL = HOST + "/browser/toolkit/components/passwordmgr/test/browser/form_basic.html";
|
||||
const TIMEOUT_PREF = "signon.masterPasswordReprompt.timeout_ms";
|
||||
|
||||
// Waits for the master password prompt and cancels it.
|
||||
function waitForDialog() {
|
||||
let dialogShown = TestUtils.topicObserved("common-dialog-loaded");
|
||||
return dialogShown.then(function([subject]) {
|
||||
let dialog = subject.Dialog;
|
||||
is(dialog.args.title, "Password Required");
|
||||
dialog.ui.button1.click();
|
||||
});
|
||||
}
|
||||
|
||||
// Test that autocomplete does not trigger a master password prompt
|
||||
// for a certain time after it was cancelled.
|
||||
add_task(function* test_mpAutocompleteTimeout() {
|
||||
let login = LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: "username",
|
||||
password: "password",
|
||||
});
|
||||
Services.logins.addLogin(login);
|
||||
LoginTestUtils.masterPassword.enable();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
LoginTestUtils.masterPassword.disable();
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
// Set master password prompt timeout to 3s.
|
||||
// If this test goes intermittent, you likely have to increase this value.
|
||||
yield SpecialPowers.pushPrefEnv({set: [[TIMEOUT_PREF, 3000]]});
|
||||
|
||||
// Wait for initial master password dialog after opening the tab.
|
||||
let dialogShown = waitForDialog();
|
||||
|
||||
yield BrowserTestUtils.withNewTab(URL, function*(browser) {
|
||||
yield dialogShown;
|
||||
|
||||
yield ContentTask.spawn(browser, null, function*() {
|
||||
// Focus the password field to trigger autocompletion.
|
||||
content.document.getElementById("form-basic-password").focus();
|
||||
});
|
||||
|
||||
// Wait 4s, dialog should not have been shown
|
||||
// (otherwise the code below will not work).
|
||||
yield new Promise((c) => setTimeout(c, 4000));
|
||||
|
||||
dialogShown = waitForDialog();
|
||||
yield ContentTask.spawn(browser, null, function*() {
|
||||
// Re-focus the password field to trigger autocompletion.
|
||||
content.document.getElementById("form-basic-username").focus();
|
||||
content.document.getElementById("form-basic-password").focus();
|
||||
});
|
||||
yield dialogShown;
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Test that the doorhanger notification for password saving is populated with
|
||||
* the correct values in various password capture cases.
|
||||
*/
|
||||
add_task(function* test_save_change() {
|
||||
let testCases = [{
|
||||
username: "username",
|
||||
password: "password",
|
||||
}, {
|
||||
username: "",
|
||||
password: "password",
|
||||
}, {
|
||||
username: "username",
|
||||
oldPassword: "password",
|
||||
password: "newPassword",
|
||||
}, {
|
||||
username: "",
|
||||
oldPassword: "password",
|
||||
password: "newPassword",
|
||||
}];
|
||||
|
||||
for (let { username, oldPassword, password } of testCases) {
|
||||
// Add a login for the origin of the form if testing a change notification.
|
||||
if (oldPassword) {
|
||||
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username,
|
||||
password: oldPassword,
|
||||
}));
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/form_basic.html",
|
||||
}, function* (browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown",
|
||||
(event) => event.target == PopupNotifications.panel);
|
||||
yield ContentTask.spawn(browser, [username, password],
|
||||
function* ([contentUsername, contentPassword]) {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").value = contentUsername;
|
||||
doc.getElementById("form-basic-password").value = contentPassword;
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
yield promiseShown;
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
// Style flush to make sure binding is attached
|
||||
notificationElement.querySelector("#password-notification-password").clientTop;
|
||||
|
||||
// Check the actual content of the popup notification.
|
||||
Assert.equal(notificationElement.querySelector("#password-notification-username")
|
||||
.value, username);
|
||||
Assert.equal(notificationElement.querySelector("#password-notification-password")
|
||||
.value, password);
|
||||
|
||||
// Simulate the action on the notification to request the login to be
|
||||
// saved, and wait for the data to be updated or saved based on the type
|
||||
// of operation we expect.
|
||||
let expectedNotification = oldPassword ? "modifyLogin" : "addLogin";
|
||||
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
|
||||
(_, data) => data == expectedNotification);
|
||||
notificationElement.button.doCommand();
|
||||
let [result] = yield promiseLogin;
|
||||
|
||||
// Check that the values in the database match the expected values.
|
||||
let login = oldPassword ? result.QueryInterface(Ci.nsIArray)
|
||||
.queryElementAt(1, Ci.nsILoginInfo)
|
||||
: result.QueryInterface(Ci.nsILoginInfo);
|
||||
Assert.equal(login.username, username);
|
||||
Assert.equal(login.password, password);
|
||||
});
|
||||
|
||||
// Clean up the database before the next test case is executed.
|
||||
Services.logins.removeAllLogins();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({"set": [
|
||||
["signon.rememberSignons.visibilityToggle", true]
|
||||
]});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that the doorhanger main action button is disabled
|
||||
* when the password field is empty.
|
||||
*
|
||||
* Also checks that submiting an empty password throws an error.
|
||||
*/
|
||||
add_task(function* test_empty_password() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/form_basic.html",
|
||||
}, function* (browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown",
|
||||
(event) => event.target == PopupNotifications.panel);
|
||||
yield ContentTask.spawn(browser, null,
|
||||
function* () {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").value = "username";
|
||||
doc.getElementById("form-basic-password").value = "p";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
yield promiseShown;
|
||||
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
let passwordTextbox = notificationElement.querySelector("#password-notification-password");
|
||||
let toggleCheckbox = notificationElement.querySelector("#password-notification-visibilityToggle");
|
||||
|
||||
// Synthesize input to empty the field
|
||||
passwordTextbox.focus();
|
||||
yield EventUtils.synthesizeKey("VK_RIGHT", {});
|
||||
yield EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
|
||||
let mainActionButton = document.getAnonymousElementByAttribute(notificationElement.button, "anonid", "button");
|
||||
Assert.ok(mainActionButton.disabled, "Main action button is disabled");
|
||||
|
||||
// Makes sure submiting an empty password throws an error
|
||||
Assert.throws(notificationElement.button.doCommand(),
|
||||
"Can't add a login with a null or empty password.",
|
||||
"Should fail for an empty password");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that the doorhanger password field shows plain or * text
|
||||
* when the checkbox is checked.
|
||||
*/
|
||||
add_task(function* test_toggle_password() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/form_basic.html",
|
||||
}, function* (browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown",
|
||||
(event) => event.target == PopupNotifications.panel);
|
||||
yield ContentTask.spawn(browser, null,
|
||||
function* () {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").value = "username";
|
||||
doc.getElementById("form-basic-password").value = "p";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
yield promiseShown;
|
||||
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
let passwordTextbox = notificationElement.querySelector("#password-notification-password");
|
||||
let toggleCheckbox = notificationElement.querySelector("#password-notification-visibilityToggle");
|
||||
|
||||
yield EventUtils.synthesizeMouseAtCenter(toggleCheckbox, {});
|
||||
Assert.ok(toggleCheckbox.checked);
|
||||
Assert.equal(passwordTextbox.type, "", "Password textbox changed to plain text");
|
||||
|
||||
yield EventUtils.synthesizeMouseAtCenter(toggleCheckbox, {});
|
||||
Assert.ok(!toggleCheckbox.checked);
|
||||
Assert.equal(passwordTextbox.type, "password", "Password textbox changed to * text");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that the doorhanger password toggle checkbox is disabled
|
||||
* when the master password is set.
|
||||
*/
|
||||
add_task(function* test_checkbox_disabled_if_has_master_password() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/form_basic.html",
|
||||
}, function* (browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown",
|
||||
(event) => event.target == PopupNotifications.panel);
|
||||
|
||||
LoginTestUtils.masterPassword.enable();
|
||||
|
||||
yield ContentTask.spawn(browser, null, function* () {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").value = "username";
|
||||
doc.getElementById("form-basic-password").value = "p";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
yield promiseShown;
|
||||
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
let passwordTextbox = notificationElement.querySelector("#password-notification-password");
|
||||
let toggleCheckbox = notificationElement.querySelector("#password-notification-visibilityToggle");
|
||||
|
||||
Assert.equal(passwordTextbox.type, "password", "Password textbox should show * text");
|
||||
Assert.ok(toggleCheckbox.getAttribute("hidden"), "checkbox is hidden when master password is set");
|
||||
});
|
||||
|
||||
LoginTestUtils.masterPassword.disable();
|
||||
});
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Test changing the password inside the doorhanger notification for passwords.
|
||||
*
|
||||
* We check the following cases:
|
||||
* - Editing the password of a new login.
|
||||
* - Editing the password of an existing login.
|
||||
* - Changing both username and password to an existing login.
|
||||
* - Changing the username to an existing login.
|
||||
* - Editing username to an empty one and a new password.
|
||||
*
|
||||
* If both the username and password matches an already existing login, we should not
|
||||
* update it's password, but only it's usage timestamp and count.
|
||||
*/
|
||||
add_task(function* test_edit_password() {
|
||||
let testCases = [{
|
||||
usernameInPage: "username",
|
||||
passwordInPage: "password",
|
||||
passwordChangedTo: "newPassword",
|
||||
timesUsed: 1,
|
||||
}, {
|
||||
usernameInPage: "username",
|
||||
usernameInPageExists: true,
|
||||
passwordInPage: "password",
|
||||
passwordInStorage: "oldPassword",
|
||||
passwordChangedTo: "newPassword",
|
||||
timesUsed: 2,
|
||||
}, {
|
||||
usernameInPage: "username",
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
passwordInPage: "password",
|
||||
passwordChangedTo: "newPassword",
|
||||
timesUsed: 2,
|
||||
}, {
|
||||
usernameInPage: "username",
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
passwordInPage: "password",
|
||||
passwordChangedTo: "password",
|
||||
timesUsed: 2,
|
||||
checkPasswordNotUpdated: true,
|
||||
}, {
|
||||
usernameInPage: "newUsername",
|
||||
usernameChangedTo: "",
|
||||
usernameChangedToExists: true,
|
||||
passwordInPage: "password",
|
||||
passwordChangedTo: "newPassword",
|
||||
timesUsed: 2,
|
||||
}];
|
||||
|
||||
for (let testCase of testCases) {
|
||||
info("Test case: " + JSON.stringify(testCase));
|
||||
|
||||
// Create the pre-existing logins when needed.
|
||||
if (testCase.usernameInPageExists) {
|
||||
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: testCase.usernameInPage,
|
||||
password: testCase.passwordInStorage,
|
||||
}));
|
||||
}
|
||||
|
||||
if (testCase.usernameChangedToExists) {
|
||||
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: testCase.usernameChangedTo,
|
||||
password: testCase.passwordChangedTo,
|
||||
}));
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/form_basic.html",
|
||||
}, function* (browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown",
|
||||
(event) => event.target == PopupNotifications.panel);
|
||||
yield ContentTask.spawn(browser, testCase,
|
||||
function* (contentTestCase) {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").value = contentTestCase.usernameInPage;
|
||||
doc.getElementById("form-basic-password").value = contentTestCase.passwordInPage;
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
yield promiseShown;
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
// Style flush to make sure binding is attached
|
||||
notificationElement.querySelector("#password-notification-password").clientTop;
|
||||
|
||||
// Modify the username in the dialog if requested.
|
||||
if (testCase.usernameChangedTo) {
|
||||
notificationElement.querySelector("#password-notification-username")
|
||||
.value = testCase.usernameChangedTo;
|
||||
}
|
||||
|
||||
// Modify the password in the dialog if requested.
|
||||
if (testCase.passwordChangedTo) {
|
||||
notificationElement.querySelector("#password-notification-password")
|
||||
.value = testCase.passwordChangedTo;
|
||||
}
|
||||
|
||||
// We expect a modifyLogin notification if the final username used by the
|
||||
// dialog exists in the logins database, otherwise an addLogin one.
|
||||
let expectModifyLogin = typeof testCase.usernameChangedTo !== "undefined"
|
||||
? testCase.usernameChangedToExists
|
||||
: testCase.usernameInPageExists;
|
||||
|
||||
// Simulate the action on the notification to request the login to be
|
||||
// saved, and wait for the data to be updated or saved based on the type
|
||||
// of operation we expect.
|
||||
let expectedNotification = expectModifyLogin ? "modifyLogin" : "addLogin";
|
||||
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
|
||||
(_, data) => data == expectedNotification);
|
||||
notificationElement.button.doCommand();
|
||||
let [result] = yield promiseLogin;
|
||||
|
||||
// Check that the values in the database match the expected values.
|
||||
let login = expectModifyLogin ? result.QueryInterface(Ci.nsIArray)
|
||||
.queryElementAt(1, Ci.nsILoginInfo)
|
||||
: result.QueryInterface(Ci.nsILoginInfo);
|
||||
|
||||
Assert.equal(login.username, testCase.usernameChangedTo ||
|
||||
testCase.usernameInPage);
|
||||
Assert.equal(login.password, testCase.passwordChangedTo ||
|
||||
testCase.passwordInPage);
|
||||
|
||||
let meta = login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
Assert.equal(meta.timesUsed, testCase.timesUsed);
|
||||
|
||||
// Check that the password was not updated if the user is empty
|
||||
if (testCase.checkPasswordNotUpdated) {
|
||||
Assert.ok(meta.timeLastUsed > meta.timeCreated);
|
||||
Assert.ok(meta.timeCreated == meta.timePasswordChanged);
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up the database before the next test case is executed.
|
||||
Services.logins.removeAllLogins();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* Test changing the username inside the doorhanger notification for passwords.
|
||||
*
|
||||
* We have to test combination of existing and non-existing logins both for
|
||||
* the original one from the webpage and the final one used by the dialog.
|
||||
*
|
||||
* We also check switching to and from empty usernames.
|
||||
*/
|
||||
add_task(function* test_edit_username() {
|
||||
let testCases = [{
|
||||
usernameInPage: "username",
|
||||
usernameChangedTo: "newUsername",
|
||||
}, {
|
||||
usernameInPage: "username",
|
||||
usernameInPageExists: true,
|
||||
usernameChangedTo: "newUsername",
|
||||
}, {
|
||||
usernameInPage: "username",
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
}, {
|
||||
usernameInPage: "username",
|
||||
usernameInPageExists: true,
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
}, {
|
||||
usernameInPage: "",
|
||||
usernameChangedTo: "newUsername",
|
||||
}, {
|
||||
usernameInPage: "newUsername",
|
||||
usernameChangedTo: "",
|
||||
}, {
|
||||
usernameInPage: "",
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
}, {
|
||||
usernameInPage: "newUsername",
|
||||
usernameChangedTo: "",
|
||||
usernameChangedToExists: true,
|
||||
}];
|
||||
|
||||
for (let testCase of testCases) {
|
||||
info("Test case: " + JSON.stringify(testCase));
|
||||
|
||||
// Create the pre-existing logins when needed.
|
||||
if (testCase.usernameInPageExists) {
|
||||
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: testCase.usernameInPage,
|
||||
password: "old password",
|
||||
}));
|
||||
}
|
||||
|
||||
if (testCase.usernameChangedToExists) {
|
||||
Services.logins.addLogin(LoginTestUtils.testData.formLogin({
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com",
|
||||
username: testCase.usernameChangedTo,
|
||||
password: "old password",
|
||||
}));
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "https://example.com/browser/toolkit/components/" +
|
||||
"passwordmgr/test/browser/form_basic.html",
|
||||
}, function* (browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"popupshown",
|
||||
(event) => event.target == PopupNotifications.panel);
|
||||
yield ContentTask.spawn(browser, testCase.usernameInPage,
|
||||
function* (usernameInPage) {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").value = usernameInPage;
|
||||
doc.getElementById("form-basic-password").value = "password";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
yield promiseShown;
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
// Style flush to make sure binding is attached
|
||||
notificationElement.querySelector("#password-notification-password").clientTop;
|
||||
|
||||
// Modify the username in the dialog if requested.
|
||||
if (testCase.usernameChangedTo) {
|
||||
notificationElement.querySelector("#password-notification-username")
|
||||
.value = testCase.usernameChangedTo;
|
||||
}
|
||||
|
||||
// We expect a modifyLogin notification if the final username used by the
|
||||
// dialog exists in the logins database, otherwise an addLogin one.
|
||||
let expectModifyLogin = testCase.usernameChangedTo
|
||||
? testCase.usernameChangedToExists
|
||||
: testCase.usernameInPageExists;
|
||||
|
||||
// Simulate the action on the notification to request the login to be
|
||||
// saved, and wait for the data to be updated or saved based on the type
|
||||
// of operation we expect.
|
||||
let expectedNotification = expectModifyLogin ? "modifyLogin" : "addLogin";
|
||||
let promiseLogin = TestUtils.topicObserved("passwordmgr-storage-changed",
|
||||
(_, data) => data == expectedNotification);
|
||||
notificationElement.button.doCommand();
|
||||
let [result] = yield promiseLogin;
|
||||
|
||||
// Check that the values in the database match the expected values.
|
||||
let login = expectModifyLogin ? result.QueryInterface(Ci.nsIArray)
|
||||
.queryElementAt(1, Ci.nsILoginInfo)
|
||||
: result.QueryInterface(Ci.nsILoginInfo);
|
||||
Assert.equal(login.username, testCase.usernameChangedTo ||
|
||||
testCase.usernameInPage);
|
||||
Assert.equal(login.password, "password");
|
||||
});
|
||||
|
||||
// Clean up the database before the next test case is executed.
|
||||
Services.logins.removeAllLogins();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.logins.removeAllLogins();
|
||||
|
||||
// Add some initial logins
|
||||
let urls = [
|
||||
"http://example.com/",
|
||||
"http://mozilla.org/",
|
||||
"http://spreadfirefox.com/",
|
||||
"https://support.mozilla.org/",
|
||||
"http://hg.mozilla.org/"
|
||||
];
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let logins = [
|
||||
new nsLoginInfo(urls[0], urls[0], null, "", "o hai", "u1", "p1"),
|
||||
new nsLoginInfo(urls[1], urls[1], null, "ehsan", "coded", "u2", "p2"),
|
||||
new nsLoginInfo(urls[2], urls[2], null, "this", "awesome", "u3", "p3"),
|
||||
new nsLoginInfo(urls[3], urls[3], null, "array of", "logins", "u4", "p4"),
|
||||
new nsLoginInfo(urls[4], urls[4], null, "then", "i wrote the test", "u5", "p5")
|
||||
];
|
||||
logins.forEach(login => Services.logins.addLogin(login));
|
||||
|
||||
// Open the password manager dialog
|
||||
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
|
||||
let pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
|
||||
SimpleTest.waitForFocus(doTest, pwmgrdlg);
|
||||
|
||||
// Test if "Copy Username" and "Copy Password" works
|
||||
function doTest() {
|
||||
let doc = pwmgrdlg.document;
|
||||
let selection = doc.getElementById("signonsTree").view.selection;
|
||||
let menuitem = doc.getElementById("context-copyusername");
|
||||
|
||||
function copyField() {
|
||||
info("Select all");
|
||||
selection.selectAll();
|
||||
assertMenuitemEnabled("copyusername", false);
|
||||
assertMenuitemEnabled("editusername", false);
|
||||
assertMenuitemEnabled("copypassword", false);
|
||||
assertMenuitemEnabled("editpassword", false);
|
||||
|
||||
info("Select the first row (with an empty username)");
|
||||
selection.select(0);
|
||||
assertMenuitemEnabled("copyusername", false, "empty username");
|
||||
assertMenuitemEnabled("editusername", true);
|
||||
assertMenuitemEnabled("copypassword", true);
|
||||
assertMenuitemEnabled("editpassword", false, "password column hidden");
|
||||
|
||||
info("Clear the selection");
|
||||
selection.clearSelection();
|
||||
assertMenuitemEnabled("copyusername", false);
|
||||
assertMenuitemEnabled("editusername", false);
|
||||
assertMenuitemEnabled("copypassword", false);
|
||||
assertMenuitemEnabled("editpassword", false);
|
||||
|
||||
info("Select the third row and making the password column visible");
|
||||
selection.select(2);
|
||||
doc.getElementById("passwordCol").hidden = false;
|
||||
assertMenuitemEnabled("copyusername", true);
|
||||
assertMenuitemEnabled("editusername", true);
|
||||
assertMenuitemEnabled("copypassword", true);
|
||||
assertMenuitemEnabled("editpassword", true, "password column visible");
|
||||
menuitem.doCommand();
|
||||
}
|
||||
|
||||
function assertMenuitemEnabled(idSuffix, expected, reason = "") {
|
||||
doc.defaultView.UpdateContextMenu();
|
||||
let actual = !doc.getElementById("context-" + idSuffix).getAttribute("disabled");
|
||||
is(actual, expected, idSuffix + " should be " + (expected ? "enabled" : "disabled") +
|
||||
(reason ? ": " + reason : ""));
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
Services.logins.removeAllLogins();
|
||||
doc.getElementById("passwordCol").hidden = true;
|
||||
finish();
|
||||
});
|
||||
pwmgrdlg.close();
|
||||
}
|
||||
|
||||
function testPassword() {
|
||||
info("Testing Copy Password");
|
||||
waitForClipboard("coded", function copyPassword() {
|
||||
menuitem = doc.getElementById("context-copypassword");
|
||||
menuitem.doCommand();
|
||||
}, cleanUp, cleanUp);
|
||||
}
|
||||
|
||||
info("Testing Copy Username");
|
||||
waitForClipboard("ehsan", copyField, testPassword, testPassword);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
const { ContentTaskUtils } = Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
|
||||
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
|
||||
|
||||
var doc;
|
||||
var pwmgr;
|
||||
var pwmgrdlg;
|
||||
var signonsTree;
|
||||
|
||||
function addLogin(site, username, password) {
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login = new nsLoginInfo(site, site, null, username, password, "u", "p");
|
||||
Services.logins.addLogin(login);
|
||||
}
|
||||
|
||||
function getUsername(row) {
|
||||
return signonsTree.view.getCellText(row, signonsTree.columns.getNamedColumn("userCol"));
|
||||
}
|
||||
|
||||
function getPassword(row) {
|
||||
return signonsTree.view.getCellText(row, signonsTree.columns.getNamedColumn("passwordCol"));
|
||||
}
|
||||
|
||||
function synthesizeDblClickOnCell(aTree, column, row) {
|
||||
let tbo = aTree.treeBoxObject;
|
||||
let rect = tbo.getCoordsForCellItem(row, aTree.columns[column], "text");
|
||||
let x = rect.x + rect.width / 2;
|
||||
let y = rect.y + rect.height / 2;
|
||||
// Simulate the double click.
|
||||
EventUtils.synthesizeMouse(aTree.body, x, y, { clickCount: 2 },
|
||||
aTree.ownerDocument.defaultView);
|
||||
}
|
||||
|
||||
function* togglePasswords() {
|
||||
pwmgrdlg.document.querySelector("#togglePasswords").doCommand();
|
||||
yield new Promise(resolve => waitForFocus(resolve, pwmgrdlg));
|
||||
}
|
||||
|
||||
function* editUsernamePromises(site, oldUsername, newUsername) {
|
||||
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login found");
|
||||
let login = Services.logins.findLogins({}, site, "", "")[0];
|
||||
is(login.username, oldUsername, "Correct username saved");
|
||||
is(getUsername(0), oldUsername, "Correct username shown");
|
||||
synthesizeDblClickOnCell(signonsTree, 1, 0);
|
||||
yield ContentTaskUtils.waitForCondition(() => signonsTree.getAttribute("editing"),
|
||||
"Waiting for editing");
|
||||
|
||||
EventUtils.sendString(newUsername, pwmgrdlg);
|
||||
let signonsIntro = doc.querySelector("#signonsIntro");
|
||||
EventUtils.sendMouseEvent({type: "click"}, signonsIntro, pwmgrdlg);
|
||||
yield ContentTaskUtils.waitForCondition(() => !signonsTree.getAttribute("editing"),
|
||||
"Waiting for editing to stop");
|
||||
|
||||
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login replaced");
|
||||
login = Services.logins.findLogins({}, site, "", "")[0];
|
||||
is(login.username, newUsername, "Correct username updated");
|
||||
is(getUsername(0), newUsername, "Correct username shown after the update");
|
||||
}
|
||||
|
||||
function* editPasswordPromises(site, oldPassword, newPassword) {
|
||||
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login found");
|
||||
let login = Services.logins.findLogins({}, site, "", "")[0];
|
||||
is(login.password, oldPassword, "Correct password saved");
|
||||
is(getPassword(0), oldPassword, "Correct password shown");
|
||||
|
||||
synthesizeDblClickOnCell(signonsTree, 2, 0);
|
||||
yield ContentTaskUtils.waitForCondition(() => signonsTree.getAttribute("editing"),
|
||||
"Waiting for editing");
|
||||
|
||||
EventUtils.sendString(newPassword, pwmgrdlg);
|
||||
let signonsIntro = doc.querySelector("#signonsIntro");
|
||||
EventUtils.sendMouseEvent({type: "click"}, signonsIntro, pwmgrdlg);
|
||||
yield ContentTaskUtils.waitForCondition(() => !signonsTree.getAttribute("editing"),
|
||||
"Waiting for editing to stop");
|
||||
|
||||
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login replaced");
|
||||
login = Services.logins.findLogins({}, site, "", "")[0];
|
||||
is(login.password, newPassword, "Correct password updated");
|
||||
is(getPassword(0), newPassword, "Correct password shown after the update");
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
registerCleanupFunction(function() {
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
Services.logins.removeAllLogins();
|
||||
// Open the password manager dialog.
|
||||
pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
|
||||
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowopened") {
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
SimpleTest.waitForFocus(function() {
|
||||
EventUtils.sendKey("RETURN", win);
|
||||
}, win);
|
||||
} else if (aSubject.location == pwmgrdlg.location && aTopic == "domwindowclosed") {
|
||||
// Unregister ourself.
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
}
|
||||
});
|
||||
|
||||
yield new Promise((resolve) => {
|
||||
SimpleTest.waitForFocus(() => {
|
||||
doc = pwmgrdlg.document;
|
||||
signonsTree = doc.querySelector("#signonsTree");
|
||||
resolve();
|
||||
}, pwmgrdlg);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_edit_multiple_logins() {
|
||||
function* testLoginChange(site, oldUsername, oldPassword, newUsername, newPassword) {
|
||||
addLogin(site, oldUsername, oldPassword);
|
||||
yield* editUsernamePromises(site, oldUsername, newUsername);
|
||||
yield* togglePasswords();
|
||||
yield* editPasswordPromises(site, oldPassword, newPassword);
|
||||
yield* togglePasswords();
|
||||
}
|
||||
|
||||
yield* testLoginChange("http://c.tn/", "userC", "passC", "usernameC", "passwordC");
|
||||
yield* testLoginChange("http://b.tn/", "userB", "passB", "usernameB", "passwordB");
|
||||
yield* testLoginChange("http://a.tn/", "userA", "passA", "usernameA", "passwordA");
|
||||
|
||||
pwmgrdlg.close();
|
||||
});
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
pwmgr.removeAllLogins();
|
||||
|
||||
// add login data
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login = new nsLoginInfo("http://example.com/", "http://example.com/", null,
|
||||
"user", "password", "u1", "p1");
|
||||
pwmgr.addLogin(login);
|
||||
|
||||
// Open the password manager dialog
|
||||
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
|
||||
let pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
|
||||
SimpleTest.waitForFocus(doTest, pwmgrdlg);
|
||||
|
||||
function doTest() {
|
||||
let doc = pwmgrdlg.document;
|
||||
|
||||
let signonsTree = doc.querySelector("#signonsTree");
|
||||
is(signonsTree.view.rowCount, 1, "One entry in the passwords list");
|
||||
|
||||
is(signonsTree.view.getCellText(0, signonsTree.columns.getNamedColumn("siteCol")),
|
||||
"http://example.com/",
|
||||
"Correct website saved");
|
||||
|
||||
is(signonsTree.view.getCellText(0, signonsTree.columns.getNamedColumn("userCol")),
|
||||
"user",
|
||||
"Correct user saved");
|
||||
|
||||
let timeCreatedCol = doc.getElementById("timeCreatedCol");
|
||||
is(timeCreatedCol.getAttribute("hidden"), "true",
|
||||
"Time created column is not displayed");
|
||||
|
||||
|
||||
let timeLastUsedCol = doc.getElementById("timeLastUsedCol");
|
||||
is(timeLastUsedCol.getAttribute("hidden"), "true",
|
||||
"Last Used column is not displayed");
|
||||
|
||||
let timePasswordChangedCol = doc.getElementById("timePasswordChangedCol");
|
||||
is(timePasswordChangedCol.getAttribute("hidden"), "",
|
||||
"Last Changed column is displayed");
|
||||
|
||||
// cleanup
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
if (aSubject.location == pwmgrdlg.location && aTopic == "domwindowclosed") {
|
||||
// unregister ourself
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
|
||||
pwmgr.removeAllLogins();
|
||||
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
pwmgrdlg.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
const LOGIN_HOST = "http://example.com";
|
||||
const LOGIN_COUNT = 5;
|
||||
|
||||
let nsLoginInfo = new Components.Constructor(
|
||||
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
|
||||
let pmDialog = window.openDialog(
|
||||
"chrome://passwordmgr/content/passwordManager.xul",
|
||||
"Toolkit:PasswordManager", "");
|
||||
|
||||
let logins = [];
|
||||
let loginCounter = 0;
|
||||
let loginOrder = null;
|
||||
let modifiedLogin;
|
||||
let testNumber = 0;
|
||||
let testObserver = {
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic == "passwordmgr-dialog-updated") {
|
||||
switch (testNumber) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
is(countLogins(), loginCounter, "Verify login added");
|
||||
ok(getLoginOrder().startsWith(loginOrder), "Verify login order");
|
||||
runNextTest();
|
||||
break;
|
||||
case 6:
|
||||
is(countLogins(), loginCounter, "Verify login count");
|
||||
is(getLoginOrder(), loginOrder, "Verify login order");
|
||||
is(getLoginPassword(), "newpassword0", "Verify login modified");
|
||||
runNextTest();
|
||||
break;
|
||||
case 7:
|
||||
is(countLogins(), loginCounter, "Verify login removed");
|
||||
ok(loginOrder.endsWith(getLoginOrder()), "Verify login order");
|
||||
runNextTest();
|
||||
break;
|
||||
case 8:
|
||||
is(countLogins(), 0, "Verify all logins removed");
|
||||
runNextTest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SimpleTest.waitForFocus(startTest, pmDialog);
|
||||
|
||||
function createLogins() {
|
||||
let login;
|
||||
for (let i = 0; i < LOGIN_COUNT; i++) {
|
||||
login = new nsLoginInfo(LOGIN_HOST + "?n=" + i, LOGIN_HOST + "?n=" + i,
|
||||
null, "user" + i, "password" + i, "u" + i, "p" + i);
|
||||
logins.push(login);
|
||||
}
|
||||
modifiedLogin = new nsLoginInfo(LOGIN_HOST + "?n=0", LOGIN_HOST + "?n=0",
|
||||
null, "user0", "newpassword0", "u0", "p0");
|
||||
is(logins.length, LOGIN_COUNT, "Verify logins created");
|
||||
}
|
||||
|
||||
function countLogins() {
|
||||
let doc = pmDialog.document;
|
||||
let signonsTree = doc.getElementById("signonsTree");
|
||||
return signonsTree.view.rowCount;
|
||||
}
|
||||
|
||||
function getLoginOrder() {
|
||||
let doc = pmDialog.document;
|
||||
let signonsTree = doc.getElementById("signonsTree");
|
||||
let column = signonsTree.columns[0]; // host column
|
||||
let order = [];
|
||||
for (let i = 0; i < signonsTree.view.rowCount; i++) {
|
||||
order.push(signonsTree.view.getCellText(i, column));
|
||||
}
|
||||
return order.join(',');
|
||||
}
|
||||
|
||||
function getLoginPassword() {
|
||||
let doc = pmDialog.document;
|
||||
let loginsTree = doc.getElementById("signonsTree");
|
||||
let column = loginsTree.columns[2]; // password column
|
||||
return loginsTree.view.getCellText(0, column);
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
Services.obs.addObserver(
|
||||
testObserver, "passwordmgr-dialog-updated", false);
|
||||
is(countLogins(), 0, "Verify starts with 0 logins");
|
||||
createLogins();
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
switch (++testNumber) {
|
||||
case 1: // add the logins
|
||||
for (let i = 0; i < logins.length; i++) {
|
||||
loginCounter++;
|
||||
loginOrder = getLoginOrder();
|
||||
Services.logins.addLogin(logins[i]);
|
||||
}
|
||||
break;
|
||||
case 6: // modify a login
|
||||
loginOrder = getLoginOrder();
|
||||
Services.logins.modifyLogin(logins[0], modifiedLogin);
|
||||
break;
|
||||
case 7: // remove a login
|
||||
loginCounter--;
|
||||
loginOrder = getLoginOrder();
|
||||
Services.logins.removeLogin(modifiedLogin);
|
||||
break;
|
||||
case 8: // remove all logins
|
||||
Services.logins.removeAllLogins();
|
||||
break;
|
||||
case 9: // finish
|
||||
Services.obs.removeObserver(
|
||||
testObserver, "passwordmgr-dialog-updated", false);
|
||||
pmDialog.close();
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
pwmgr.removeAllLogins();
|
||||
|
||||
// Add some initial logins
|
||||
let urls = [
|
||||
"http://example.com/",
|
||||
"http://example.org/",
|
||||
"http://mozilla.com/",
|
||||
"http://mozilla.org/",
|
||||
"http://spreadfirefox.com/",
|
||||
"http://planet.mozilla.org/",
|
||||
"https://developer.mozilla.org/",
|
||||
"http://hg.mozilla.org/",
|
||||
"http://dxr.mozilla.org/",
|
||||
"http://feeds.mozilla.org/",
|
||||
];
|
||||
let users = [
|
||||
"user",
|
||||
"username",
|
||||
"ehsan",
|
||||
"ehsan",
|
||||
"john",
|
||||
"what?",
|
||||
"really?",
|
||||
"you sure?",
|
||||
"my user name",
|
||||
"my username",
|
||||
];
|
||||
let pwds = [
|
||||
"password",
|
||||
"password",
|
||||
"mypass",
|
||||
"mypass",
|
||||
"smith",
|
||||
"very secret",
|
||||
"super secret",
|
||||
"absolutely",
|
||||
"mozilla",
|
||||
"mozilla.com",
|
||||
];
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
for (let i = 0; i < 10; i++)
|
||||
pwmgr.addLogin(new nsLoginInfo(urls[i], urls[i], null, users[i], pwds[i],
|
||||
"u" + (i + 1), "p" + (i + 1)));
|
||||
|
||||
// Open the password manager dialog
|
||||
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
|
||||
let pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
|
||||
SimpleTest.waitForFocus(doTest, pwmgrdlg);
|
||||
|
||||
// the meat of the test
|
||||
function doTest() {
|
||||
let doc = pwmgrdlg.document;
|
||||
let win = doc.defaultView;
|
||||
let sTree = doc.getElementById("signonsTree");
|
||||
let filter = doc.getElementById("filter");
|
||||
let siteCol = doc.getElementById("siteCol");
|
||||
let userCol = doc.getElementById("userCol");
|
||||
let passwordCol = doc.getElementById("passwordCol");
|
||||
|
||||
let toggleCalls = 0;
|
||||
function toggleShowPasswords(func) {
|
||||
let toggleButton = doc.getElementById("togglePasswords");
|
||||
let showMode = (toggleCalls++ % 2) == 0;
|
||||
|
||||
// only watch for a confirmation dialog every other time being called
|
||||
if (showMode) {
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowclosed")
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
else if (aTopic == "domwindowopened") {
|
||||
let targetWin = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
SimpleTest.waitForFocus(function() {
|
||||
EventUtils.sendKey("RETURN", targetWin);
|
||||
}, targetWin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Services.obs.addObserver(function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "passwordmgr-password-toggle-complete") {
|
||||
Services.obs.removeObserver(arguments.callee, aTopic);
|
||||
func();
|
||||
}
|
||||
}, "passwordmgr-password-toggle-complete", false);
|
||||
|
||||
EventUtils.synthesizeMouse(toggleButton, 1, 1, {}, win);
|
||||
}
|
||||
|
||||
function clickCol(col) {
|
||||
EventUtils.synthesizeMouse(col, 20, 1, {}, win);
|
||||
setTimeout(runNextTest, 0);
|
||||
}
|
||||
|
||||
function setFilter(string) {
|
||||
filter.value = string;
|
||||
filter.doCommand();
|
||||
setTimeout(runNextTest, 0);
|
||||
}
|
||||
|
||||
function checkSortMarkers(activeCol) {
|
||||
let isOk = true;
|
||||
let col = null;
|
||||
let hasAttr = false;
|
||||
let treecols = activeCol.parentNode;
|
||||
for (let i = 0; i < treecols.childNodes.length; i++) {
|
||||
col = treecols.childNodes[i];
|
||||
if (col.nodeName != "treecol")
|
||||
continue;
|
||||
hasAttr = col.hasAttribute("sortDirection");
|
||||
isOk &= col == activeCol ? hasAttr : !hasAttr;
|
||||
}
|
||||
ok(isOk, "Only " + activeCol.id + " has a sort marker");
|
||||
}
|
||||
|
||||
function checkSortDirection(col, ascending) {
|
||||
checkSortMarkers(col);
|
||||
let direction = ascending ? "ascending" : "descending";
|
||||
is(col.getAttribute("sortDirection"), direction,
|
||||
col.id + ": sort direction is " + direction);
|
||||
}
|
||||
|
||||
function checkColumnEntries(aCol, expectedValues) {
|
||||
let actualValues = getColumnEntries(aCol);
|
||||
is(actualValues.length, expectedValues.length, "Checking length of expected column");
|
||||
for (let i = 0; i < expectedValues.length; i++)
|
||||
is(actualValues[i], expectedValues[i], "Checking column entry #" + i);
|
||||
}
|
||||
|
||||
function getColumnEntries(aCol) {
|
||||
let entries = [];
|
||||
let column = sTree.columns[aCol];
|
||||
let numRows = sTree.view.rowCount;
|
||||
for (let i = 0; i < numRows; i++)
|
||||
entries.push(sTree.view.getCellText(i, column));
|
||||
return entries;
|
||||
}
|
||||
|
||||
let testCounter = 0;
|
||||
let expectedValues;
|
||||
function runNextTest() {
|
||||
switch (testCounter++) {
|
||||
case 0:
|
||||
expectedValues = urls.slice().sort();
|
||||
checkColumnEntries(0, expectedValues);
|
||||
checkSortDirection(siteCol, true);
|
||||
// Toggle sort direction on Host column
|
||||
clickCol(siteCol);
|
||||
break;
|
||||
case 1:
|
||||
expectedValues.reverse();
|
||||
checkColumnEntries(0, expectedValues);
|
||||
checkSortDirection(siteCol, false);
|
||||
// Sort by Username
|
||||
clickCol(userCol);
|
||||
break;
|
||||
case 2:
|
||||
expectedValues = users.slice().sort();
|
||||
checkColumnEntries(1, expectedValues);
|
||||
checkSortDirection(userCol, true);
|
||||
// Sort by Password
|
||||
clickCol(passwordCol);
|
||||
break;
|
||||
case 3:
|
||||
expectedValues = pwds.slice().sort();
|
||||
checkColumnEntries(2, expectedValues);
|
||||
checkSortDirection(passwordCol, true);
|
||||
// Set filter
|
||||
setFilter("moz");
|
||||
break;
|
||||
case 4:
|
||||
expectedValues = [ "absolutely", "mozilla", "mozilla.com",
|
||||
"mypass", "mypass", "super secret",
|
||||
"very secret" ];
|
||||
checkColumnEntries(2, expectedValues);
|
||||
checkSortDirection(passwordCol, true);
|
||||
// Reset filter
|
||||
setFilter("");
|
||||
break;
|
||||
case 5:
|
||||
expectedValues = pwds.slice().sort();
|
||||
checkColumnEntries(2, expectedValues);
|
||||
checkSortDirection(passwordCol, true);
|
||||
// cleanup
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
// unregister ourself
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
|
||||
pwmgr.removeAllLogins();
|
||||
finish();
|
||||
});
|
||||
pwmgrdlg.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle Show Passwords to display Password column, then start tests
|
||||
toggleShowPasswords(runNextTest);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const PROMPT_URL = "chrome://global/content/commonDialog.xul";
|
||||
var { interfaces: Ci } = Components;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
isnot(tab, gBrowser.selectedTab, "New tab shouldn't be selected");
|
||||
|
||||
let listener = {
|
||||
onOpenWindow: function(window) {
|
||||
var domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
waitForFocus(() => {
|
||||
is(domwindow.document.location.href, PROMPT_URL, "Should have seen a prompt window");
|
||||
is(domwindow.args.promptType, "promptUserAndPass", "Should be an authenticate prompt");
|
||||
|
||||
is(gBrowser.selectedTab, tab, "Should have selected the new tab");
|
||||
|
||||
domwindow.document.documentElement.cancelDialog();
|
||||
}, domwindow);
|
||||
},
|
||||
|
||||
onCloseWindow: function() {
|
||||
}
|
||||
};
|
||||
|
||||
Services.wm.addListener(listener);
|
||||
registerCleanupFunction(() => {
|
||||
Services.wm.removeListener(listener);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
tab.linkedBrowser.addEventListener("load", () => {
|
||||
finish();
|
||||
}, true);
|
||||
tab.linkedBrowser.loadURI("http://example.com/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs");
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
pwmgr.removeAllLogins();
|
||||
|
||||
// Add some initial logins
|
||||
let urls = [
|
||||
"http://example.com/",
|
||||
"http://example.org/",
|
||||
"http://mozilla.com/",
|
||||
"http://mozilla.org/",
|
||||
"http://spreadfirefox.com/",
|
||||
"http://planet.mozilla.org/",
|
||||
"https://developer.mozilla.org/",
|
||||
"http://hg.mozilla.org/",
|
||||
"http://dxr.mozilla.org/",
|
||||
"http://feeds.mozilla.org/",
|
||||
];
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let logins = [
|
||||
new nsLoginInfo(urls[0], urls[0], null, "user", "password", "u1", "p1"),
|
||||
new nsLoginInfo(urls[1], urls[1], null, "username", "password", "u2", "p2"),
|
||||
new nsLoginInfo(urls[2], urls[2], null, "ehsan", "mypass", "u3", "p3"),
|
||||
new nsLoginInfo(urls[3], urls[3], null, "ehsan", "mypass", "u4", "p4"),
|
||||
new nsLoginInfo(urls[4], urls[4], null, "john", "smith", "u5", "p5"),
|
||||
new nsLoginInfo(urls[5], urls[5], null, "what?", "very secret", "u6", "p6"),
|
||||
new nsLoginInfo(urls[6], urls[6], null, "really?", "super secret", "u7", "p7"),
|
||||
new nsLoginInfo(urls[7], urls[7], null, "you sure?", "absolutely", "u8", "p8"),
|
||||
new nsLoginInfo(urls[8], urls[8], null, "my user name", "mozilla", "u9", "p9"),
|
||||
new nsLoginInfo(urls[9], urls[9], null, "my username", "mozilla.com", "u10", "p10"),
|
||||
];
|
||||
logins.forEach(login => pwmgr.addLogin(login));
|
||||
|
||||
// Open the password manager dialog
|
||||
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
|
||||
let pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
|
||||
SimpleTest.waitForFocus(doTest, pwmgrdlg);
|
||||
|
||||
// the meat of the test
|
||||
function doTest() {
|
||||
let doc = pwmgrdlg.document;
|
||||
let win = doc.defaultView;
|
||||
let filter = doc.getElementById("filter");
|
||||
let tree = doc.getElementById("signonsTree");
|
||||
let view = tree.view;
|
||||
|
||||
is(filter.value, "", "Filter box should initially be empty");
|
||||
is(view.rowCount, 10, "There should be 10 passwords initially");
|
||||
|
||||
// Prepare a set of tests
|
||||
// filter: the text entered in the filter search box
|
||||
// count: the number of logins which should match the respective filter
|
||||
// count2: the number of logins which should match the respective filter
|
||||
// if the passwords are being shown as well
|
||||
// Note: if a test doesn't have count2 set, count is used instead.
|
||||
let tests = [
|
||||
{filter: "pass", count: 0, count2: 4},
|
||||
{filter: "", count: 10}, // test clearing the filter
|
||||
{filter: "moz", count: 7},
|
||||
{filter: "mozi", count: 7},
|
||||
{filter: "mozil", count: 7},
|
||||
{filter: "mozill", count: 7},
|
||||
{filter: "mozilla", count: 7},
|
||||
{filter: "mozilla.com", count: 1, count2: 2},
|
||||
{filter: "user", count: 4},
|
||||
{filter: "user ", count: 1},
|
||||
{filter: " user", count: 2},
|
||||
{filter: "http", count: 10},
|
||||
{filter: "https", count: 1},
|
||||
{filter: "secret", count: 0, count2: 2},
|
||||
{filter: "secret!", count: 0},
|
||||
];
|
||||
|
||||
let toggleCalls = 0;
|
||||
function toggleShowPasswords(func) {
|
||||
let toggleButton = doc.getElementById("togglePasswords");
|
||||
let showMode = (toggleCalls++ % 2) == 0;
|
||||
|
||||
// only watch for a confirmation dialog every other time being called
|
||||
if (showMode) {
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowclosed")
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
else if (aTopic == "domwindowopened") {
|
||||
let targetWin = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
SimpleTest.waitForFocus(function() {
|
||||
EventUtils.sendKey("RETURN", targetWin);
|
||||
}, targetWin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Services.obs.addObserver(function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "passwordmgr-password-toggle-complete") {
|
||||
Services.obs.removeObserver(arguments.callee, aTopic);
|
||||
func();
|
||||
}
|
||||
}, "passwordmgr-password-toggle-complete", false);
|
||||
|
||||
EventUtils.synthesizeMouse(toggleButton, 1, 1, {}, win);
|
||||
}
|
||||
|
||||
function runTests(mode, endFunction) {
|
||||
let testCounter = 0;
|
||||
|
||||
function setFilter(string) {
|
||||
filter.value = string;
|
||||
filter.doCommand();
|
||||
}
|
||||
|
||||
function runOneTest(testCase) {
|
||||
function tester() {
|
||||
is(view.rowCount, expected, expected + " logins should match '" + testCase.filter + "'");
|
||||
}
|
||||
|
||||
let expected;
|
||||
switch (mode) {
|
||||
case 1: // without showing passwords
|
||||
expected = testCase.count;
|
||||
break;
|
||||
case 2: // showing passwords
|
||||
expected = ("count2" in testCase) ? testCase.count2 : testCase.count;
|
||||
break;
|
||||
case 3: // toggle
|
||||
expected = testCase.count;
|
||||
tester();
|
||||
toggleShowPasswords(function () {
|
||||
expected = ("count2" in testCase) ? testCase.count2 : testCase.count;
|
||||
tester();
|
||||
toggleShowPasswords(proceed);
|
||||
});
|
||||
return;
|
||||
}
|
||||
tester();
|
||||
proceed();
|
||||
}
|
||||
|
||||
function proceed() {
|
||||
// run the next test if necessary or proceed with the tests
|
||||
if (testCounter != tests.length)
|
||||
runNextTest();
|
||||
else
|
||||
endFunction();
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
let testCase = tests[testCounter++];
|
||||
setFilter(testCase.filter);
|
||||
setTimeout(runOneTest, 0, testCase);
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
function step1() {
|
||||
runTests(1, step2);
|
||||
}
|
||||
|
||||
function step2() {
|
||||
toggleShowPasswords(function() {
|
||||
runTests(2, step3);
|
||||
});
|
||||
}
|
||||
|
||||
function step3() {
|
||||
toggleShowPasswords(function() {
|
||||
runTests(3, lastStep);
|
||||
});
|
||||
}
|
||||
|
||||
function lastStep() {
|
||||
// cleanup
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
// unregister ourself
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
|
||||
pwmgr.removeAllLogins();
|
||||
finish();
|
||||
});
|
||||
pwmgrdlg.close();
|
||||
}
|
||||
|
||||
step1();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Test username selection dialog, on password update from a p-only form,
|
||||
* when there are multiple saved logins on the domain.
|
||||
*/
|
||||
|
||||
// Copied from prompt_common.js. TODO: share the code.
|
||||
function getSelectDialogDoc() {
|
||||
// Trudge through all the open windows, until we find the one
|
||||
// that has 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;
|
||||
|
||||
if (childDoc.location.href == "chrome://global/content/selectDialog.xul")
|
||||
return childDoc;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
let login1 = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"notifyu1", "notifyp1", "user", "pass");
|
||||
let login1B = new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"notifyu1B", "notifyp1B", "user", "pass");
|
||||
|
||||
add_task(function* test_changeUPLoginOnPUpdateForm_accept() {
|
||||
info("Select an u+p login from multiple logins, on password update form, and accept.");
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login1B);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_change_p.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
return getSelectDialogDoc();
|
||||
}, "Wait for selection dialog to be accessible.");
|
||||
|
||||
let doc = getSelectDialogDoc();
|
||||
let dialog = doc.getElementsByTagName("dialog")[0];
|
||||
let listbox = doc.getElementById("list");
|
||||
|
||||
is(listbox.selectedIndex, 0, "Checking selected index");
|
||||
is(listbox.itemCount, 2, "Checking selected length");
|
||||
['notifyu1', 'notifyu1B'].forEach((username, i) => {
|
||||
is(listbox.getItemAtIndex(i).label, username, "Check username selection on dialog");
|
||||
});
|
||||
|
||||
dialog.acceptDialog();
|
||||
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
return !getSelectDialogDoc();
|
||||
}, "Wait for selection dialog to disappear.");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 2, "Should have 2 logins");
|
||||
|
||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "pass2", "Check the password changed");
|
||||
is(login.timesUsed, 2, "Check times used");
|
||||
|
||||
login = SpecialPowers.wrap(logins[1]).QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1B", "Check the username unchanged");
|
||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
// cleanup
|
||||
login1.password = "pass2";
|
||||
Services.logins.removeLogin(login1);
|
||||
login1.password = "notifyp1";
|
||||
|
||||
Services.logins.removeLogin(login1B);
|
||||
});
|
||||
|
||||
add_task(function* test_changeUPLoginOnPUpdateForm_cancel() {
|
||||
info("Select an u+p login from multiple logins, on password update form, and cancel.");
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login1B);
|
||||
|
||||
yield testSubmittingLoginForm("subtst_notifications_change_p.html", function*(fieldValues) {
|
||||
is(fieldValues.username, "null", "Checking submitted username");
|
||||
is(fieldValues.password, "pass2", "Checking submitted password");
|
||||
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
return getSelectDialogDoc();
|
||||
}, "Wait for selection dialog to be accessible.");
|
||||
|
||||
let doc = getSelectDialogDoc();
|
||||
let dialog = doc.getElementsByTagName("dialog")[0];
|
||||
let listbox = doc.getElementById("list");
|
||||
|
||||
is(listbox.selectedIndex, 0, "Checking selected index");
|
||||
is(listbox.itemCount, 2, "Checking selected length");
|
||||
['notifyu1', 'notifyu1B'].forEach((username, i) => {
|
||||
is(listbox.getItemAtIndex(i).label, username, "Check username selection on dialog");
|
||||
});
|
||||
|
||||
dialog.cancelDialog();
|
||||
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
return !getSelectDialogDoc();
|
||||
}, "Wait for selection dialog to disappear.");
|
||||
});
|
||||
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 2, "Should have 2 logins");
|
||||
|
||||
let login = SpecialPowers.wrap(logins[0]).QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1", "Check the username unchanged");
|
||||
is(login.password, "notifyp1", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
login = SpecialPowers.wrap(logins[1]).QueryInterface(Ci.nsILoginMetaInfo);
|
||||
is(login.username, "notifyu1B", "Check the username unchanged");
|
||||
is(login.password, "notifyp1B", "Check the password unchanged");
|
||||
is(login.timesUsed, 1, "Check times used");
|
||||
|
||||
// cleanup
|
||||
Services.logins.removeLogin(login1);
|
||||
Services.logins.removeLogin(login1B);
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head>
|
||||
<body onload="document.getElementById('form-basic-username').focus();">
|
||||
<!-- Username field is focused by js onload -->
|
||||
<form id="form-basic">
|
||||
<input id="form-basic-username" name="username">
|
||||
<input id="form-basic-password" name="password" type="password">
|
||||
<input id="form-basic-submit" type="submit">
|
||||
</form>
|
||||
|
||||
</body></html>
|
||||
12
toolkit/components/passwordmgr/test/browser/form_basic.html
Normal file
12
toolkit/components/passwordmgr/test/browser/form_basic.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simplest form with username and password fields. -->
|
||||
<form id="form-basic">
|
||||
<input id="form-basic-username" name="username">
|
||||
<input id="form-basic-password" name="password" type="password">
|
||||
<input id="form-basic-submit" type="submit">
|
||||
</form>
|
||||
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Form in an iframe -->
|
||||
<iframe src="https://example.org/browser/toolkit/components/passwordmgr/test/browser/form_basic.html" id="test-iframe"></iframe>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simplest form with username and password fields. -->
|
||||
<form id="form-basic" action="http://another.domain/custom_action.html">
|
||||
<input id="form-basic-username" name="username">
|
||||
<input id="form-basic-password" name="password" type="password">
|
||||
<input id="form-basic-submit" type="submit">
|
||||
</form>
|
||||
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simplest form with username and password fields. -->
|
||||
<form id="form-basic" action="https://another.domain/custom_action.html">
|
||||
<input id="form-basic-username" name="username">
|
||||
<input id="form-basic-password" name="password" type="password">
|
||||
<input id="form-basic-submit" type="submit">
|
||||
</form>
|
||||
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- Simplest form with username and password fields. -->
|
||||
<form id="form-basic" action="./custom_action.html">
|
||||
<input id="form-basic-username" name="username">
|
||||
<input id="form-basic-password" name="password" type="password">
|
||||
<input id="form-basic-submit" type="submit">
|
||||
</form>
|
||||
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
|
||||
<!-- Simplest form with username and password fields. -->
|
||||
<input id="form-basic-username" name="username">
|
||||
<input id="form-basic-password" name="password" type="password">
|
||||
<input id="form-basic-submit" type="submit">
|
||||
|
||||
<button id="add">Add input[type=password]</button>
|
||||
|
||||
<script>
|
||||
document.getElementById("add").addEventListener("click", function () {
|
||||
var node = document.createElement("input");
|
||||
node.setAttribute("type", "password");
|
||||
document.querySelector("body").appendChild(node);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body></html>
|
||||
137
toolkit/components/passwordmgr/test/browser/head.js
Normal file
137
toolkit/components/passwordmgr/test/browser/head.js
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
const DIRECTORY_PATH = "/browser/toolkit/components/passwordmgr/test/browser/";
|
||||
|
||||
Cu.import("resource://testing-common/LoginTestUtils.jsm", this);
|
||||
Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
|
||||
|
||||
registerCleanupFunction(function* cleanup_removeAllLoginsAndResetRecipes() {
|
||||
Services.logins.removeAllLogins();
|
||||
|
||||
let recipeParent = LoginTestUtils.recipes.getRecipeParent();
|
||||
if (!recipeParent) {
|
||||
// No need to reset the recipes if the recipe module wasn't even loaded.
|
||||
return;
|
||||
}
|
||||
yield recipeParent.then(recipeParentResult => recipeParentResult.reset());
|
||||
});
|
||||
|
||||
/**
|
||||
* Loads a test page in `DIRECTORY_URL` which automatically submits to formsubmit.sjs and returns a
|
||||
* promise resolving with the field values when the optional `aTaskFn` is done.
|
||||
*
|
||||
* @param {String} aPageFile - test page file name which auto-submits to formsubmit.sjs
|
||||
* @param {Function} aTaskFn - task which can be run before the tab closes.
|
||||
* @param {String} [aOrigin="http://example.com"] - origin of the server to use
|
||||
* to load `aPageFile`.
|
||||
*/
|
||||
function testSubmittingLoginForm(aPageFile, aTaskFn, aOrigin = "http://example.com") {
|
||||
return BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: aOrigin + DIRECTORY_PATH + aPageFile,
|
||||
}, function*(browser) {
|
||||
ok(true, "loaded " + aPageFile);
|
||||
let fieldValues = yield ContentTask.spawn(browser, undefined, function*() {
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
return content.location.pathname.endsWith("/formsubmit.sjs") &&
|
||||
content.document.readyState == "complete";
|
||||
}, "Wait for form submission load (formsubmit.sjs)");
|
||||
let username = content.document.getElementById("user").textContent;
|
||||
let password = content.document.getElementById("pass").textContent;
|
||||
return {
|
||||
username,
|
||||
password,
|
||||
};
|
||||
});
|
||||
ok(true, "form submission loaded");
|
||||
if (aTaskFn) {
|
||||
yield* aTaskFn(fieldValues);
|
||||
}
|
||||
return fieldValues;
|
||||
});
|
||||
}
|
||||
|
||||
function checkOnlyLoginWasUsedTwice({ justChanged }) {
|
||||
// Check to make sure we updated the timestamps and use count on the
|
||||
// existing login that was submitted for the test.
|
||||
let logins = Services.logins.getAllLogins();
|
||||
is(logins.length, 1, "Should only have 1 login");
|
||||
ok(logins[0] instanceof Ci.nsILoginMetaInfo, "metainfo QI");
|
||||
is(logins[0].timesUsed, 2, "check .timesUsed for existing login submission");
|
||||
ok(logins[0].timeCreated < logins[0].timeLastUsed, "timeLastUsed bumped");
|
||||
if (justChanged) {
|
||||
is(logins[0].timeLastUsed, logins[0].timePasswordChanged, "timeLastUsed == timePasswordChanged");
|
||||
} else {
|
||||
is(logins[0].timeCreated, logins[0].timePasswordChanged, "timeChanged not updated");
|
||||
}
|
||||
}
|
||||
|
||||
// Begin popup notification (doorhanger) functions //
|
||||
|
||||
const REMEMBER_BUTTON = 0;
|
||||
const NEVER_BUTTON = 1;
|
||||
|
||||
const CHANGE_BUTTON = 0;
|
||||
const DONT_CHANGE_BUTTON = 1;
|
||||
|
||||
/**
|
||||
* Checks if we have a password capture popup notification
|
||||
* of the right type and with the right label.
|
||||
*
|
||||
* @param {String} aKind The desired `passwordNotificationType`
|
||||
* @param {Object} [popupNotifications = PopupNotifications]
|
||||
* @return the found password popup notification.
|
||||
*/
|
||||
function getCaptureDoorhanger(aKind, popupNotifications = PopupNotifications) {
|
||||
ok(true, "Looking for " + aKind + " popup notification");
|
||||
let notification = popupNotifications.getNotification("password");
|
||||
if (notification) {
|
||||
is(notification.options.passwordNotificationType, aKind, "Notification type matches.");
|
||||
if (aKind == "password-change") {
|
||||
is(notification.mainAction.label, "Update", "Main action label matches update doorhanger.");
|
||||
} else if (aKind == "password-save") {
|
||||
is(notification.mainAction.label, "Remember", "Main action label matches save doorhanger.");
|
||||
}
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the specified popup notification button.
|
||||
*
|
||||
* @param {Element} aPopup Popup Notification element
|
||||
* @param {Number} aButtonIndex Number indicating which button to click.
|
||||
* See the constants in this file.
|
||||
*/
|
||||
function clickDoorhangerButton(aPopup, aButtonIndex) {
|
||||
ok(true, "Looking for action at index " + aButtonIndex);
|
||||
|
||||
let notifications = aPopup.owner.panel.childNodes;
|
||||
ok(notifications.length > 0, "at least one notification displayed");
|
||||
ok(true, notifications.length + " notification(s)");
|
||||
let notification = notifications[0];
|
||||
|
||||
if (aButtonIndex == 0) {
|
||||
ok(true, "Triggering main action");
|
||||
notification.button.doCommand();
|
||||
} else if (aButtonIndex <= aPopup.secondaryActions.length) {
|
||||
ok(true, "Triggering secondary action " + aButtonIndex);
|
||||
notification.childNodes[aButtonIndex].doCommand();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the doorhanger's username and password.
|
||||
*
|
||||
* @param {String} username The username.
|
||||
* @param {String} password The password.
|
||||
*/
|
||||
function* checkDoorhangerUsernamePassword(username, password) {
|
||||
yield BrowserTestUtils.waitForCondition(() => {
|
||||
return document.getElementById("password-notification-username").value == username;
|
||||
}, "Wait for nsLoginManagerPrompter writeDataToUI()");
|
||||
is(document.getElementById("password-notification-username").value, username,
|
||||
"Check doorhanger username");
|
||||
is(document.getElementById("password-notification-password").value, password,
|
||||
"Check doorhanger password");
|
||||
}
|
||||
|
||||
// End popup notification (doorhanger) functions //
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<!-- This frame is initially loaded over HTTP. -->
|
||||
<iframe id="test-iframe"
|
||||
src="http://example.org/browser/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html"/>
|
||||
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<form>
|
||||
<input name="password" type="password">
|
||||
</form>
|
||||
|
||||
<!-- Link to reload this page over HTTPS. -->
|
||||
<a id="test-link"
|
||||
href="https://example.org/browser/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html">HTTPS</a>
|
||||
|
||||
</body></html>
|
||||
129
toolkit/components/passwordmgr/test/browser/multiple_forms.html
Normal file
129
toolkit/components/passwordmgr/test/browser/multiple_forms.html
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Password only form">
|
||||
<input id='test-password-1' type='password' name='pname' value=''>
|
||||
<input type='submit'>Submit</input>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Username only form">
|
||||
<input id='test-username-1' type='text' name='uname' value=''>
|
||||
<input type='submit'>Submit</input>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password blank form">
|
||||
<input id='test-username-2' type='text' name='uname' value=''>
|
||||
<input id='test-password-2' type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password form, prefilled username">
|
||||
<input id='test-username-3' type='text' name='uname' value='testuser'>
|
||||
<input id='test-password-3' type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password form, prefilled username and password">
|
||||
<input id='test-username-4' type='text' name='uname' value='testuser'>
|
||||
<input id='test-password-4' type='password' name='pname' value='testpass'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="One username and two passwords empty form">
|
||||
<input id='test-username-5' type='text' name='uname'>
|
||||
<input id='test-password-5' type='password' name='pname'>
|
||||
<input id='test-password2-5' type='password' name='pname2'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="One username and two passwords form, fields prefiled">
|
||||
<input id='test-username-6' type='text' name='uname' value="testuser">
|
||||
<input id='test-password-6' type='password' name='pname' value="testpass">
|
||||
<input id='test-password2-6' type='password' name='pname2' value="testpass">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="test-form"
|
||||
description="Username and password fields with no form">
|
||||
<input id='test-username-7' type='text' name='uname' value="testuser">
|
||||
<input id='test-password-7' type='password' name='pname' value="testpass">
|
||||
</div>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password blank form, with disabled password">
|
||||
<input id='test-username-8' type='text' name='uname' value=''>
|
||||
<input id='test-password-8' type='password' name='pname' value='' disabled>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password blank form, with disabled username">
|
||||
<input id='test-username-9' type='text' name='uname' value='' disabled>
|
||||
<input id='test-password-9' type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password blank form, with readonly password">
|
||||
<input id='test-username-10' type='text' name='uname' value=''>
|
||||
<input id='test-password-10' type='password' name='pname' value='' readonly>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Simple username and password blank form, with readonly username">
|
||||
<input id='test-username-11' type='text' name='uname' value='' readonly>
|
||||
<input id='test-password-11' type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Two username and one passwords form, fields prefiled">
|
||||
<input id='test-username-12' type='text' name='uname' value="testuser">
|
||||
<input id='test-username2-12' type='text' name='uname2' value="testuser">
|
||||
<input id='test-password-12' type='password' name='pname' value="testpass">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<form class="test-form"
|
||||
description="Two username and one passwords form, one disabled username field">
|
||||
<input id='test-username-13' type='text' name='uname'>
|
||||
<input id='test-username2-13' type='text' name='uname2' disabled>
|
||||
<input id='test-password-13' type='password' name='pname'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="test-form"
|
||||
description="Second username and password fields with no form">
|
||||
<input id='test-username-14' type='text' name='uname'>
|
||||
<input id='test-password-14' type='password' name='pname' expectedFail>
|
||||
</div>
|
||||
|
||||
<!-- Form in an iframe -->
|
||||
<iframe src="https://example.org/browser/toolkit/components/passwordmgr/test/browser/form_basic.html" id="test-iframe"></iframe>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setHeader("Content-Type", "test/content", false);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications - Basic 1un 1pw</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 1</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 10</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications - Popup Windows</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 11 (popup windows)</h2>
|
||||
<script>
|
||||
|
||||
// Ignore the '?' and split on |
|
||||
[username, password, features, autoClose] = window.location.search.substring(1).split('|');
|
||||
|
||||
var url = "subtst_notifications_11_popup.html?" + username + "|" + password;
|
||||
var popupWin = window.open(url, "subtst_11", features);
|
||||
|
||||
// Popup window will call this function on form submission.
|
||||
function formSubmitted() {
|
||||
if (autoClose)
|
||||
popupWin.close();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 11</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
// Get the password from the query string (exclude '?').
|
||||
[username, password] = window.location.search.substring(1).split('|');
|
||||
userField.value = username;
|
||||
passField.value = password;
|
||||
form.submit();
|
||||
window.opener.formSubmitted();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications - autocomplete=off on the username field</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 2</h2>
|
||||
(username autocomplete=off)
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user" autocomplete="off">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications with 2 password fields and no username</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 24</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="pass1" name="pass1" type="password" value="staticpw">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
pass.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var pass = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications with 2 password fields and 1 username field and one other text field before the first password field</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>1 username field followed by a text field followed by 2 username fields</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user" value="staticpw">
|
||||
<input id="city" name="city" value="city">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<input id="pin" name="pin" type="password" value="static-pin">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications - autocomplete=off on the password field</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 3</h2>
|
||||
(password autocomplete=off)
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user">
|
||||
<input id="pass" name="pass" type="password" autocomplete="off">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" >
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 4</h2>
|
||||
(form autocomplete=off)
|
||||
<form id="form" action="formsubmit.sjs" autocomplete="off">
|
||||
<input id="user" name="user">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications - Form with only a username field</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 5</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 6</h2>
|
||||
(password-only form)
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 8</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "pass2";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 9</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "";
|
||||
passField.value = "pass2";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Change password</h2>
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="pass_current" name="pass_current" type="password" value="notifyp1">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<input id="pass_confirm" name="pass_confirm" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
passField.value = "pass2";
|
||||
passConfirmField.value = "pass2";
|
||||
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
var passConfirmField = document.getElementById("pass_confirm");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
13
toolkit/components/passwordmgr/test/chrome/chrome.ini
Normal file
13
toolkit/components/passwordmgr/test/chrome/chrome.ini
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[DEFAULT]
|
||||
skip-if = os == 'android'
|
||||
|
||||
[test_privbrowsing_perwindowpb.html]
|
||||
skip-if = true # Bug 1173337
|
||||
support-files =
|
||||
../formsubmit.sjs
|
||||
notification_common.js
|
||||
privbrowsing_perwindowpb_iframe.html
|
||||
subtst_privbrowsing_1.html
|
||||
subtst_privbrowsing_2.html
|
||||
subtst_privbrowsing_3.html
|
||||
subtst_privbrowsing_4.html
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Initialization: for each test, remove any prior notifications.
|
||||
*/
|
||||
function cleanUpPopupNotifications() {
|
||||
var container = getPopupNotifications(window.top);
|
||||
var notes = container._currentNotifications;
|
||||
info(true, "Removing " + notes.length + " popup notifications.");
|
||||
for (var i = notes.length - 1; i >= 0; i--) {
|
||||
notes[i].remove();
|
||||
}
|
||||
}
|
||||
cleanUpPopupNotifications();
|
||||
|
||||
/*
|
||||
* getPopupNotifications
|
||||
*
|
||||
* Fetches the popup notification for the specified window.
|
||||
*/
|
||||
function getPopupNotifications(aWindow) {
|
||||
var Ci = SpecialPowers.Ci;
|
||||
var Cc = SpecialPowers.Cc;
|
||||
ok(Ci != null, "Access Ci");
|
||||
ok(Cc != null, "Access Cc");
|
||||
|
||||
var chromeWin = SpecialPowers.wrap(aWindow)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler.ownerDocument.defaultView;
|
||||
|
||||
var popupNotifications = chromeWin.PopupNotifications;
|
||||
return popupNotifications;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if we have a password popup notification
|
||||
* of the right type and with the right label.
|
||||
*
|
||||
* @deprecated Write a browser-chrome test instead and use the fork of this method there.
|
||||
* @returns the found password popup notification.
|
||||
*/
|
||||
function getPopup(aPopupNote, aKind) {
|
||||
ok(true, "Looking for " + aKind + " popup notification");
|
||||
var notification = aPopupNote.getNotification("password");
|
||||
if (notification) {
|
||||
is(notification.options.passwordNotificationType, aKind, "Notification type matches.");
|
||||
if (aKind == "password-change") {
|
||||
is(notification.mainAction.label, "Update", "Main action label matches update doorhanger.");
|
||||
} else if (aKind == "password-save") {
|
||||
is(notification.mainAction.label, "Remember", "Main action label matches save doorhanger.");
|
||||
}
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated - Use a browser chrome test instead.
|
||||
*
|
||||
* Clicks the specified popup notification button.
|
||||
*/
|
||||
function clickPopupButton(aPopup, aButtonIndex) {
|
||||
ok(true, "Looking for action at index " + aButtonIndex);
|
||||
|
||||
var notifications = SpecialPowers.wrap(aPopup.owner).panel.childNodes;
|
||||
ok(notifications.length > 0, "at least one notification displayed");
|
||||
ok(true, notifications.length + " notifications");
|
||||
var notification = notifications[0];
|
||||
|
||||
if (aButtonIndex == 0) {
|
||||
ok(true, "Triggering main action");
|
||||
notification.button.doCommand();
|
||||
} else if (aButtonIndex <= aPopup.secondaryActions.length) {
|
||||
var index = aButtonIndex;
|
||||
ok(true, "Triggering secondary action " + index);
|
||||
notification.childNodes[index].doCommand();
|
||||
}
|
||||
}
|
||||
|
||||
const kRememberButton = 0;
|
||||
const kNeverButton = 1;
|
||||
|
||||
const kChangeButton = 0;
|
||||
const kDontChangeButton = 1;
|
||||
|
||||
function dumpNotifications() {
|
||||
try {
|
||||
// PopupNotifications
|
||||
var container = getPopupNotifications(window.top);
|
||||
ok(true, "is popup panel open? " + container.isPanelOpen);
|
||||
var notes = container._currentNotifications;
|
||||
ok(true, "Found " + notes.length + " popup notifications.");
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
ok(true, "#" + i + ": " + notes[i].id);
|
||||
}
|
||||
|
||||
// Notification bars
|
||||
var chromeWin = SpecialPowers.wrap(window.top)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler.ownerDocument.defaultView;
|
||||
var nb = chromeWin.getNotificationBox(window.top);
|
||||
notes = nb.allNotifications;
|
||||
ok(true, "Found " + notes.length + " notification bars.");
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
ok(true, "#" + i + ": " + notes[i].getAttribute("value"));
|
||||
}
|
||||
} catch (e) { todo(false, "WOAH! " + e); }
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="iframe"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications (private browsing)</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 1</h2>
|
||||
<!--
|
||||
Make sure that the password-save notification appears outside of
|
||||
the private mode, but not inside it.
|
||||
-->
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user" type="text">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
var passField = document.getElementById("pass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications (private browsing)</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 2</h2>
|
||||
<!--
|
||||
Make sure that the password-change notification appears outside of
|
||||
the private mode, but not inside it.
|
||||
-->
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<input id="newpass" name="newpass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
passField.value = "notifyp1";
|
||||
passField2.value = "notifyp2";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
var passField = document.getElementById("pass");
|
||||
var passField2 = document.getElementById("newpass");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications (private browsing)</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 3</h2>
|
||||
<!--
|
||||
Make sure that the user/pass fields are auto-filled outside of
|
||||
the private mode, but not inside it.
|
||||
-->
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user" type="text">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
form.submit();
|
||||
}
|
||||
|
||||
var form = document.getElementById("form");
|
||||
window.addEventListener('message', () => { submitForm(); });
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Subtest for Login Manager notifications (private browsing)</title>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Subtest 4</h2>
|
||||
<!--
|
||||
Make sure that the user/pass fields have manual filling enabled
|
||||
in private mode.
|
||||
-->
|
||||
<form id="form" action="formsubmit.sjs">
|
||||
<input id="user" name="user" type="text">
|
||||
<input id="pass" name="pass" type="password">
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function startAutocomplete() {
|
||||
userField.focus();
|
||||
doKey("down");
|
||||
setTimeout(submitForm, 100);
|
||||
}
|
||||
|
||||
function submitForm() {
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
setTimeout(function() { form.submit(); }, 100);
|
||||
}
|
||||
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
|
||||
window.addEventListener('message', () => { startAutocomplete(); });
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=248970
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Private Browsing</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="notification_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=248970">Mozilla Bug 248970</a>
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 248970 **/
|
||||
// based on test_notifications.html
|
||||
|
||||
const Ci = SpecialPowers.Ci;
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Cr = SpecialPowers.Cr;
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var testpath = "/chrome/toolkit/components/passwordmgr/test/chrome/";
|
||||
var prefix = "http://test2.example.com" + testpath;
|
||||
var subtests = [
|
||||
"subtst_privbrowsing_1.html", // 1
|
||||
"subtst_privbrowsing_1.html", // 2
|
||||
"subtst_privbrowsing_1.html", // 3
|
||||
"subtst_privbrowsing_2.html", // 4
|
||||
"subtst_privbrowsing_2.html", // 5
|
||||
"subtst_privbrowsing_2.html", // 6
|
||||
"subtst_privbrowsing_3.html", // 7
|
||||
"subtst_privbrowsing_3.html", // 8
|
||||
"subtst_privbrowsing_4.html", // 9
|
||||
"subtst_privbrowsing_3.html" // 10
|
||||
];
|
||||
var observer;
|
||||
|
||||
var testNum = 0;
|
||||
function loadNextTest() {
|
||||
// run the initialization code for each test
|
||||
switch (++ testNum) {
|
||||
case 1:
|
||||
popupNotifications = normalWindowPopupNotifications;
|
||||
iframe = normalWindowIframe;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
popupNotifications = privateWindowPopupNotifications;
|
||||
iframe = privateWindowIframe;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
popupNotifications = normalWindowPopupNotifications;
|
||||
iframe = normalWindowIframe;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
pwmgr.addLogin(login);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
popupNotifications = privateWindowPopupNotifications;
|
||||
iframe = privateWindowIframe;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
popupNotifications = normalWindowPopupNotifications;
|
||||
iframe = normalWindowIframe;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
pwmgr.addLogin(login);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
popupNotifications = privateWindowPopupNotifications;
|
||||
iframe = privateWindowIframe;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
break;
|
||||
|
||||
case 10:
|
||||
popupNotifications = normalWindowPopupNotifications;
|
||||
iframe = normalWindowIframe;
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected call to loadNextTest for test #" + testNum);
|
||||
}
|
||||
|
||||
if (testNum === 7) {
|
||||
observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
|
||||
SimpleTest.executeSoon(() => { iframe.contentWindow.postMessage("go", "*"); });
|
||||
});
|
||||
SpecialPowers.addObserver(observer, "passwordmgr-processed-form", false);
|
||||
}
|
||||
|
||||
ok(true, "Starting test #" + testNum);
|
||||
iframe.src = prefix + subtests[testNum - 1];
|
||||
}
|
||||
|
||||
function checkTest() {
|
||||
var popup;
|
||||
var gotUser;
|
||||
var gotPass;
|
||||
|
||||
switch (testNum) {
|
||||
case 1:
|
||||
// run outside of private mode, popup notification should appear
|
||||
popup = getPopup(popupNotifications, "password-save");
|
||||
ok(popup, "got popup notification");
|
||||
popup.remove();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// run inside of private mode, popup notification should not appear
|
||||
popup = getPopup(popupNotifications, "password-save");
|
||||
ok(!popup, "checking for no popup notification");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// run outside of private mode, popup notification should appear
|
||||
popup = getPopup(popupNotifications, "password-save");
|
||||
ok(popup, "got popup notification");
|
||||
popup.remove();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// run outside of private mode, popup notification should appear
|
||||
popup = getPopup(popupNotifications, "password-change");
|
||||
ok(popup, "got popup notification");
|
||||
popup.remove();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// run inside of private mode, popup notification should not appear
|
||||
popup = getPopup(popupNotifications, "password-change");
|
||||
ok(!popup, "checking for no popup notification");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// run outside of private mode, popup notification should appear
|
||||
popup = getPopup(popupNotifications, "password-change");
|
||||
ok(popup, "got popup notification");
|
||||
popup.remove();
|
||||
pwmgr.removeLogin(login);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// verify that the user/pass pair was autofilled
|
||||
gotUser = iframe.contentDocument.getElementById("user").textContent;
|
||||
gotPass = iframe.contentDocument.getElementById("pass").textContent;
|
||||
is(gotUser, "notifyu1", "Checking submitted username");
|
||||
is(gotPass, "notifyp1", "Checking submitted password");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// verify that the user/pass pair was not autofilled
|
||||
gotUser = iframe.contentDocument.getElementById("user").textContent;
|
||||
gotPass = iframe.contentDocument.getElementById("pass").textContent;
|
||||
is(gotUser, "", "Checking submitted username");
|
||||
is(gotPass, "", "Checking submitted password");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// verify that the user/pass pair was available for autocomplete
|
||||
gotUser = iframe.contentDocument.getElementById("user").textContent;
|
||||
gotPass = iframe.contentDocument.getElementById("pass").textContent;
|
||||
is(gotUser, "notifyu1", "Checking submitted username");
|
||||
is(gotPass, "notifyp1", "Checking submitted password");
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// verify that the user/pass pair was autofilled
|
||||
gotUser = iframe.contentDocument.getElementById("user").textContent;
|
||||
gotPass = iframe.contentDocument.getElementById("pass").textContent;
|
||||
is(gotUser, "notifyu1", "Checking submitted username");
|
||||
is(gotPass, "notifyp1", "Checking submitted password");
|
||||
pwmgr.removeLogin(login);
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected call to checkTest for test #" + testNum);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
var contentPage = "http://mochi.test:8888/chrome/toolkit/components/passwordmgr/test/chrome/privbrowsing_perwindowpb_iframe.html";
|
||||
var testWindows = [];
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function obs(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(obs, aTopic);
|
||||
setTimeout(aCallback, 0);
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
|
||||
function testOnWindow(aIsPrivate, aCallback) {
|
||||
var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
whenDelayedStartupFinished(win, function() {
|
||||
win.addEventListener("DOMContentLoaded", function onInnerLoad() {
|
||||
if (win.content.location.href != contentPage) {
|
||||
win.gBrowser.loadURI(contentPage);
|
||||
return;
|
||||
}
|
||||
win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
|
||||
|
||||
win.content.addEventListener('load', function innerLoad2() {
|
||||
win.content.removeEventListener('load', innerLoad2, false);
|
||||
testWindows.push(win);
|
||||
SimpleTest.executeSoon(function() { aCallback(win); });
|
||||
}, false, true);
|
||||
}, true);
|
||||
SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); });
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
var ignoreLoad = false;
|
||||
function handleLoad(aEvent) {
|
||||
// ignore every other load event ... We get one for loading the subtest (which
|
||||
// we want to ignore), and another when the subtest's form submits itself
|
||||
// (which we want to handle, to start the next test).
|
||||
ignoreLoad = !ignoreLoad;
|
||||
if (ignoreLoad) {
|
||||
ok(true, "Ignoring load of subtest #" + testNum);
|
||||
return;
|
||||
}
|
||||
ok(true, "Processing submission of subtest #" + testNum);
|
||||
|
||||
checkTest();
|
||||
|
||||
if (testNum < subtests.length) {
|
||||
loadNextTest();
|
||||
} else {
|
||||
ok(true, "private browsing notification tests finished.");
|
||||
|
||||
testWindows.forEach(function(aWin) {
|
||||
aWin.close();
|
||||
});
|
||||
|
||||
SpecialPowers.removeObserver(observer, "passwordmgr-processed-form");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
ok(pwmgr != null, "Access pwmgr");
|
||||
|
||||
// We need to make sure no logins have been stored by previous tests
|
||||
// for forms in |url|, otherwise the change password notification
|
||||
// would turn into a prompt, and the test will fail.
|
||||
var url = "http://test2.example.com";
|
||||
is(pwmgr.countLogins(url, "", null), 0, "No logins should be stored for " + url);
|
||||
|
||||
var nsLoginInfo = new SpecialPowers.wrap(SpecialPowers.Components).Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
var login = new nsLoginInfo(url, url, null, "notifyu1", "notifyp1", "user", "pass");
|
||||
|
||||
var normalWindow;
|
||||
var privateWindow;
|
||||
|
||||
var iframe;
|
||||
var normalWindowIframe;
|
||||
var privateWindowIframe;
|
||||
|
||||
var popupNotifications;
|
||||
var normalWindowPopupNotifications;
|
||||
var privateWindowPopupNotifications;
|
||||
|
||||
testOnWindow(false, function(aWin) {
|
||||
var selectedBrowser = aWin.gBrowser.selectedBrowser;
|
||||
normalWindowIframe = selectedBrowser.contentDocument.getElementById("iframe");
|
||||
normalWindowIframe.onload = handleLoad;
|
||||
selectedBrowser.focus();
|
||||
|
||||
normalWindowPopupNotifications = getPopupNotifications(selectedBrowser.contentWindow.top);
|
||||
ok(normalWindowPopupNotifications, "Got popupNotifications in normal window");
|
||||
// ignore the first load for this window;
|
||||
ignoreLoad = false;
|
||||
|
||||
testOnWindow(true, function(aPrivateWin) {
|
||||
selectedBrowser = aPrivateWin.gBrowser.selectedBrowser;
|
||||
privateWindowIframe = selectedBrowser.contentDocument.getElementById("iframe");
|
||||
privateWindowIframe.onload = handleLoad;
|
||||
selectedBrowser.focus();
|
||||
|
||||
privateWindowPopupNotifications = getPopupNotifications(selectedBrowser.contentWindow.top);
|
||||
ok(privateWindowPopupNotifications, "Got popupNotifications in private window");
|
||||
// ignore the first load for this window;
|
||||
ignoreLoad = false;
|
||||
|
||||
SimpleTest.executeSoon(loadNextTest);
|
||||
});
|
||||
});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
11
toolkit/components/passwordmgr/test/chrome_timeout.js
Normal file
11
toolkit/components/passwordmgr/test/chrome_timeout.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
addMessageListener('setTimeout', msg => {
|
||||
let timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
|
||||
timer.init(_ => {
|
||||
sendAsyncMessage('timeout');
|
||||
}, msg.delay, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
});
|
||||
|
||||
sendAsyncMessage('ready');
|
||||
37
toolkit/components/passwordmgr/test/formsubmit.sjs
Normal file
37
toolkit/components/passwordmgr/test/formsubmit.sjs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
try {
|
||||
reallyHandleRequest(request, response);
|
||||
} catch (e) {
|
||||
response.setStatusLine("1.0", 200, "AlmostOK");
|
||||
response.write("Error handling request: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function reallyHandleRequest(request, response) {
|
||||
var match;
|
||||
var requestAuth = true;
|
||||
|
||||
// XXX I bet this doesn't work for POST requests.
|
||||
var query = request.queryString;
|
||||
|
||||
var user = null, pass = null;
|
||||
// user=xxx
|
||||
match = /user=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
user = match[1];
|
||||
|
||||
// pass=xxx
|
||||
match = /pass=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
pass = match[1];
|
||||
|
||||
response.setStatusLine("1.0", 200, "OK");
|
||||
|
||||
response.setHeader("Content-Type", "application/xhtml+xml", false);
|
||||
response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
|
||||
response.write("<p>User: <span id='user'>" + user + "</span></p>\n");
|
||||
response.write("<p>Pass: <span id='pass'>" + pass + "</span></p>\n");
|
||||
response.write("</html>");
|
||||
}
|
||||
20
toolkit/components/passwordmgr/test/mochitest.ini
Normal file
20
toolkit/components/passwordmgr/test/mochitest.ini
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[DEFAULT]
|
||||
skip-if = e10s
|
||||
support-files =
|
||||
authenticate.sjs
|
||||
blank.html
|
||||
formsubmit.sjs
|
||||
prompt_common.js
|
||||
pwmgr_common.js
|
||||
subtst_master_pass.html
|
||||
subtst_prompt_async.html
|
||||
chrome_timeout.js
|
||||
|
||||
[test_master_password.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
[test_prompt_async.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
[test_xhr.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
[test_xml_load.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
try {
|
||||
reallyHandleRequest(request, response);
|
||||
} catch (e) {
|
||||
response.setStatusLine("1.0", 200, "AlmostOK");
|
||||
response.write("Error handling request: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function reallyHandleRequest(request, response) {
|
||||
var match;
|
||||
var requestAuth = true, requestProxyAuth = true;
|
||||
|
||||
// Allow the caller to drive how authentication is processed via the query.
|
||||
// Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
|
||||
// The extra ? allows the user/pass/realm checks to succeed if the name is
|
||||
// at the beginning of the query string.
|
||||
var query = "?" + request.queryString;
|
||||
|
||||
var expected_user = "", expected_pass = "", realm = "mochitest";
|
||||
var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy";
|
||||
var huge = false, plugin = false, anonymous = false;
|
||||
var authHeaderCount = 1;
|
||||
// user=xxx
|
||||
match = /[^_]user=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
expected_user = match[1];
|
||||
|
||||
// pass=xxx
|
||||
match = /[^_]pass=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
expected_pass = match[1];
|
||||
|
||||
// realm=xxx
|
||||
match = /[^_]realm=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
realm = match[1];
|
||||
|
||||
// proxy_user=xxx
|
||||
match = /proxy_user=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
proxy_expected_user = match[1];
|
||||
|
||||
// proxy_pass=xxx
|
||||
match = /proxy_pass=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
proxy_expected_pass = match[1];
|
||||
|
||||
// proxy_realm=xxx
|
||||
match = /proxy_realm=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
proxy_realm = match[1];
|
||||
|
||||
// huge=1
|
||||
match = /huge=1/.exec(query);
|
||||
if (match)
|
||||
huge = true;
|
||||
|
||||
// plugin=1
|
||||
match = /plugin=1/.exec(query);
|
||||
if (match)
|
||||
plugin = true;
|
||||
|
||||
// multiple=1
|
||||
match = /multiple=([^&]*)/.exec(query);
|
||||
if (match)
|
||||
authHeaderCount = match[1]+0;
|
||||
|
||||
// anonymous=1
|
||||
match = /anonymous=1/.exec(query);
|
||||
if (match)
|
||||
anonymous = true;
|
||||
|
||||
// Look for an authentication header, if any, in the request.
|
||||
//
|
||||
// EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
//
|
||||
// This test only supports Basic auth. The value sent by the client is
|
||||
// "username:password", obscured with base64 encoding.
|
||||
|
||||
var actual_user = "", actual_pass = "", authHeader, authPresent = false;
|
||||
if (request.hasHeader("Authorization")) {
|
||||
authPresent = true;
|
||||
authHeader = request.getHeader("Authorization");
|
||||
match = /Basic (.+)/.exec(authHeader);
|
||||
if (match.length != 2)
|
||||
throw new Error("Couldn't parse auth header: " + authHeader);
|
||||
|
||||
var userpass = base64ToString(match[1]); // no atob() :-(
|
||||
match = /(.*):(.*)/.exec(userpass);
|
||||
if (match.length != 3)
|
||||
throw new Error("Couldn't decode auth header: " + userpass);
|
||||
actual_user = match[1];
|
||||
actual_pass = match[2];
|
||||
}
|
||||
|
||||
var proxy_actual_user = "", proxy_actual_pass = "";
|
||||
if (request.hasHeader("Proxy-Authorization")) {
|
||||
authHeader = request.getHeader("Proxy-Authorization");
|
||||
match = /Basic (.+)/.exec(authHeader);
|
||||
if (match.length != 2)
|
||||
throw new Error("Couldn't parse auth header: " + authHeader);
|
||||
|
||||
var userpass = base64ToString(match[1]); // no atob() :-(
|
||||
match = /(.*):(.*)/.exec(userpass);
|
||||
if (match.length != 3)
|
||||
throw new Error("Couldn't decode auth header: " + userpass);
|
||||
proxy_actual_user = match[1];
|
||||
proxy_actual_pass = match[2];
|
||||
}
|
||||
|
||||
// Don't request authentication if the credentials we got were what we
|
||||
// expected.
|
||||
if (expected_user == actual_user &&
|
||||
expected_pass == actual_pass) {
|
||||
requestAuth = false;
|
||||
}
|
||||
if (proxy_expected_user == proxy_actual_user &&
|
||||
proxy_expected_pass == proxy_actual_pass) {
|
||||
requestProxyAuth = false;
|
||||
}
|
||||
|
||||
if (anonymous) {
|
||||
if (authPresent) {
|
||||
response.setStatusLine("1.0", 400, "Unexpected authorization header found");
|
||||
} else {
|
||||
response.setStatusLine("1.0", 200, "Authorization header not found");
|
||||
}
|
||||
} else {
|
||||
if (requestProxyAuth) {
|
||||
response.setStatusLine("1.0", 407, "Proxy authentication required");
|
||||
for (i = 0; i < authHeaderCount; ++i)
|
||||
response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
|
||||
} else if (requestAuth) {
|
||||
response.setStatusLine("1.0", 401, "Authentication required");
|
||||
for (i = 0; i < authHeaderCount; ++i)
|
||||
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
|
||||
} else {
|
||||
response.setStatusLine("1.0", 200, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
response.setHeader("Content-Type", "application/xhtml+xml", false);
|
||||
response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
|
||||
response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
|
||||
response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n");
|
||||
response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
|
||||
response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
|
||||
response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n");
|
||||
|
||||
if (huge) {
|
||||
response.write("<div style='display: none'>");
|
||||
for (i = 0; i < 100000; i++) {
|
||||
response.write("123456789\n");
|
||||
}
|
||||
response.write("</div>");
|
||||
response.write("<span id='footnote'>This is a footnote after the huge content fill</span>");
|
||||
}
|
||||
|
||||
if (plugin) {
|
||||
response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " +
|
||||
"type='application/x-test'></embed>\n");
|
||||
}
|
||||
|
||||
response.write("</html>");
|
||||
}
|
||||
|
||||
|
||||
// base64 decoder
|
||||
//
|
||||
// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa()
|
||||
// doesn't seem to exist. :-(
|
||||
/* Convert Base64 data to a string */
|
||||
const toBinaryTable = [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
];
|
||||
const base64Pad = '=';
|
||||
|
||||
function base64ToString(data) {
|
||||
|
||||
var result = '';
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
|
||||
// Convert one by one.
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||
var padding = (data[i] == base64Pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c == -1) continue;
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding)
|
||||
result += String.fromCharCode((leftdata >> leftbits) & 0xff);
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits)
|
||||
throw Components.Exception('Corrupted base64 string');
|
||||
|
||||
return result;
|
||||
}
|
||||
69
toolkit/components/passwordmgr/test/mochitest/mochitest.ini
Normal file
69
toolkit/components/passwordmgr/test/mochitest/mochitest.ini
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
../../../prompts/test/chromeScript.js
|
||||
../../../prompts/test/prompt_common.js
|
||||
../../../satchel/test/parent_utils.js
|
||||
../../../satchel/test/satchel_common.js
|
||||
../authenticate.sjs
|
||||
../blank.html
|
||||
../browser/form_autofocus_js.html
|
||||
../browser/form_basic.html
|
||||
../browser/form_cross_origin_secure_action.html
|
||||
../pwmgr_common.js
|
||||
auth2/authenticate.sjs
|
||||
|
||||
[test_autocomplete_https_upgrade.html]
|
||||
skip-if = toolkit == 'android' # autocomplete
|
||||
[test_autofill_https_upgrade.html]
|
||||
skip-if = toolkit == 'android' # Bug 1259768
|
||||
[test_autofill_password-only.html]
|
||||
[test_autofocus_js.html]
|
||||
skip-if = toolkit == 'android' # autocomplete
|
||||
[test_basic_form.html]
|
||||
[test_basic_form_0pw.html]
|
||||
[test_basic_form_1pw.html]
|
||||
[test_basic_form_1pw_2.html]
|
||||
[test_basic_form_2pw_1.html]
|
||||
[test_basic_form_2pw_2.html]
|
||||
[test_basic_form_3pw_1.html]
|
||||
[test_basic_form_autocomplete.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_insecure_form_field_autocomplete.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_password_field_autocomplete.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_insecure_form_field_no_saved_login.html]
|
||||
skip-if = toolkit == 'android' || os == 'linux' # android:autocomplete., linux: bug 1325778
|
||||
[test_basic_form_html5.html]
|
||||
[test_basic_form_pwevent.html]
|
||||
[test_basic_form_pwonly.html]
|
||||
[test_bug_627616.html]
|
||||
skip-if = toolkit == 'android' # Tests desktop prompts
|
||||
[test_bug_776171.html]
|
||||
[test_case_differences.html]
|
||||
skip-if = toolkit == 'android' # autocomplete
|
||||
[test_form_action_1.html]
|
||||
[test_form_action_2.html]
|
||||
[test_form_action_javascript.html]
|
||||
[test_formless_autofill.html]
|
||||
[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 = os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_prompt_http.html]
|
||||
skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_prompt_noWindow.html]
|
||||
skip-if = e10s || toolkit == 'android' # Tests desktop prompts. e10s: bug 1217876
|
||||
[test_prompt_promptAuth.html]
|
||||
skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_prompt_promptAuth_proxy.html]
|
||||
skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_recipe_login_fields.html]
|
||||
[test_username_focus.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_xhr_2.html]
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autocomplete on an HTTPS page using upgraded HTTP logins</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const chromeScript = runChecksAfterCommonInit(false);
|
||||
|
||||
runInParent(function addLogins() {
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Create some logins just for this form, since we'll be deleting them.
|
||||
let nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
|
||||
// We have two actual HTTPS to avoid autofill before the schemeUpgrades pref flips to true.
|
||||
let login0 = new nsLoginInfo("https://example.org", "https://example.org", null,
|
||||
"name", "pass", "uname", "pword");
|
||||
|
||||
let login1 = new nsLoginInfo("https://example.org", "https://example.org", null,
|
||||
"name1", "pass1", "uname", "pword");
|
||||
|
||||
// Same as above but HTTP instead of HTTPS (to test de-duping)
|
||||
let login2 = new nsLoginInfo("http://example.org", "http://example.org", null,
|
||||
"name1", "passHTTP", "uname", "pword");
|
||||
|
||||
// Different HTTP login to upgrade with secure formSubmitURL
|
||||
let login3 = new nsLoginInfo("http://example.org", "https://example.org", null,
|
||||
"name2", "passHTTPtoHTTPS", "uname", "pword");
|
||||
|
||||
try {
|
||||
Services.logins.addLogin(login0);
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login2);
|
||||
Services.logins.addLogin(login3);
|
||||
} catch (e) {
|
||||
assert.ok(false, "addLogin threw: " + e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
<iframe src="https://example.org/tests/toolkit/components/passwordmgr/test/mochitest/form_basic.html"></iframe>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
const shiftModifier = SpecialPowers.Ci.nsIDOMEvent.SHIFT_MASK;
|
||||
|
||||
let iframe = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]);
|
||||
let iframeDoc;
|
||||
let uname;
|
||||
let pword;
|
||||
|
||||
// Restore the form to the default state.
|
||||
function restoreForm() {
|
||||
pword.focus();
|
||||
uname.value = "";
|
||||
pword.value = "";
|
||||
uname.focus();
|
||||
}
|
||||
|
||||
// Check for expected username/password in form.
|
||||
function checkACForm(expectedUsername, expectedPassword) {
|
||||
let formID = uname.parentNode.id;
|
||||
is(uname.value, expectedUsername, "Checking " + formID + " username");
|
||||
is(pword.value, expectedPassword, "Checking " + formID + " password");
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["signon.schemeUpgrades", true]]});
|
||||
|
||||
yield new Promise(resolve => {
|
||||
iframe.addEventListener("load", function onLoad() {
|
||||
iframe.removeEventListener("load", onLoad);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
iframeDoc = iframe.contentDocument;
|
||||
uname = iframeDoc.getElementById("form-basic-username");
|
||||
pword = iframeDoc.getElementById("form-basic-password");
|
||||
});
|
||||
|
||||
add_task(function* test_empty_first_entry() {
|
||||
// Make sure initial form is empty.
|
||||
checkACForm("", "");
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup is initially closed");
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down");
|
||||
let results = yield shownPromise;
|
||||
popupState = yield getPopupState();
|
||||
is(popupState.selectedIndex, -1, "Check no entries are selected");
|
||||
checkArrayValues(results, ["name", "name1", "name2"], "initial");
|
||||
|
||||
// Check first entry
|
||||
let index0Promise = notifySelectedIndex(0);
|
||||
doKey("down");
|
||||
yield index0Promise;
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("name", "pass");
|
||||
});
|
||||
|
||||
add_task(function* test_empty_second_entry() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("name1", "pass1");
|
||||
});
|
||||
|
||||
add_task(function* test_search() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
// We need to blur for the autocomplete controller to notice the forced value below.
|
||||
uname.blur();
|
||||
uname.value = "name";
|
||||
uname.focus();
|
||||
sendChar("1");
|
||||
doKey("down"); // open
|
||||
let results = yield shownPromise;
|
||||
checkArrayValues(results, ["name1"], "check result deduping for 'name1'");
|
||||
doKey("down"); // first
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("name1", "pass1");
|
||||
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup is now closed");
|
||||
});
|
||||
|
||||
add_task(function* test_delete_first_entry() {
|
||||
restoreForm();
|
||||
uname.focus();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down");
|
||||
yield shownPromise;
|
||||
|
||||
let index0Promise = notifySelectedIndex(0);
|
||||
doKey("down");
|
||||
yield index0Promise;
|
||||
|
||||
let deletionPromise = promiseStorageChanged(["removeLogin"]);
|
||||
// On OS X, shift-backspace and shift-delete work, just delete does not.
|
||||
// On Win/Linux, shift-backspace does not work, delete and shift-delete do.
|
||||
doKey("delete", shiftModifier);
|
||||
yield deletionPromise;
|
||||
checkACForm("", "");
|
||||
|
||||
let results = yield notifyMenuChanged(2, "name1");
|
||||
|
||||
checkArrayValues(results, ["name1", "name2"], "two should remain after deleting the first");
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, true, "Check popup stays open after deleting");
|
||||
doKey("escape");
|
||||
popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup closed upon ESC");
|
||||
});
|
||||
|
||||
add_task(function* test_delete_duplicate_entry() {
|
||||
restoreForm();
|
||||
uname.focus();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down");
|
||||
yield shownPromise;
|
||||
|
||||
let index0Promise = notifySelectedIndex(0);
|
||||
doKey("down");
|
||||
yield index0Promise;
|
||||
|
||||
let deletionPromise = promiseStorageChanged(["removeLogin"]);
|
||||
// On OS X, shift-backspace and shift-delete work, just delete does not.
|
||||
// On Win/Linux, shift-backspace does not work, delete and shift-delete do.
|
||||
doKey("delete", shiftModifier);
|
||||
yield deletionPromise;
|
||||
checkACForm("", "");
|
||||
|
||||
is(LoginManager.countLogins("http://example.org", "http://example.org", null), 1,
|
||||
"Check that the HTTP login remains");
|
||||
is(LoginManager.countLogins("https://example.org", "https://example.org", null), 0,
|
||||
"Check that the HTTPS login was deleted");
|
||||
|
||||
// Two menu items should remain as the HTTPS login should have been deleted but
|
||||
// the HTTP would remain.
|
||||
let results = yield notifyMenuChanged(1, "name2");
|
||||
|
||||
checkArrayValues(results, ["name2"], "one should remain after deleting the HTTPS name1");
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, true, "Check popup stays open after deleting");
|
||||
doKey("escape");
|
||||
popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup closed upon ESC");
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autocomplete on an HTTPS page using upgraded HTTP logins</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const MISSING_ACTION_PATH = TESTS_DIR + "mochitest/form_basic.html";
|
||||
const CROSS_ORIGIN_SECURE_PATH = TESTS_DIR + "mochitest/form_cross_origin_secure_action.html";
|
||||
|
||||
const chromeScript = runChecksAfterCommonInit(false);
|
||||
|
||||
let nsLoginInfo = SpecialPowers.wrap(SpecialPowers.Components).Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
SpecialPowers.Ci.nsILoginInfo,
|
||||
"init");
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
<iframe></iframe>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
let iframe = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]);
|
||||
|
||||
// Check for expected username/password in form.
|
||||
function checkACForm(expectedUsername, expectedPassword) {
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let uname = iframeDoc.getElementById("form-basic-username");
|
||||
let pword = iframeDoc.getElementById("form-basic-password");
|
||||
let formID = uname.parentNode.id;
|
||||
is(uname.value, expectedUsername, "Checking " + formID + " username");
|
||||
is(pword.value, expectedPassword, "Checking " + formID + " password");
|
||||
}
|
||||
function* prepareLoginsAndProcessForm(url, logins = []) {
|
||||
LoginManager.removeAllLogins();
|
||||
|
||||
let dates = Date.now();
|
||||
for (let login of logins) {
|
||||
SpecialPowers.do_QueryInterface(login, SpecialPowers.Ci.nsILoginMetaInfo);
|
||||
// Force all dates to be the same so they don't affect things like deduping.
|
||||
login.timeCreated = login.timePasswordChanged = login.timeLastUsed = dates;
|
||||
LoginManager.addLogin(login);
|
||||
}
|
||||
|
||||
iframe.src = url;
|
||||
yield promiseFormsProcessed();
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["signon.schemeUpgrades", true]]});
|
||||
});
|
||||
|
||||
add_task(function* test_simpleNoDupesNoAction() {
|
||||
yield prepareLoginsAndProcessForm("https://example.com" + MISSING_ACTION_PATH, [
|
||||
new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"name2", "pass2", "uname", "pword"),
|
||||
]);
|
||||
|
||||
checkACForm("name2", "pass2");
|
||||
});
|
||||
|
||||
add_task(function* test_simpleNoDupesUpgradeOriginAndAction() {
|
||||
yield prepareLoginsAndProcessForm("https://example.com" + CROSS_ORIGIN_SECURE_PATH, [
|
||||
new nsLoginInfo("http://example.com", "http://another.domain", null,
|
||||
"name2", "pass2", "uname", "pword"),
|
||||
]);
|
||||
|
||||
checkACForm("name2", "pass2");
|
||||
});
|
||||
|
||||
add_task(function* test_simpleNoDupesUpgradeOriginOnly() {
|
||||
yield prepareLoginsAndProcessForm("https://example.com" + CROSS_ORIGIN_SECURE_PATH, [
|
||||
new nsLoginInfo("http://example.com", "https://another.domain", null,
|
||||
"name2", "pass2", "uname", "pword"),
|
||||
]);
|
||||
|
||||
checkACForm("name2", "pass2");
|
||||
});
|
||||
|
||||
add_task(function* test_simpleNoDupesUpgradeActionOnly() {
|
||||
yield prepareLoginsAndProcessForm("https://example.com" + CROSS_ORIGIN_SECURE_PATH, [
|
||||
new nsLoginInfo("https://example.com", "http://another.domain", null,
|
||||
"name2", "pass2", "uname", "pword"),
|
||||
]);
|
||||
|
||||
checkACForm("name2", "pass2");
|
||||
});
|
||||
|
||||
add_task(function* test_dedupe() {
|
||||
yield prepareLoginsAndProcessForm("https://example.com" + MISSING_ACTION_PATH, [
|
||||
new nsLoginInfo("https://example.com", "https://example.com", null,
|
||||
"name1", "passHTTPStoHTTPS", "uname", "pword"),
|
||||
new nsLoginInfo("http://example.com", "http://example.com", null,
|
||||
"name1", "passHTTPtoHTTP", "uname", "pword"),
|
||||
new nsLoginInfo("http://example.com", "https://example.com", null,
|
||||
"name1", "passHTTPtoHTTPS", "uname", "pword"),
|
||||
new nsLoginInfo("https://example.com", "http://example.com", null,
|
||||
"name1", "passHTTPStoHTTP", "uname", "pword"),
|
||||
]);
|
||||
|
||||
checkACForm("name1", "passHTTPStoHTTPS");
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test password-only forms should prefer a password-only login when present</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: Bug 444968
|
||||
<script>
|
||||
let pwmgrCommonScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
pwmgrCommonScript.sendSyncMessage("setupParent", { selfFilling: true });
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let chromeScript = runInParent(function chromeSetup() {
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
let pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
|
||||
let login1A = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login1B = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2A = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2B = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
let login2C = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
|
||||
login1A.init("http://mochi.test:8888", "http://bug444968-1", null,
|
||||
"testuser1A", "testpass1A", "", "");
|
||||
login1B.init("http://mochi.test:8888", "http://bug444968-1", null,
|
||||
"", "testpass1B", "", "");
|
||||
|
||||
login2A.init("http://mochi.test:8888", "http://bug444968-2", null,
|
||||
"testuser2A", "testpass2A", "", "");
|
||||
login2B.init("http://mochi.test:8888", "http://bug444968-2", null,
|
||||
"", "testpass2B", "", "");
|
||||
login2C.init("http://mochi.test:8888", "http://bug444968-2", null,
|
||||
"testuser2C", "testpass2C", "", "");
|
||||
|
||||
pwmgr.addLogin(login1A);
|
||||
pwmgr.addLogin(login1B);
|
||||
pwmgr.addLogin(login2A);
|
||||
pwmgr.addLogin(login2B);
|
||||
pwmgr.addLogin(login2C);
|
||||
|
||||
addMessageListener("removeLogins", function removeLogins() {
|
||||
pwmgr.removeLogin(login1A);
|
||||
pwmgr.removeLogin(login1B);
|
||||
pwmgr.removeLogin(login2A);
|
||||
pwmgr.removeLogin(login2B);
|
||||
pwmgr.removeLogin(login2C);
|
||||
});
|
||||
});
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => chromeScript.sendSyncMessage("removeLogins"));
|
||||
|
||||
registerRunTests();
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<!-- first 3 forms have matching user+pass and pass-only logins -->
|
||||
|
||||
<!-- user+pass form. -->
|
||||
<form id="form1" action="http://bug444968-1">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- password-only form. -->
|
||||
<form id="form2" action="http://bug444968-1">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- user+pass form, username prefilled -->
|
||||
<form id="form3" action="http://bug444968-1">
|
||||
<input type="text" name="uname" value="testuser1A">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<!-- next 4 forms have matching user+pass (2x) and pass-only (1x) logins -->
|
||||
|
||||
<!-- user+pass form. -->
|
||||
<form id="form4" action="http://bug444968-2">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- password-only form. -->
|
||||
<form id="form5" action="http://bug444968-2">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- user+pass form, username prefilled -->
|
||||
<form id="form6" action="http://bug444968-2">
|
||||
<input type="text" name="uname" value="testuser2A">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- user+pass form, username prefilled -->
|
||||
<form id="form7" action="http://bug444968-2">
|
||||
<input type="text" name="uname" value="testuser2C">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/* Test for Login Manager: 444968 (password-only forms should prefer a
|
||||
* password-only login when present )
|
||||
*/
|
||||
function startTest() {
|
||||
checkForm(1, "testuser1A", "testpass1A");
|
||||
checkForm(2, "testpass1B");
|
||||
checkForm(3, "testuser1A", "testpass1A");
|
||||
|
||||
checkUnmodifiedForm(4); // 2 logins match
|
||||
checkForm(5, "testpass2B");
|
||||
checkForm(6, "testuser2A", "testpass2A");
|
||||
checkForm(7, "testuser2C", "testpass2C");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test login autocomplete is activated when focused by js on load</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const chromeScript = runChecksAfterCommonInit(false);
|
||||
|
||||
runInParent(function addLogins() {
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Create some logins just for this form, since we'll be deleting them.
|
||||
let nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
|
||||
let login0 = new nsLoginInfo("https://example.org", "https://example.org", null,
|
||||
"name", "pass", "uname", "pword");
|
||||
|
||||
let login1 = new nsLoginInfo("https://example.org", "https://example.org", null,
|
||||
"name1", "pass1", "uname", "pword");
|
||||
|
||||
try {
|
||||
Services.logins.addLogin(login0);
|
||||
Services.logins.addLogin(login1);
|
||||
} catch (e) {
|
||||
assert.ok(false, "addLogin threw: " + e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<iframe src="https://example.org/tests/toolkit/components/passwordmgr/test/mochitest/form_autofocus_js.html"></iframe>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
let iframe = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]);
|
||||
let iframeDoc;
|
||||
|
||||
add_task(function* setup() {
|
||||
yield new Promise(resolve => {
|
||||
iframe.addEventListener("load", function onLoad() {
|
||||
iframe.removeEventListener("load", onLoad);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
iframeDoc = iframe.contentDocument;
|
||||
|
||||
SimpleTest.requestFlakyTimeout("Giving a chance for the unexpected popupshown to occur");
|
||||
});
|
||||
|
||||
add_task(function* test_initial_focus() {
|
||||
let results = yield notifyMenuChanged(2, "name");
|
||||
checkArrayValues(results, ["name", "name1"], "Two results");
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
is(iframeDoc.getElementById("form-basic-password").value, "pass", "Check first password filled");
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup is now closed");
|
||||
});
|
||||
|
||||
// This depends on the filling from the previous test.
|
||||
add_task(function* test_not_reopened_if_filled() {
|
||||
listenForUnexpectedPopupShown();
|
||||
let usernameField = iframeDoc.getElementById("form-basic-username");
|
||||
usernameField.focus();
|
||||
info("Waiting to see if a popupshown occurs");
|
||||
yield new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// cleanup
|
||||
gPopupShownExpected = true;
|
||||
iframeDoc.getElementById("form-basic-submit").focus();
|
||||
});
|
||||
|
||||
add_task(function* test_reopened_after_edit_not_matching_saved() {
|
||||
let usernameField = iframeDoc.getElementById("form-basic-username");
|
||||
usernameField.value = "nam";
|
||||
let shownPromise = promiseACShown();
|
||||
usernameField.focus();
|
||||
yield shownPromise;
|
||||
iframeDoc.getElementById("form-basic-submit").focus();
|
||||
});
|
||||
|
||||
add_task(function* test_not_reopened_after_selecting() {
|
||||
let formFillController = SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].
|
||||
getService(SpecialPowers.Ci.nsIFormFillController);
|
||||
let usernameField = iframeDoc.getElementById("form-basic-username");
|
||||
usernameField.value = "";
|
||||
iframeDoc.getElementById("form-basic-password").value = "";
|
||||
listenForUnexpectedPopupShown();
|
||||
formFillController.markAsLoginManagerField(usernameField);
|
||||
info("Waiting to see if a popupshown occurs");
|
||||
yield new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Cleanup
|
||||
gPopupShownExpected = true;
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test basic autofill</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: simple form fill
|
||||
|
||||
<script>
|
||||
runChecksAfterCommonInit(startTest);
|
||||
|
||||
/** Test for Login Manager: form fill, multiple forms. **/
|
||||
|
||||
function startTest() {
|
||||
is($_(1, "uname").value, "testuser", "Checking for filled username");
|
||||
is($_(1, "pword").value, "testpass", "Checking for filled password");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<form id="form1" action="formtest.js">
|
||||
<p>This is form 1.</p>
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test forms with no password fields</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: forms with no password fields
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<!-- Form with no user field or password field -->
|
||||
<form id="form1" action="formtest.js">
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Form with no user field or password field, but one other field -->
|
||||
<form id="form2" action="formtest.js">
|
||||
<input type="checkbox">
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Form with no user field or password field, but one other field -->
|
||||
<form id="form3" action="formtest.js">
|
||||
<input type="checkbox" name="uname" value="">
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Form with a text field, but no password field -->
|
||||
<form id="form4" action="formtest.js">
|
||||
<input type="text" name="yyyyy">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Form with a user field, but no password field -->
|
||||
<form id="form5" action="formtest.js">
|
||||
<input type="text" name="uname">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: form fill, no password fields. **/
|
||||
|
||||
function startTest() {
|
||||
is($_(3, "uname").value, "", "Checking for unfilled checkbox (form 3)");
|
||||
is($_(4, "yyyyy").value, "", "Checking for unfilled text field (form 4)");
|
||||
is($_(5, "uname").value, "", "Checking for unfilled text field (form 5)");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
runChecksAfterCommonInit(startTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autofill for forms with 1 password field</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: forms with 1 password field
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<!-- no username fields -->
|
||||
|
||||
<form id='form1' action='formtest.js'> 1
|
||||
<!-- Blank, so fill in the password -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form2' action='formtest.js'> 2
|
||||
<!-- Already contains the password, so nothing to do. -->
|
||||
<input type='password' name='pname' value='testpass'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form3' action='formtest.js'> 3
|
||||
<!-- Contains unknown password, so don't change it -->
|
||||
<input type='password' name='pname' value='xxxxxxxx'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
<!-- username fields -->
|
||||
|
||||
<form id='form4' action='formtest.js'> 4
|
||||
<!-- Blanks, so fill in login -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form5' action='formtest.js'> 5
|
||||
<!-- Username already set, so fill in password -->
|
||||
<input type='text' name='uname' value='testuser'>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form6' action='formtest.js'> 6
|
||||
<!-- Unknown username, so don't fill in password -->
|
||||
<input type='text' name='uname' value='xxxxxxxx'>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form7' action='formtest.js'> 7
|
||||
<!-- Password already set, could fill in username but that's weird so we don't -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='testpass'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form8' action='formtest.js'> 8
|
||||
<!-- Unknown password, so don't fill in a username -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='xxxxxxxx'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- extra text fields -->
|
||||
|
||||
<form id='form9' action='formtest.js'> 9
|
||||
<!-- text field _after_ password should never be treated as a username field -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='text' name='uname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form10' action='formtest.js'> 10
|
||||
<!-- only the first text field before the password should be for username -->
|
||||
<input type='text' name='other' value=''>
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form11' action='formtest.js'> 11
|
||||
<!-- variation just to make sure extra text field is still ignored -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='text' name='other' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- same as last bunch, but with xxxx in the extra field. -->
|
||||
|
||||
<form id='form12' action='formtest.js'> 12
|
||||
<!-- text field _after_ password should never be treated as a username field -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='text' name='uname' value='xxxxxxxx'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form13' action='formtest.js'> 13
|
||||
<!-- only the first text field before the password should be for username -->
|
||||
<input type='text' name='other' value='xxxxxxxx'>
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form14' action='formtest.js'> 14
|
||||
<!-- variation just to make sure extra text field is still ignored -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='text' name='other' value='xxxxxxxx'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: simple form fill **/
|
||||
|
||||
function startTest() {
|
||||
var f = 1;
|
||||
|
||||
// 1-3
|
||||
checkForm(f++, "testpass");
|
||||
checkForm(f++, "testpass");
|
||||
checkForm(f++, "xxxxxxxx");
|
||||
|
||||
// 4-8
|
||||
checkForm(f++, "testuser", "testpass");
|
||||
checkForm(f++, "testuser", "testpass");
|
||||
checkForm(f++, "xxxxxxxx", "");
|
||||
checkForm(f++, "", "testpass");
|
||||
checkForm(f++, "", "xxxxxxxx");
|
||||
|
||||
// 9-14
|
||||
checkForm(f++, "testpass", "");
|
||||
checkForm(f++, "", "testuser", "testpass");
|
||||
checkForm(f++, "testuser", "testpass", "");
|
||||
checkForm(f++, "testpass", "xxxxxxxx");
|
||||
checkForm(f++, "xxxxxxxx", "testuser", "testpass");
|
||||
checkForm(f++, "testuser", "testpass", "xxxxxxxx");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test forms with 1 password field, part 2</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: forms with 1 password field, part 2
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<form id='form1' action='formtest.js'> 1
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form2' action='formtest.js'> 2
|
||||
<input type='password' name='pname' value='' disabled>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form3' action='formtest.js'> 3
|
||||
<input type='password' name='pname' value='' readonly>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form4' action='formtest.js'> 4
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form5' action='formtest.js'> 5
|
||||
<input type='text' name='uname' value='' disabled>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form6' action='formtest.js'> 6
|
||||
<input type='text' name='uname' value='' readonly>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form7' action='formtest.js'> 7
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='' disabled>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form8' action='formtest.js'> 8
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='' readonly>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form9' action='formtest.js'> 9
|
||||
<input type='text' name='uname' value='TESTUSER'>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form10' action='formtest.js'> 10
|
||||
<input type='text' name='uname' value='TESTUSER' readonly>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form11' action='formtest.js'> 11
|
||||
<input type='text' name='uname' value='TESTUSER' disabled>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: simple form fill, part 2 **/
|
||||
|
||||
function startTest() {
|
||||
var f;
|
||||
|
||||
// Test various combinations of disabled/readonly inputs
|
||||
checkForm(1, "testpass"); // control
|
||||
checkUnmodifiedForm(2);
|
||||
checkUnmodifiedForm(3);
|
||||
checkForm(4, "testuser", "testpass"); // control
|
||||
for (f = 5; f <= 8; f++) { checkUnmodifiedForm(f); }
|
||||
// Test case-insensitive comparison of username field
|
||||
checkForm(9, "testuser", "testpass");
|
||||
checkForm(10, "TESTUSER", "testpass");
|
||||
checkForm(11, "TESTUSER", "testpass");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autofill for forms with 2 password fields</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: forms with 2 password fields
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
<!-- no username fields -->
|
||||
|
||||
<form id='form1' action='formtest.js'> 1
|
||||
<!-- simple form, fill in first pw -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form2' action='formtest.js'> 2
|
||||
<!-- same but reverse pname and qname, field names are ignored. -->
|
||||
<input type='password' name='qname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form3' action='formtest.js'> 3
|
||||
<!-- text field after password fields should be ignored, no username. -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<input type='text' name='uname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form4' action='formtest.js'> 4
|
||||
<!-- nothing to do, password already present -->
|
||||
<input type='password' name='pname' value='testpass'>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form5' action='formtest.js'> 5
|
||||
<!-- don't clobber an existing unrecognized password -->
|
||||
<input type='password' name='pname' value='xxxxxxxx'>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form6' action='formtest.js'> 6
|
||||
<!-- fill in first field, 2nd field shouldn't be touched anyway. -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value='xxxxxxxx'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- with username fields -->
|
||||
|
||||
|
||||
|
||||
<form id='form7' action='formtest.js'> 7
|
||||
<!-- simple form, should fill in username and first pw -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form8' action='formtest.js'> 8
|
||||
<!-- reverse pname and qname, field names are ignored. -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form9' action='formtest.js'> 9
|
||||
<!-- username already filled, so just fill first password -->
|
||||
<input type='text' name='uname' value='testuser'>
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form10' action='formtest.js'> 10
|
||||
<!-- unknown username, don't fill in a password -->
|
||||
<input type='text' name='uname' value='xxxxxxxx'>
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form11' action='formtest.js'> 11
|
||||
<!-- don't clobber unknown password -->
|
||||
<input type='text' name='uname' value='testuser'>
|
||||
<input type='password' name='pname' value='xxxxxxxx'>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form12' action='formtest.js'> 12
|
||||
<!-- fill in 1st pass, don't clobber 2nd pass -->
|
||||
<input type='text' name='uname' value='testuser'>
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='password' name='qname' value='xxxxxxxx'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form13' action='formtest.js'> 13
|
||||
<!-- nothing to do, user and pass prefilled. life is easy. -->
|
||||
<input type='text' name='uname' value='testuser'>
|
||||
<input type='password' name='pname' value='testpass'>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form14' action='formtest.js'> 14
|
||||
<!-- shouldn't fill in username because 1st pw field is unknown. -->
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='xxxxxxxx'>
|
||||
<input type='password' name='qname' value='testpass'>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form15' action='formtest.js'> 15
|
||||
<!-- textfield in the middle of pw fields should be ignored -->
|
||||
<input type='password' name='pname' value=''>
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
<form id='form16' action='formtest.js'> 16
|
||||
<!-- same, and don't clobber existing unknown password -->
|
||||
<input type='password' name='pname' value='xxxxxxxx'>
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='qname' value=''>
|
||||
<button type='submit'>Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: simple form fill **/
|
||||
|
||||
function startTest() {
|
||||
var f = 1;
|
||||
|
||||
// 1-6 no username
|
||||
checkForm(f++, "testpass", "");
|
||||
checkForm(f++, "testpass", "");
|
||||
checkForm(f++, "testpass", "", "");
|
||||
checkForm(f++, "testpass", "");
|
||||
checkForm(f++, "xxxxxxxx", "");
|
||||
checkForm(f++, "testpass", "xxxxxxxx");
|
||||
|
||||
// 7-15 with username
|
||||
checkForm(f++, "testuser", "testpass", "");
|
||||
checkForm(f++, "testuser", "testpass", "");
|
||||
checkForm(f++, "testuser", "testpass", "");
|
||||
checkForm(f++, "xxxxxxxx", "", "");
|
||||
checkForm(f++, "testuser", "xxxxxxxx", "");
|
||||
checkForm(f++, "testuser", "testpass", "xxxxxxxx");
|
||||
checkForm(f++, "testuser", "testpass", "");
|
||||
checkForm(f++, "", "xxxxxxxx", "testpass");
|
||||
checkForm(f++, "testpass", "", "");
|
||||
checkForm(f++, "xxxxxxxx", "", "");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for form fill with 2 password fields</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: form fill, 2 password fields
|
||||
<p id="display"></p>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: form fill, 2 password fields **/
|
||||
|
||||
/*
|
||||
* If a form has two password fields, other things may be going on....
|
||||
*
|
||||
* 1 - The user might be creating a new login (2nd field for typo checking)
|
||||
* 2 - The user is changing a password (old and new password each have field)
|
||||
*
|
||||
* This test is for case #1.
|
||||
*/
|
||||
|
||||
var numSubmittedForms = 0;
|
||||
var numStartingLogins = 0;
|
||||
|
||||
function startTest() {
|
||||
// Check for unfilled forms
|
||||
is($_(1, "uname").value, "", "Checking username 1");
|
||||
is($_(1, "pword").value, "", "Checking password 1A");
|
||||
is($_(1, "qword").value, "", "Checking password 1B");
|
||||
|
||||
// Fill in the username and password fields, for account creation.
|
||||
// Form 1
|
||||
$_(1, "uname").value = "newuser1";
|
||||
$_(1, "pword").value = "newpass1";
|
||||
$_(1, "qword").value = "newpass1";
|
||||
|
||||
var button = getFormSubmitButton(1);
|
||||
|
||||
todo(false, "form submission disabled, can't auto-accept dialog yet");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
||||
// Called by each form's onsubmit handler.
|
||||
function checkSubmit(formNum) {
|
||||
numSubmittedForms++;
|
||||
|
||||
// End the test at the last form.
|
||||
if (formNum == 999) {
|
||||
is(numSubmittedForms, 999, "Ensuring all forms submitted for testing.");
|
||||
|
||||
var numEndingLogins = LoginManager.countLogins("", "", "");
|
||||
|
||||
ok(numEndingLogins > 0, "counting logins at end");
|
||||
is(numStartingLogins, numEndingLogins + 222, "counting logins at end");
|
||||
|
||||
SimpleTest.finish();
|
||||
return false; // return false to cancel current form submission
|
||||
}
|
||||
|
||||
// submit the next form.
|
||||
var button = getFormSubmitButton(formNum + 1);
|
||||
button.click();
|
||||
|
||||
return false; // return false to cancel current form submission
|
||||
}
|
||||
|
||||
|
||||
function getFormSubmitButton(formNum) {
|
||||
var form = $("form" + formNum); // by id, not name
|
||||
ok(form != null, "getting form " + formNum);
|
||||
|
||||
// we can't just call form.submit(), because that doesn't seem to
|
||||
// invoke the form onsubmit handler.
|
||||
var button = form.firstChild;
|
||||
while (button && button.type != "submit") { button = button.nextSibling; }
|
||||
ok(button != null, "getting form submit button");
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
runChecksAfterCommonInit(startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="content" style="display: none">
|
||||
<form id="form1" onsubmit="return checkSubmit(1)" action="http://newuser.com">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<input type="password" name="qword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autofill for forms with 3 password fields</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: forms with 3 password fields (form filling)
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
<p>The next three forms are <b>user/pass/passB/passC</b>, as all-empty, preuser(only), and preuser/pass</p>
|
||||
<form id="form1" action="formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="rword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<form id="form2" action="formtest.js">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="pword">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="rword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<form id="form3" action="formtest.js">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="pword" value="testpass">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="rword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
|
||||
<p>The next three forms are <b>user/passB/pass/passC</b>, as all-empty, preuser(only), and preuser/pass</p>
|
||||
<form id="form4" action="formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="pword">
|
||||
<input type="password" name="rword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<form id="form5" action="formtest.js">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="pword">
|
||||
<input type="password" name="rword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<form id="form6" action="formtest.js">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="pword" value="testpass">
|
||||
<input type="password" name="rword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<p>The next three forms are <b>user/passB/passC/pass</b>, as all-empty, preuser(only), and preuser/pass</p>
|
||||
<form id="form7" action="formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="rword">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<form id="form8" action="formtest.js">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="rword">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<form id="form9" action="formtest.js">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="qword">
|
||||
<input type="password" name="rword">
|
||||
<input type="password" name="pword" value="testpass">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: form fill, 3 password fields **/
|
||||
|
||||
// Test to make sure 3-password forms are filled properly.
|
||||
|
||||
function startTest() {
|
||||
// Check form 1
|
||||
is($_(1, "uname").value, "testuser", "Checking username 1");
|
||||
is($_(1, "pword").value, "testpass", "Checking password 1");
|
||||
is($_(1, "qword").value, "", "Checking password 1 (q)");
|
||||
is($_(1, "rword").value, "", "Checking password 1 (r)");
|
||||
// Check form 2
|
||||
is($_(2, "uname").value, "testuser", "Checking username 2");
|
||||
is($_(2, "pword").value, "testpass", "Checking password 2");
|
||||
is($_(2, "qword").value, "", "Checking password 2 (q)");
|
||||
is($_(2, "rword").value, "", "Checking password 2 (r)");
|
||||
// Check form 3
|
||||
is($_(3, "uname").value, "testuser", "Checking username 3");
|
||||
is($_(3, "pword").value, "testpass", "Checking password 3");
|
||||
is($_(3, "qword").value, "", "Checking password 3 (q)");
|
||||
is($_(3, "rword").value, "", "Checking password 3 (r)");
|
||||
|
||||
// Check form 4
|
||||
is($_(4, "uname").value, "testuser", "Checking username 4");
|
||||
todo_is($_(4, "qword").value, "", "Checking password 4 (q)");
|
||||
todo_is($_(4, "pword").value, "testpass", "Checking password 4");
|
||||
is($_(4, "rword").value, "", "Checking password 4 (r)");
|
||||
// Check form 5
|
||||
is($_(5, "uname").value, "testuser", "Checking username 5");
|
||||
todo_is($_(5, "qword").value, "", "Checking password 5 (q)");
|
||||
todo_is($_(5, "pword").value, "testpass", "Checking password 5");
|
||||
is($_(5, "rword").value, "", "Checking password 5 (r)");
|
||||
// Check form 6
|
||||
is($_(6, "uname").value, "testuser", "Checking username 6");
|
||||
todo_is($_(6, "qword").value, "", "Checking password 6 (q)");
|
||||
is($_(6, "pword").value, "testpass", "Checking password 6");
|
||||
is($_(6, "rword").value, "", "Checking password 6 (r)");
|
||||
|
||||
// Check form 7
|
||||
is($_(7, "uname").value, "testuser", "Checking username 7");
|
||||
todo_is($_(7, "qword").value, "", "Checking password 7 (q)");
|
||||
is($_(7, "rword").value, "", "Checking password 7 (r)");
|
||||
todo_is($_(7, "pword").value, "testpass", "Checking password 7");
|
||||
// Check form 8
|
||||
is($_(8, "uname").value, "testuser", "Checking username 8");
|
||||
todo_is($_(8, "qword").value, "", "Checking password 8 (q)");
|
||||
is($_(8, "rword").value, "", "Checking password 8 (r)");
|
||||
todo_is($_(8, "pword").value, "testpass", "Checking password 8");
|
||||
// Check form 9
|
||||
is($_(9, "uname").value, "testuser", "Checking username 9");
|
||||
todo_is($_(9, "qword").value, "", "Checking password 9 (q)");
|
||||
is($_(9, "rword").value, "", "Checking password 9 (r)");
|
||||
is($_(9, "pword").value, "testpass", "Checking password 9");
|
||||
|
||||
// TODO: as with the 2-password cases, add tests to check for creating new
|
||||
// logins and changing passwords.
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,859 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test basic login autocomplete</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: multiple login autocomplete
|
||||
|
||||
<script>
|
||||
var chromeScript = runChecksAfterCommonInit();
|
||||
|
||||
var setupScript = runInParent(function setup() {
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Create some logins just for this form, since we'll be deleting them.
|
||||
var nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
assert.ok(nsLoginInfo != null, "nsLoginInfo constructor");
|
||||
|
||||
// login0 has no username, so should be filtered out from the autocomplete list.
|
||||
var login0 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"", "user0pass", "", "pword");
|
||||
|
||||
var login1 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"tempuser1", "temppass1", "uname", "pword");
|
||||
|
||||
var login2 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"testuser2", "testpass2", "uname", "pword");
|
||||
|
||||
var login3 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"testuser3", "testpass3", "uname", "pword");
|
||||
|
||||
var login4 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"zzzuser4", "zzzpass4", "uname", "pword");
|
||||
|
||||
// login 5 only used in the single-user forms
|
||||
var login5 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete2", null,
|
||||
"singleuser5", "singlepass5", "uname", "pword");
|
||||
|
||||
var login6A = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete3", null,
|
||||
"form7user1", "form7pass1", "uname", "pword");
|
||||
var login6B = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete3", null,
|
||||
"form7user2", "form7pass2", "uname", "pword");
|
||||
|
||||
var login7 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete4", null,
|
||||
"form8user", "form8pass", "uname", "pword");
|
||||
|
||||
var login8A = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete5", null,
|
||||
"form9userAB", "form9pass", "uname", "pword");
|
||||
var login8B = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete5", null,
|
||||
"form9userAAB", "form9pass", "uname", "pword");
|
||||
var login8C = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete5", null,
|
||||
"form9userAABzz", "form9pass", "uname", "pword");
|
||||
|
||||
var login10 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete7", null,
|
||||
"testuser10", "testpass10", "uname", "pword");
|
||||
|
||||
|
||||
// try/catch in case someone runs the tests manually, twice.
|
||||
try {
|
||||
Services.logins.addLogin(login0);
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login2);
|
||||
Services.logins.addLogin(login3);
|
||||
Services.logins.addLogin(login4);
|
||||
Services.logins.addLogin(login5);
|
||||
Services.logins.addLogin(login6A);
|
||||
Services.logins.addLogin(login6B);
|
||||
Services.logins.addLogin(login7);
|
||||
Services.logins.addLogin(login8A);
|
||||
Services.logins.addLogin(login8B);
|
||||
// login8C is added later
|
||||
Services.logins.addLogin(login10);
|
||||
} catch (e) {
|
||||
assert.ok(false, "addLogin threw: " + e);
|
||||
}
|
||||
|
||||
addMessageListener("addLogin", loginVariableName => {
|
||||
let login = eval(loginVariableName);
|
||||
assert.ok(!!login, "Login to add is defined: " + loginVariableName);
|
||||
Services.logins.addLogin(login);
|
||||
});
|
||||
addMessageListener("removeLogin", loginVariableName => {
|
||||
let login = eval(loginVariableName);
|
||||
assert.ok(!!login, "Login to delete is defined: " + loginVariableName);
|
||||
Services.logins.removeLogin(login);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
|
||||
<!-- form1 tests multiple matching logins -->
|
||||
<form id="form1" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- other forms test single logins, with autocomplete=off set -->
|
||||
<form id="form2" action="http://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword" autocomplete="off">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form3" action="http://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname" autocomplete="off">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form4" action="http://autocomplete2" onsubmit="return false;" autocomplete="off">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form5" action="http://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname" autocomplete="off">
|
||||
<input type="password" name="pword" autocomplete="off">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- control -->
|
||||
<form id="form6" action="http://autocomplete2" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- This form will be manipulated to insert a different username field. -->
|
||||
<form id="form7" action="http://autocomplete3" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- test for no autofill after onblur with blank username -->
|
||||
<form id="form8" action="http://autocomplete4" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- test autocomplete dropdown -->
|
||||
<form id="form9" action="http://autocomplete5" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- test for onUsernameInput recipe testing -->
|
||||
<form id="form11" action="http://autocomplete7" onsubmit="return false;">
|
||||
<input type="text" name="1">
|
||||
<input type="text" name="2">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- tests <form>-less autocomplete -->
|
||||
<div id="form12">
|
||||
<input type="text" name="uname" id="uname">
|
||||
<input type="password" name="pword" id="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: multiple login autocomplete. **/
|
||||
|
||||
var uname = $_(1, "uname");
|
||||
var pword = $_(1, "pword");
|
||||
const shiftModifier = SpecialPowers.Ci.nsIDOMEvent.SHIFT_MASK;
|
||||
|
||||
// Restore the form to the default state.
|
||||
function restoreForm() {
|
||||
uname.value = "";
|
||||
pword.value = "";
|
||||
uname.focus();
|
||||
}
|
||||
|
||||
// Check for expected username/password in form.
|
||||
function checkACForm(expectedUsername, expectedPassword) {
|
||||
var formID = uname.parentNode.id;
|
||||
is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
|
||||
is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
|
||||
}
|
||||
|
||||
function sendFakeAutocompleteEvent(element) {
|
||||
var acEvent = document.createEvent("HTMLEvents");
|
||||
acEvent.initEvent("DOMAutoComplete", true, false);
|
||||
element.dispatchEvent(acEvent);
|
||||
}
|
||||
|
||||
function spinEventLoop() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["security.insecure_field_warning.contextual.enabled", false],
|
||||
["signon.autofillForms.http", true]]});
|
||||
listenForUnexpectedPopupShown();
|
||||
});
|
||||
|
||||
add_task(function* test_form1_initial_empty() {
|
||||
yield SimpleTest.promiseFocus(window);
|
||||
|
||||
// Make sure initial form is empty.
|
||||
checkACForm("", "");
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup is initially closed");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_menuitems() {
|
||||
yield SimpleTest.promiseFocus(window);
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
let results = yield shownPromise;
|
||||
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
|
||||
|
||||
let expectedMenuItems = ["tempuser1",
|
||||
"testuser2",
|
||||
"testuser3",
|
||||
"zzzuser4"];
|
||||
checkArrayValues(results, expectedMenuItems, "Check all menuitems are displayed correctly.");
|
||||
|
||||
checkACForm("", ""); // value shouldn't update just by selecting
|
||||
doKey("return"); // not "enter"!
|
||||
yield spinEventLoop(); // let focus happen
|
||||
checkACForm("", "");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_first_entry() {
|
||||
yield SimpleTest.promiseFocus(window);
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
|
||||
|
||||
doKey("down"); // first
|
||||
checkACForm("", ""); // value shouldn't update just by selecting
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("tempuser1", "temppass1");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_second_entry() {
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser2", "testpass2");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_third_entry() {
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("down"); // third
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser3", "testpass3");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_fourth_entry() {
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("down"); // third
|
||||
doKey("down"); // fourth
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("zzzuser4", "zzzpass4");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_wraparound_first_entry() {
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
yield spinEventLoop(); // let focus happen
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("down"); // third
|
||||
doKey("down"); // fourth
|
||||
doKey("down"); // deselects
|
||||
doKey("down"); // first
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("tempuser1", "temppass1");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_wraparound_up_last_entry() {
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("up"); // last (fourth)
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("zzzuser4", "zzzpass4");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_wraparound_down_up_up() {
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down"); // select first entry
|
||||
doKey("up"); // selects nothing!
|
||||
doKey("up"); // select last entry
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("zzzuser4", "zzzpass4");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_wraparound_up_last() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down");
|
||||
doKey("up"); // deselects
|
||||
doKey("up"); // last entry
|
||||
doKey("up");
|
||||
doKey("up");
|
||||
doKey("up"); // first entry
|
||||
doKey("up"); // deselects
|
||||
doKey("up"); // last entry
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("zzzuser4", "zzzpass4");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_fill_username_without_autofill_right() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Set first entry w/o triggering autocomplete
|
||||
doKey("down"); // first
|
||||
doKey("right");
|
||||
yield spinEventLoop();
|
||||
checkACForm("tempuser1", ""); // empty password
|
||||
});
|
||||
|
||||
add_task(function* test_form1_fill_username_without_autofill_left() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Set first entry w/o triggering autocomplete
|
||||
doKey("down"); // first
|
||||
doKey("left");
|
||||
checkACForm("tempuser1", ""); // empty password
|
||||
});
|
||||
|
||||
add_task(function* test_form1_pageup_first() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check first entry (page up)
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("page_up"); // first
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("tempuser1", "temppass1");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_pagedown_last() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
/* test 13 */
|
||||
// Check last entry (page down)
|
||||
doKey("down"); // first
|
||||
doKey("page_down"); // last
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("zzzuser4", "zzzpass4");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_untrusted_event() {
|
||||
restoreForm();
|
||||
yield spinEventLoop();
|
||||
|
||||
// Send a fake (untrusted) event.
|
||||
checkACForm("", "");
|
||||
uname.value = "zzzuser4";
|
||||
sendFakeAutocompleteEvent(uname);
|
||||
yield spinEventLoop();
|
||||
checkACForm("zzzuser4", "");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_delete() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// XXX tried sending character "t" before/during dropdown to test
|
||||
// filtering, but had no luck. Seemed like the character was getting lost.
|
||||
// Setting uname.value didn't seem to work either. This works with a human
|
||||
// driver, so I'm not sure what's up.
|
||||
|
||||
// Delete the first entry (of 4), "tempuser1"
|
||||
doKey("down");
|
||||
var numLogins;
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 5, "Correct number of logins before deleting one");
|
||||
|
||||
let countChangedPromise = notifyMenuChanged(3);
|
||||
var deletionPromise = promiseStorageChanged(["removeLogin"]);
|
||||
// On OS X, shift-backspace and shift-delete work, just delete does not.
|
||||
// On Win/Linux, shift-backspace does not work, delete and shift-delete do.
|
||||
doKey("delete", shiftModifier);
|
||||
yield deletionPromise;
|
||||
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 4, "Correct number of logins after deleting one");
|
||||
yield countChangedPromise;
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser2", "testpass2");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_first_after_deletion() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check the new first entry (of 3)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser2", "testpass2");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_delete_second() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Delete the second entry (of 3), "testuser3"
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 3, "Correct number of logins after deleting one");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("zzzuser4", "zzzpass4");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_first_after_deletion2() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check the new first entry (of 2)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser2", "testpass2");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_delete_last() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
/* test 54 */
|
||||
// Delete the last entry (of 2), "zzzuser4"
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 2, "Correct number of logins after deleting one");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser2", "testpass2");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_first_after_3_deletions() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check the only remaining entry
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser2", "testpass2");
|
||||
});
|
||||
|
||||
add_task(function* test_form1_check_only_entry_remaining() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
/* test 56 */
|
||||
// Delete the only remaining entry, "testuser2"
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkACForm("", "");
|
||||
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
|
||||
is(numLogins, 1, "Correct number of logins after deleting one");
|
||||
|
||||
// remove the login that's not shown in the list.
|
||||
setupScript.sendSyncMessage("removeLogin", "login0");
|
||||
});
|
||||
|
||||
/* Tests for single-user forms for ignoring autocomplete=off */
|
||||
add_task(function* test_form2() {
|
||||
// Turn our attention to form2
|
||||
uname = $_(2, "uname");
|
||||
pword = $_(2, "pword");
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
});
|
||||
|
||||
add_task(function* test_form3() {
|
||||
uname = $_(3, "uname");
|
||||
pword = $_(3, "pword");
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
});
|
||||
|
||||
add_task(function* test_form4() {
|
||||
uname = $_(4, "uname");
|
||||
pword = $_(4, "pword");
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
});
|
||||
|
||||
add_task(function* test_form5() {
|
||||
uname = $_(5, "uname");
|
||||
pword = $_(5, "pword");
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
});
|
||||
|
||||
add_task(function* test_form6() {
|
||||
// (this is a control, w/o autocomplete=off, to ensure the login
|
||||
// that was being suppressed would have been filled in otherwise)
|
||||
uname = $_(6, "uname");
|
||||
pword = $_(6, "pword");
|
||||
checkACForm("singleuser5", "singlepass5");
|
||||
});
|
||||
|
||||
add_task(function* test_form6_changeUsername() {
|
||||
// Test that the password field remains filled in after changing
|
||||
// the username.
|
||||
uname.focus();
|
||||
doKey("right");
|
||||
sendChar("X");
|
||||
// Trigger the 'blur' event on uname
|
||||
pword.focus();
|
||||
yield spinEventLoop();
|
||||
checkACForm("singleuser5X", "singlepass5");
|
||||
|
||||
setupScript.sendSyncMessage("removeLogin", "login5");
|
||||
});
|
||||
|
||||
add_task(function* test_form7() {
|
||||
uname = $_(7, "uname");
|
||||
pword = $_(7, "pword");
|
||||
checkACForm("", "");
|
||||
|
||||
// Insert a new username field into the form. We'll then make sure
|
||||
// that invoking the autocomplete doesn't try to fill the form.
|
||||
var newField = document.createElement("input");
|
||||
newField.setAttribute("type", "text");
|
||||
newField.setAttribute("name", "uname2");
|
||||
pword.parentNode.insertBefore(newField, pword);
|
||||
is($_(7, "uname2").value, "", "Verifying empty uname2");
|
||||
|
||||
// Delete login6B. It was created just to prevent filling in a login
|
||||
// automatically, removing it makes it more likely that we'll catch a
|
||||
// future regression with form filling here.
|
||||
setupScript.sendSyncMessage("removeLogin", "login6B");
|
||||
});
|
||||
|
||||
add_task(function* test_form7_2() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
// The form changes, so we expect the old username field to get the
|
||||
// selected autocomplete value, but neither the new username field nor
|
||||
// the password field should have any values filled in.
|
||||
yield spinEventLoop();
|
||||
checkACForm("form7user1", "");
|
||||
is($_(7, "uname2").value, "", "Verifying empty uname2");
|
||||
restoreForm(); // clear field, so reloading test doesn't fail
|
||||
|
||||
setupScript.sendSyncMessage("removeLogin", "login6A");
|
||||
});
|
||||
|
||||
add_task(function* test_form8() {
|
||||
uname = $_(8, "uname");
|
||||
pword = $_(8, "pword");
|
||||
checkACForm("form8user", "form8pass");
|
||||
restoreForm();
|
||||
});
|
||||
|
||||
add_task(function* test_form8_blur() {
|
||||
checkACForm("", "");
|
||||
// Focus the previous form to trigger a blur.
|
||||
$_(7, "uname").focus();
|
||||
});
|
||||
|
||||
add_task(function* test_form8_2() {
|
||||
checkACForm("", "");
|
||||
restoreForm();
|
||||
});
|
||||
|
||||
add_task(function* test_form8_3() {
|
||||
checkACForm("", "");
|
||||
setupScript.sendSyncMessage("removeLogin", "login7");
|
||||
});
|
||||
|
||||
add_task(function* test_form9_filtering() {
|
||||
// Turn our attention to form9 to test the dropdown - bug 497541
|
||||
uname = $_(9, "uname");
|
||||
pword = $_(9, "pword");
|
||||
uname.focus();
|
||||
let shownPromise = promiseACShown();
|
||||
sendString("form9userAB");
|
||||
yield shownPromise;
|
||||
|
||||
checkACForm("form9userAB", "");
|
||||
uname.focus();
|
||||
doKey("left");
|
||||
shownPromise = promiseACShown();
|
||||
sendChar("A");
|
||||
let results = yield shownPromise;
|
||||
|
||||
checkACForm("form9userAAB", "");
|
||||
checkArrayValues(results, ["form9userAAB"], "Check dropdown is updated after inserting 'A'");
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("form9userAAB", "form9pass");
|
||||
});
|
||||
|
||||
add_task(function* test_form9_autocomplete_cache() {
|
||||
// Note that this addLogin call will only be seen by the autocomplete
|
||||
// attempt for the sendChar if we do not successfully cache the
|
||||
// autocomplete results.
|
||||
setupScript.sendSyncMessage("addLogin", "login8C");
|
||||
uname.focus();
|
||||
let promise0 = notifyMenuChanged(0);
|
||||
sendChar("z");
|
||||
yield promise0;
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup shouldn't open");
|
||||
|
||||
// check that empty results are cached - bug 496466
|
||||
promise0 = notifyMenuChanged(0);
|
||||
sendChar("z");
|
||||
yield promise0;
|
||||
popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup stays closed due to cached empty result");
|
||||
});
|
||||
|
||||
add_task(function* test_form11_recipes() {
|
||||
yield loadRecipes({
|
||||
siteRecipes: [{
|
||||
"hosts": ["mochi.test:8888"],
|
||||
"usernameSelector": "input[name='1']",
|
||||
"passwordSelector": "input[name='2']"
|
||||
}],
|
||||
});
|
||||
uname = $_(11, "1");
|
||||
pword = $_(11, "2");
|
||||
|
||||
// First test DOMAutocomplete
|
||||
// Switch the password field to type=password so _fillForm marks the username
|
||||
// field for autocomplete.
|
||||
pword.type = "password";
|
||||
yield promiseFormsProcessed();
|
||||
restoreForm();
|
||||
checkACForm("", "");
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser10", "testpass10");
|
||||
|
||||
// Now test recipes with blur on the username field.
|
||||
restoreForm();
|
||||
checkACForm("", "");
|
||||
uname.value = "testuser10";
|
||||
checkACForm("testuser10", "");
|
||||
doKey("tab");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("testuser10", "testpass10");
|
||||
yield resetRecipes();
|
||||
});
|
||||
|
||||
add_task(function* test_form12_formless() {
|
||||
// Test form-less autocomplete
|
||||
uname = $_(12, "uname");
|
||||
pword = $_(12, "pword");
|
||||
restoreForm();
|
||||
checkACForm("", "");
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Trigger autocomplete
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
let processedPromise = promiseFormsProcessed();
|
||||
doKey("return"); // not "enter"!
|
||||
yield processedPromise;
|
||||
checkACForm("testuser", "testpass");
|
||||
});
|
||||
|
||||
add_task(function* test_form12_open_on_trusted_focus() {
|
||||
uname = $_(12, "uname");
|
||||
pword = $_(12, "pword");
|
||||
uname.value = "";
|
||||
pword.value = "";
|
||||
|
||||
// Move focus to the password field so we can test the first click on the
|
||||
// username field.
|
||||
pword.focus();
|
||||
checkACForm("", "");
|
||||
const firePrivEventPromise = new Promise((resolve) => {
|
||||
uname.addEventListener("click", (e) => {
|
||||
ok(e.isTrusted, "Ensure event is trusted");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
const shownPromise = promiseACShown();
|
||||
synthesizeMouseAtCenter(uname, {});
|
||||
yield firePrivEventPromise;
|
||||
yield shownPromise;
|
||||
doKey("down");
|
||||
const processedPromise = promiseFormsProcessed();
|
||||
doKey("return"); // not "enter"!
|
||||
yield processedPromise;
|
||||
checkACForm("testuser", "testpass");
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for html5 input types (email, tel, url, etc.)</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: html5 input types (email, tel, url, etc.)
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
|
||||
runInParent(function setup() {
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
let pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
|
||||
login1 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login2 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login3 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
login4 = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
|
||||
login1.init("http://mochi.test:8888", "http://bug600551-1", null,
|
||||
"testuser@example.com", "testpass1", "", "");
|
||||
login2.init("http://mochi.test:8888", "http://bug600551-2", null,
|
||||
"555-555-5555", "testpass2", "", "");
|
||||
login3.init("http://mochi.test:8888", "http://bug600551-3", null,
|
||||
"http://mozilla.org", "testpass3", "", "");
|
||||
login4.init("http://mochi.test:8888", "http://bug600551-4", null,
|
||||
"123456789", "testpass4", "", "");
|
||||
|
||||
pwmgr.addLogin(login1);
|
||||
pwmgr.addLogin(login2);
|
||||
pwmgr.addLogin(login3);
|
||||
pwmgr.addLogin(login4);
|
||||
});
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<form id="form1" action="http://bug600551-1">
|
||||
<input type="email" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form2" action="http://bug600551-2">
|
||||
<input type="tel" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form3" action="http://bug600551-3">
|
||||
<input type="url" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form4" action="http://bug600551-4">
|
||||
<input type="number" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- The following forms should not be filled with usernames -->
|
||||
<form id="form5" action="formtest.js">
|
||||
<input type="search" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form6" action="formtest.js">
|
||||
<input type="datetime" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form7" action="formtest.js">
|
||||
<input type="date" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form8" action="formtest.js">
|
||||
<input type="month" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form9" action="formtest.js">
|
||||
<input type="week" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form10" action="formtest.js">
|
||||
<input type="time" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form11" action="formtest.js">
|
||||
<input type="datetime-local" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form12" action="formtest.js">
|
||||
<input type="range" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="form13" action="formtest.js">
|
||||
<input type="color" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/* Test for Login Manager: 600551
|
||||
(Password manager not working with input type=email)
|
||||
*/
|
||||
function startTest() {
|
||||
checkForm(1, "testuser@example.com", "testpass1");
|
||||
checkForm(2, "555-555-5555", "testpass2");
|
||||
checkForm(3, "http://mozilla.org", "testpass3");
|
||||
checkForm(4, "123456789", "testpass4");
|
||||
|
||||
is($_(5, "uname").value, "", "type=search should not be considered a username");
|
||||
|
||||
is($_(6, "uname").value, "", "type=datetime should not be considered a username");
|
||||
|
||||
is($_(7, "uname").value, "", "type=date should not be considered a username");
|
||||
|
||||
is($_(8, "uname").value, "", "type=month should not be considered a username");
|
||||
|
||||
is($_(9, "uname").value, "", "type=week should not be considered a username");
|
||||
|
||||
is($_(10, "uname").value, "", "type=time should not be considered a username");
|
||||
|
||||
is($_(11, "uname").value, "", "type=datetime-local should not be considered a username");
|
||||
|
||||
is($_(12, "uname").value, "50", "type=range should not be considered a username");
|
||||
|
||||
is($_(13, "uname").value, "#000000", "type=color should not be considered a username");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=355063
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Test for Bug 355063</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<script type="application/javascript">
|
||||
/** Test for Bug 355063 **/
|
||||
|
||||
runChecksAfterCommonInit(function startTest() {
|
||||
info("startTest");
|
||||
// Password Manager's own listener should always have been added first, so
|
||||
// the test's listener should be called after the pwmgr's listener fills in
|
||||
// a login.
|
||||
//
|
||||
SpecialPowers.addChromeEventListener("DOMFormHasPassword", function eventFired() {
|
||||
SpecialPowers.removeChromeEventListener("DOMFormHasPassword", eventFired);
|
||||
var passField = $("p1");
|
||||
passField.addEventListener("input", checkForm);
|
||||
});
|
||||
addForm();
|
||||
});
|
||||
|
||||
function addForm() {
|
||||
info("addForm");
|
||||
var c = document.getElementById("content");
|
||||
c.innerHTML = "<form id=form1>form1: <input id=u1><input type=password id=p1></form><br>";
|
||||
}
|
||||
|
||||
function checkForm() {
|
||||
info("checkForm");
|
||||
var userField = document.getElementById("u1");
|
||||
var passField = document.getElementById("p1");
|
||||
is(userField.value, "testuser", "checking filled username");
|
||||
is(passField.value, "testpass", "checking filled password");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=355063">Mozilla Bug 355063</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
forms go here!
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test forms and logins without a username</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: forms and logins without a username.
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
runInParent(() => {
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
var pwmgr = Cc["@mozilla.org/login-manager;1"]
|
||||
.getService(Ci.nsILoginManager);
|
||||
|
||||
var nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo);
|
||||
|
||||
// pwlogin1 uses a unique formSubmitURL, to check forms where no other logins
|
||||
// will apply. pwlogin2 uses the normal formSubmitURL, so that we can test
|
||||
// forms with a mix of username and non-username logins that might apply.
|
||||
//
|
||||
// Note: pwlogin2 is deleted at the end of the test.
|
||||
|
||||
pwlogin1 = new nsLoginInfo();
|
||||
pwlogin2 = new nsLoginInfo();
|
||||
|
||||
pwlogin1.init("http://mochi.test:8888", "http://mochi.test:1111", null,
|
||||
"", "1234", "uname", "pword");
|
||||
|
||||
pwlogin2.init("http://mochi.test:8888", "http://mochi.test:8888", null,
|
||||
"", "1234", "uname", "pword");
|
||||
|
||||
|
||||
pwmgr.addLogin(pwlogin1);
|
||||
pwmgr.addLogin(pwlogin2);
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
<!-- simple form: no username field, 1 password field -->
|
||||
<form id='form1' action='http://mochi.test:1111/formtest.js'> 1
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- simple form: no username field, 2 password fields -->
|
||||
<form id='form2' action='http://mochi.test:1111/formtest.js'> 2
|
||||
<input type='password' name='pname1' value=''>
|
||||
<input type='password' name='pname2' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- simple form: no username field, 3 password fields -->
|
||||
<form id='form3' action='http://mochi.test:1111/formtest.js'> 3
|
||||
<input type='password' name='pname1' value=''>
|
||||
<input type='password' name='pname2' value=''>
|
||||
<input type='password' name='pname3' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- 4 password fields, should be ignored. -->
|
||||
<form id='form4' action='http://mochi.test:1111/formtest.js'> 4
|
||||
<input type='password' name='pname1' value=''>
|
||||
<input type='password' name='pname2' value=''>
|
||||
<input type='password' name='pname3' value=''>
|
||||
<input type='password' name='pname4' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- 1 username field -->
|
||||
<form id='form5' action='http://mochi.test:1111/formtest.js'> 5
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
|
||||
<!-- 1 username field, with a value set -->
|
||||
<form id='form6' action='http://mochi.test:1111/formtest.js'> 6
|
||||
<input type='text' name='uname' value='someuser'>
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
(The following forms have 2 potentially-matching logins, on is
|
||||
password-only, the other is username+password)
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!-- 1 username field, with value set. Fill in the matching U+P login -->
|
||||
<form id='form7' action='formtest.js'> 7
|
||||
<input type='text' name='uname' value='testuser'>
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- 1 username field, with value set. Don't fill in U+P login-->
|
||||
<form id='form8' action='formtest.js'> 8
|
||||
<input type='text' name='uname' value='someuser'>
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- 1 username field, too small for U+P login -->
|
||||
<form id='form9' action='formtest.js'> 9
|
||||
<input type='text' name='uname' value='' maxlength="4">
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- 1 username field, too small for U+P login -->
|
||||
<form id='form10' action='formtest.js'> 10
|
||||
<input type='text' name='uname' value='' maxlength="0">
|
||||
<input type='password' name='pname' value=''>
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- 1 username field, too small for U+P login -->
|
||||
<form id='form11' action='formtest.js'> 11
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='' maxlength="4">
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- 1 username field, too small for either login -->
|
||||
<form id='form12' action='formtest.js'> 12
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='' maxlength="1">
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- 1 username field, too small for either login -->
|
||||
<form id='form13' action='formtest.js'> 13
|
||||
<input type='text' name='uname' value=''>
|
||||
<input type='password' name='pname' value='' maxlength="0">
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: password-only logins **/
|
||||
function startTest() {
|
||||
|
||||
checkForm(1, "1234");
|
||||
checkForm(2, "1234", "");
|
||||
checkForm(3, "1234", "", "");
|
||||
checkUnmodifiedForm(4);
|
||||
|
||||
checkForm(5, "", "1234");
|
||||
checkForm(6, "someuser", "");
|
||||
|
||||
checkForm(7, "testuser", "testpass");
|
||||
checkForm(8, "someuser", "");
|
||||
|
||||
checkForm(9, "", "1234");
|
||||
checkForm(10, "", "1234");
|
||||
checkForm(11, "", "1234");
|
||||
|
||||
checkUnmodifiedForm(12);
|
||||
checkUnmodifiedForm(13);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test bug 627616 related to proxy authentication</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var Ci = SpecialPowers.Ci;
|
||||
|
||||
function makeXHR(expectedStatus, expectedText, extra) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "authenticate.sjs?" +
|
||||
"proxy_user=proxy_user&" +
|
||||
"proxy_pass=proxy_pass&" +
|
||||
"proxy_realm=proxy_realm&" +
|
||||
"user=user1name&" +
|
||||
"pass=user1pass&" +
|
||||
"realm=mochirealm&" +
|
||||
extra || "");
|
||||
xhr.onloadend = function() {
|
||||
is(xhr.status, expectedStatus, "xhr.status");
|
||||
is(xhr.statusText, expectedText, "xhr.statusText");
|
||||
runNextTest();
|
||||
};
|
||||
return xhr;
|
||||
}
|
||||
|
||||
function testNonAnonymousCredentials() {
|
||||
var xhr = makeXHR(200, "OK");
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function testAnonymousCredentials() {
|
||||
// Test that an anonymous request correctly performs proxy authentication
|
||||
var xhr = makeXHR(401, "Authentication required");
|
||||
SpecialPowers.wrap(xhr).channel.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function testAnonymousNoAuth() {
|
||||
// Next, test that an anonymous request still does not include any non-proxy
|
||||
// authentication headers.
|
||||
var xhr = makeXHR(200, "Authorization header not found", "anonymous=1");
|
||||
SpecialPowers.wrap(xhr).channel.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
var gExpectedDialogs = 0;
|
||||
var gCurrentTest;
|
||||
function runNextTest() {
|
||||
is(gExpectedDialogs, 0, "received expected number of auth dialogs");
|
||||
mm.sendAsyncMessage("prepareForNextTest");
|
||||
mm.addMessageListener("prepareForNextTestDone", function prepared(msg) {
|
||||
mm.removeMessageListener("prepareForNextTestDone", prepared);
|
||||
if (pendingTests.length > 0) {
|
||||
({expectedDialogs: gExpectedDialogs,
|
||||
test: gCurrentTest} = pendingTests.shift());
|
||||
gCurrentTest.call(this);
|
||||
} else {
|
||||
mm.sendAsyncMessage("cleanup");
|
||||
mm.addMessageListener("cleanupDone", () => {
|
||||
// mm.destroy() is called as a cleanup function by runInParent(), no
|
||||
// need to do it here.
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var pendingTests = [{expectedDialogs: 2, test: testNonAnonymousCredentials},
|
||||
{expectedDialogs: 1, test: testAnonymousCredentials},
|
||||
{expectedDialogs: 0, test: testAnonymousNoAuth}];
|
||||
|
||||
let mm = runInParent(() => {
|
||||
const { classes: parentCc, interfaces: parentCi, utils: parentCu } = Components;
|
||||
|
||||
parentCu.import("resource://gre/modules/Services.jsm");
|
||||
parentCu.import("resource://gre/modules/NetUtil.jsm");
|
||||
parentCu.import("resource://gre/modules/Timer.jsm");
|
||||
parentCu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: "http://example.com",
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
|
||||
let pps = parentCc["@mozilla.org/network/protocol-proxy-service;1"].
|
||||
getService(parentCi.nsIProtocolProxyService);
|
||||
pps.asyncResolve(channel, 0, {
|
||||
onProxyAvailable(req, uri, pi, status) {
|
||||
let mozproxy = "moz-proxy://" + pi.host + ":" + pi.port;
|
||||
let login = parentCc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(parentCi.nsILoginInfo);
|
||||
login.init(mozproxy, null, "proxy_realm", "proxy_user", "proxy_pass",
|
||||
"", "");
|
||||
Services.logins.addLogin(login);
|
||||
|
||||
let login2 = parentCc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(parentCi.nsILoginInfo);
|
||||
login2.init("http://mochi.test:8888", null, "mochirealm", "user1name",
|
||||
"user1pass", "", "");
|
||||
Services.logins.addLogin(login2);
|
||||
|
||||
sendAsyncMessage("setupDone");
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([parentCi.nsIProtocolProxyCallback]),
|
||||
});
|
||||
|
||||
addMessageListener("prepareForNextTest", message => {
|
||||
parentCc["@mozilla.org/network/http-auth-manager;1"].
|
||||
getService(parentCi.nsIHttpAuthManager).
|
||||
clearAll();
|
||||
sendAsyncMessage("prepareForNextTestDone");
|
||||
});
|
||||
|
||||
let dialogObserverTopic = "common-dialog-loaded";
|
||||
|
||||
function dialogObserver(subj, topic, data) {
|
||||
subj.Dialog.ui.prompt.document.documentElement.acceptDialog();
|
||||
sendAsyncMessage("promptAccepted");
|
||||
}
|
||||
|
||||
Services.obs.addObserver(dialogObserver, dialogObserverTopic, false);
|
||||
|
||||
addMessageListener("cleanup", message => {
|
||||
Services.obs.removeObserver(dialogObserver, dialogObserverTopic);
|
||||
sendAsyncMessage("cleanupDone");
|
||||
});
|
||||
});
|
||||
|
||||
mm.addMessageListener("promptAccepted", msg => {
|
||||
gExpectedDialogs--;
|
||||
});
|
||||
mm.addMessageListener("setupDone", msg => {
|
||||
runNextTest();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=776171
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 776171 related to HTTP auth</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="startTest()">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/**
|
||||
* This test checks we correctly ignore authentication entry
|
||||
* for a subpath and use creds from the URL when provided when XHR
|
||||
* is used with filled user name and password.
|
||||
*
|
||||
* 1. connect auth2/authenticate.sjs that expects user1:pass1 password
|
||||
* 2. connect a dummy URL at the same path
|
||||
* 3. connect authenticate.sjs that again expects user1:pass1 password
|
||||
* in this case, however, we have an entry without an identity
|
||||
* for this path (that is a parent for auth2 path in the first step)
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function doxhr(URL, user, pass, next) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
if (user && pass)
|
||||
xhr.open("POST", URL, true, user, pass);
|
||||
else
|
||||
xhr.open("POST", URL, true);
|
||||
xhr.onload = function() {
|
||||
is(xhr.status, 200, "Got status 200");
|
||||
next();
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
ok(false, "request passed");
|
||||
finishTest();
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
doxhr("auth2/authenticate.sjs?user=user1&pass=pass1&realm=realm1", "user1", "pass1", function() {
|
||||
doxhr("auth2", null, null, function() {
|
||||
doxhr("authenticate.sjs?user=user1&pass=pass1&realm=realm1", "user1", "pass1", SimpleTest.finish);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autocomplete due to multiple matching logins</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: autocomplete due to multiple matching logins
|
||||
|
||||
<script>
|
||||
runChecksAfterCommonInit(false);
|
||||
|
||||
SpecialPowers.loadChromeScript(function addLogins() {
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Create some logins just for this form, since we'll be deleting them.
|
||||
var nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
|
||||
var login0 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"name", "pass", "uname", "pword");
|
||||
|
||||
var login1 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"Name", "Pass", "uname", "pword");
|
||||
|
||||
var login2 = new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
|
||||
"USER", "PASS", "uname", "pword");
|
||||
|
||||
try {
|
||||
Services.logins.addLogin(login0);
|
||||
Services.logins.addLogin(login1);
|
||||
Services.logins.addLogin(login2);
|
||||
} catch (e) {
|
||||
assert.ok(false, "addLogin threw: " + e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
|
||||
<!-- form1 tests multiple matching logins -->
|
||||
<form id="form1" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: autocomplete due to multiple matching logins **/
|
||||
|
||||
var uname = $_(1, "uname");
|
||||
var pword = $_(1, "pword");
|
||||
|
||||
// Restore the form to the default state.
|
||||
function restoreForm() {
|
||||
uname.value = "";
|
||||
pword.value = "";
|
||||
uname.focus();
|
||||
}
|
||||
|
||||
// Check for expected username/password in form.
|
||||
function checkACForm(expectedUsername, expectedPassword) {
|
||||
var formID = uname.parentNode.id;
|
||||
is(uname.value, expectedUsername, "Checking " + formID + " username");
|
||||
is(pword.value, expectedPassword, "Checking " + formID + " password");
|
||||
}
|
||||
|
||||
add_task(function* test_empty_first_entry() {
|
||||
/* test 1 */
|
||||
// Make sure initial form is empty.
|
||||
checkACForm("", "");
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
let popupState = yield getPopupState();
|
||||
is(popupState.open, false, "Check popup is initially closed");
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down");
|
||||
let results = yield shownPromise;
|
||||
popupState = yield getPopupState();
|
||||
is(popupState.selectedIndex, -1, "Check no entries are selected");
|
||||
checkArrayValues(results, ["name", "Name", "USER"], "initial");
|
||||
|
||||
// Check first entry
|
||||
let index0Promise = notifySelectedIndex(0);
|
||||
doKey("down");
|
||||
yield index0Promise;
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("name", "pass");
|
||||
});
|
||||
|
||||
add_task(function* test_empty_second_entry() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("return"); // not "enter"!
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("Name", "Pass");
|
||||
});
|
||||
|
||||
add_task(function* test_empty_third_entry() {
|
||||
restoreForm();
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
doKey("down"); // first
|
||||
doKey("down"); // second
|
||||
doKey("down"); // third
|
||||
doKey("return");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("USER", "PASS");
|
||||
});
|
||||
|
||||
add_task(function* test_preserve_matching_username_case() {
|
||||
restoreForm();
|
||||
uname.value = "user";
|
||||
let shownPromise = promiseACShown();
|
||||
doKey("down"); // open
|
||||
yield shownPromise;
|
||||
|
||||
// Check that we don't clobber user-entered text when tabbing away
|
||||
// (even with no autocomplete entry selected)
|
||||
doKey("tab");
|
||||
yield promiseFormsProcessed();
|
||||
checkACForm("user", "PASS");
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for considering form action</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: Bug 360493
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<!-- normal form with normal relative action. -->
|
||||
<form id="form1" action="formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- fully specify the action URL -->
|
||||
<form id="form2" action="http://mochi.test:8888/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- fully specify the action URL, and change the path -->
|
||||
<form id="form3" action="http://mochi.test:8888/zomg/wtf/bbq/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- fully specify the action URL, and change the path and filename -->
|
||||
<form id="form4" action="http://mochi.test:8888/zomg/wtf/bbq/passwordmgr/test/not_a_test.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- specify the action URL relative to the current document-->
|
||||
<form id="form5" action="./formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- specify the action URL relative to the current server -->
|
||||
<form id="form6" action="/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Change the method from get to post -->
|
||||
<form id="form7" action="formtest.js" method="POST">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Blank action URL specified -->
|
||||
<form id="form8" action="">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- |action| attribute entirely missing -->
|
||||
<form id="form9" >
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- action url as javascript -->
|
||||
<form id="form10" action="javascript:alert('this form is not submitted so this alert should not be invoked');">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- TODO: action=IP.ADDRESS instead of HOSTNAME? -->
|
||||
<!-- TODO: test with |base href="http://othersite//"| ? -->
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: 360493 (Cross-Site Forms + Password
|
||||
Manager = Security Failure) **/
|
||||
|
||||
// This test is designed to make sure variations on the form's |action|
|
||||
// and |method| continue to work with the fix for 360493.
|
||||
|
||||
function startTest() {
|
||||
for (var i = 1; i <= 9; i++) {
|
||||
// Check form i
|
||||
is($_(i, "uname").value, "testuser", "Checking for filled username " + i);
|
||||
is($_(i, "pword").value, "testpass", "Checking for filled password " + i);
|
||||
}
|
||||
|
||||
// The login's formSubmitURL isn't "javascript:", so don't fill it in.
|
||||
isnot($_(10, "uname"), "testuser", "Checking username w/ JS action URL");
|
||||
isnot($_(10, "pword"), "testpass", "Checking password w/ JS action URL");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for considering form action</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: Bug 360493
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<!-- The tests in this page exercise things that shouldn't work. -->
|
||||
|
||||
<!-- Change port # of action URL from 8888 to 7777 -->
|
||||
<form id="form1" action="http://localhost:7777/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- No port # in action URL -->
|
||||
<form id="form2" action="http://localhost/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Change protocol from http:// to ftp://, include the expected 8888 port # -->
|
||||
<form id="form3" action="ftp://localhost:8888/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Change protocol from http:// to ftp://, no port # specified -->
|
||||
<form id="form4" action="ftp://localhost/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Try a weird URL. -->
|
||||
<form id="form5" action="about:blank">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Try a weird URL. (If the normal embedded action URL doesn't work, that should mean other URLs won't either) -->
|
||||
<form id="form6" action="view-source:http://localhost:8888/tests/toolkit/components/passwordmgr/test/formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Try a weird URL. -->
|
||||
<form id="form7" action="view-source:formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Action URL points to a different host (this is the archetypical exploit) -->
|
||||
<form id="form8" action="http://www.cnn.com/">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Action URL points to a different host, user field prefilled -->
|
||||
<form id="form9" action="http://www.cnn.com/">
|
||||
<input type="text" name="uname" value="testuser">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
<!-- Try wrapping a evil form around a good form, to see if we can confuse the parser. -->
|
||||
<form id="form10-A" action="http://www.cnn.com/">
|
||||
<form id="form10-B" action="formtest.js">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit (inner)</button>
|
||||
<button type="reset"> Reset (inner)</button>
|
||||
</form>
|
||||
<button type="submit" id="neutered_submit10">Submit (outer)</button>
|
||||
<button type="reset">Reset (outer)</button>
|
||||
</form>
|
||||
|
||||
<!-- Try wrapping a good form around an evil form, to see if we can confuse the parser. -->
|
||||
<form id="form11-A" action="formtest.js">
|
||||
<form id="form11-B" action="http://www.cnn.com/">
|
||||
<input type="text" name="uname">
|
||||
<input type="password" name="pword">
|
||||
|
||||
<button type="submit">Submit (inner)</button>
|
||||
<button type="reset"> Reset (inner)</button>
|
||||
</form>
|
||||
<button type="submit" id="neutered_submit11">Submit (outer)</button>
|
||||
<button type="reset">Reset (outer)</button>
|
||||
</form>
|
||||
|
||||
<!-- TODO: probably should have some accounts which have no port # in the action url. JS too. And different host/proto. -->
|
||||
<!-- TODO: www.site.com vs. site.com? -->
|
||||
<!-- TODO: foo.site.com vs. bar.site.com? -->
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: 360493 (Cross-Site Forms + Password Manager = Security Failure) **/
|
||||
|
||||
function startTest() {
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
// Check form i
|
||||
is($_(i, "uname").value, "", "Checking for unfilled username " + i);
|
||||
is($_(i, "pword").value, "", "Checking for unfilled password " + i);
|
||||
}
|
||||
|
||||
is($_(9, "uname").value, "testuser", "Checking for unmodified username 9");
|
||||
is($_(9, "pword").value, "", "Checking for unfilled password 9");
|
||||
|
||||
is($_("10-A", "uname").value, "", "Checking for unfilled username 10A");
|
||||
is($_("10-A", "pword").value, "", "Checking for unfilled password 10A");
|
||||
|
||||
// The DOM indicates this form could be filled, as the evil inner form
|
||||
// is discarded. And yet pwmgr seems not to fill it. Not sure why.
|
||||
todo(false, "Mangled form combo not being filled when maybe it could be?");
|
||||
is($_("11-A", "uname").value, "testuser", "Checking filled username 11A");
|
||||
is($_("11-A", "pword").value, "testpass", "Checking filled password 11A");
|
||||
|
||||
// Verify this by making sure there are no extra forms in the document, and
|
||||
// that the submit button for the neutered forms don't do anything.
|
||||
// If the test finds extra forms the submit() causes the test to timeout, then
|
||||
// there may be a security issue.
|
||||
is(document.forms.length, 11, "Checking for unexpected forms");
|
||||
$("neutered_submit10").click();
|
||||
$("neutered_submit11").click();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test forms with a JS submit action</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Login Manager test: form with JS submit action
|
||||
<script>
|
||||
runChecksAfterCommonInit(() => startTest());
|
||||
|
||||
runInParent(function setup() {
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let jslogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
|
||||
jslogin.init("http://mochi.test:8888", "javascript:", null,
|
||||
"jsuser", "jspass123", "uname", "pword");
|
||||
Services.logins.addLogin(jslogin);
|
||||
});
|
||||
|
||||
/** Test for Login Manager: JS action URL **/
|
||||
|
||||
function startTest() {
|
||||
checkForm(1, "jsuser", "jspass123");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
<form id='form1' action='javascript:alert("never shows")'> 1
|
||||
<input name="uname">
|
||||
<input name="pword" type="password">
|
||||
|
||||
<button type='submit'>Submit</button>
|
||||
<button type='reset'> Reset </button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test autofilling of fields outside of a form</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
// Tell the parent to setup test logins.
|
||||
chromeScript.sendAsyncMessage("setupParent", { selfFilling: true });
|
||||
});
|
||||
});
|
||||
|
||||
let doneSetupPromise = new Promise(resolve => {
|
||||
// When the setup is done, load a recipe for this test.
|
||||
chromeScript.addMessageListener("doneSetup", function doneSetup() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* setup() {
|
||||
info("Waiting for loads and setup");
|
||||
yield doneSetupPromise;
|
||||
|
||||
yield loadRecipes({
|
||||
siteRecipes: [{
|
||||
hosts: ["mochi.test:8888"],
|
||||
usernameSelector: "input[name='recipeuname']",
|
||||
passwordSelector: "input[name='recipepword']",
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const DEFAULT_ORIGIN = "http://mochi.test:8888";
|
||||
const TESTCASES = [
|
||||
{
|
||||
// Inputs
|
||||
document: `<input type=password>`,
|
||||
|
||||
// Expected outputs
|
||||
expectedInputValues: ["testpass"],
|
||||
},
|
||||
{
|
||||
document: `<input>
|
||||
<input type=password>`,
|
||||
expectedInputValues: ["testuser", "testpass"],
|
||||
},
|
||||
{
|
||||
document: `<input>
|
||||
<input type=password>
|
||||
<input type=password>`,
|
||||
expectedInputValues: ["testuser", "testpass", ""],
|
||||
},
|
||||
{
|
||||
document: `<input>
|
||||
<input type=password>
|
||||
<input type=password>
|
||||
<input type=password>`,
|
||||
expectedInputValues: ["testuser", "testpass", "", ""],
|
||||
},
|
||||
{
|
||||
document: `<input>
|
||||
<input type=password form="form1">
|
||||
<input type=password>
|
||||
<form id="form1">
|
||||
<input>
|
||||
<input type=password>
|
||||
</form>`,
|
||||
expectedFormCount: 2,
|
||||
expectedInputValues: ["testuser", "testpass", "testpass", "", ""],
|
||||
},
|
||||
{
|
||||
document: `<!-- formless password field selector recipe test -->
|
||||
<input>
|
||||
<input type=password>
|
||||
<input>
|
||||
<input type=password name="recipepword">`,
|
||||
expectedInputValues: ["", "", "testuser", "testpass"],
|
||||
},
|
||||
{
|
||||
document: `<!-- formless username and password field selector recipe test -->
|
||||
<input name="recipeuname">
|
||||
<input>
|
||||
<input type=password>
|
||||
<input type=password name="recipepword">`,
|
||||
expectedInputValues: ["testuser", "", "", "testpass"],
|
||||
},
|
||||
{
|
||||
document: `<!-- form and formless recipe field selector test -->
|
||||
<input name="recipeuname">
|
||||
<input>
|
||||
<input type=password form="form1"> <!-- not filled since recipe affects both FormLikes -->
|
||||
<input type=password>
|
||||
<input type=password name="recipepword">
|
||||
<form id="form1">
|
||||
<input>
|
||||
<input type=password>
|
||||
</form>`,
|
||||
expectedFormCount: 2,
|
||||
expectedInputValues: ["testuser", "", "", "", "testpass", "", ""],
|
||||
},
|
||||
];
|
||||
|
||||
add_task(function* test() {
|
||||
let loginFrame = document.getElementById("loginFrame");
|
||||
let frameDoc = loginFrame.contentWindow.document;
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
info("Starting testcase: " + JSON.stringify(tc));
|
||||
|
||||
let numFormLikesExpected = tc.expectedFormCount || 1;
|
||||
|
||||
let processedFormPromise = promiseFormsProcessed(numFormLikesExpected);
|
||||
|
||||
frameDoc.documentElement.innerHTML = tc.document;
|
||||
info("waiting for " + numFormLikesExpected + " processed form(s)");
|
||||
yield processedFormPromise;
|
||||
|
||||
let testInputs = frameDoc.documentElement.querySelectorAll("input");
|
||||
is(testInputs.length, tc.expectedInputValues.length, "Check number of inputs");
|
||||
for (let i = 0; i < tc.expectedInputValues.length; i++) {
|
||||
let expectedValue = tc.expectedInputValues[i];
|
||||
is(testInputs[i].value, expectedValue,
|
||||
"Check expected input value " + i + ": " + expectedValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<iframe id="loginFrame" src="http://mochi.test:8888/tests/toolkit/components/passwordmgr/test/blank.html"></iframe>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test capturing of fields outside of a form</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* setup() {
|
||||
info("Waiting for page and frame loads");
|
||||
yield loadPromise;
|
||||
|
||||
yield loadRecipes({
|
||||
siteRecipes: [{
|
||||
hosts: ["mochi.test:8888"],
|
||||
usernameSelector: "input[name='recipeuname']",
|
||||
passwordSelector: "input[name='recipepword']",
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
const DEFAULT_ORIGIN = "http://mochi.test:8888";
|
||||
const TESTCASES = [
|
||||
{
|
||||
// Inputs
|
||||
document: `<input type=password value="pass1">`,
|
||||
inputIndexForFormLike: 0,
|
||||
|
||||
// Expected outputs similar to RemoteLogins:onFormSubmit
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: null,
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">`,
|
||||
inputIndexForFormLike: 0,
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">`,
|
||||
inputIndexForFormLike: 1,
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">`,
|
||||
inputIndexForFormLike: 2,
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass2",
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">
|
||||
<input type=password value="pass2">`,
|
||||
inputIndexForFormLike: 3,
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass2",
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="user2" form="form1">
|
||||
<input type=password value="pass1">
|
||||
<form id="form1">
|
||||
<input value="user3">
|
||||
<input type=password value="pass2">
|
||||
</form>`,
|
||||
inputIndexForFormLike: 2,
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<!-- recipe field override -->
|
||||
<input name="recipeuname" value="username from recipe">
|
||||
<input value="default field username">
|
||||
<input type=password value="pass1">
|
||||
<input name="recipepword" type=password value="pass2">`,
|
||||
inputIndexForFormLike: 2,
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "username from recipe",
|
||||
newPasswordFieldValue: "pass2",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
];
|
||||
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test() {
|
||||
let loginFrame = document.getElementById("loginFrame");
|
||||
let frameDoc = loginFrame.contentWindow.document;
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
info("Starting testcase: " + JSON.stringify(tc));
|
||||
frameDoc.documentElement.innerHTML = tc.document;
|
||||
let inputForFormLike = frameDoc.querySelectorAll("input")[tc.inputIndexForFormLike];
|
||||
|
||||
let formLike = LoginFormFactory.createFromField(inputForFormLike);
|
||||
|
||||
info("Calling _onFormSubmit with FormLike");
|
||||
let processedPromise = getSubmitMessage();
|
||||
LoginManagerContent._onFormSubmit(formLike);
|
||||
|
||||
let submittedResult = yield processedPromise;
|
||||
|
||||
// Check data sent via RemoteLogins:onFormSubmit
|
||||
is(submittedResult.hostname, tc.hostname, "Check hostname");
|
||||
is(submittedResult.formSubmitURL, tc.formSubmitURL, "Check formSubmitURL");
|
||||
|
||||
if (tc.usernameFieldValue === null) {
|
||||
is(submittedResult.usernameField, tc.usernameFieldValue, "Check usernameField");
|
||||
} else {
|
||||
is(submittedResult.usernameField.value, tc.usernameFieldValue, "Check usernameField");
|
||||
}
|
||||
|
||||
is(submittedResult.newPasswordField.value, tc.newPasswordFieldValue, "Check newPasswordFieldValue");
|
||||
|
||||
if (tc.oldPasswordFieldValue === null) {
|
||||
is(submittedResult.oldPasswordField, tc.oldPasswordFieldValue, "Check oldPasswordFieldValue");
|
||||
} else {
|
||||
is(submittedResult.oldPasswordField.value, tc.oldPasswordFieldValue, "Check oldPasswordFieldValue");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<iframe id="loginFrame" src="http://mochi.test:8888/tests/toolkit/components/passwordmgr/test/blank.html"></iframe>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test capturing of fields outside of a form due to navigation</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["signon.formlessCapture.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
info("Waiting for page and frame loads");
|
||||
yield loadPromise;
|
||||
|
||||
yield loadRecipes({
|
||||
siteRecipes: [{
|
||||
hosts: ["test1.mochi.test:8888"],
|
||||
usernameSelector: "input[name='recipeuname']",
|
||||
passwordSelector: "input[name='recipepword']",
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
const DEFAULT_ORIGIN = "http://test1.mochi.test:8888";
|
||||
const SCRIPTS = {
|
||||
PUSHSTATE: `history.pushState({}, "Pushed state", "?pushed");`,
|
||||
WINDOW_LOCATION: `window.location = "data:text/html;charset=utf-8,window.location";`,
|
||||
};
|
||||
const TESTCASES = [
|
||||
{
|
||||
// Inputs
|
||||
document: `<input type=password value="pass1">`,
|
||||
|
||||
// Expected outputs similar to RemoteLogins:onFormSubmit
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: null,
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">`,
|
||||
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">`,
|
||||
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass2",
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">
|
||||
<input type=password value="pass2">`,
|
||||
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass2",
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input value="user1">
|
||||
<input type=password value="user2" form="form1">
|
||||
<input type=password value="pass1">
|
||||
<form id="form1">
|
||||
<input value="user3">
|
||||
<input type=password value="pass2">
|
||||
</form>`,
|
||||
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "user1",
|
||||
newPasswordFieldValue: "pass1",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<!-- recipe field override -->
|
||||
<input name="recipeuname" value="username from recipe">
|
||||
<input value="default field username">
|
||||
<input type=password value="pass1">
|
||||
<input name="recipepword" type=password value="pass2">`,
|
||||
|
||||
hostname: DEFAULT_ORIGIN,
|
||||
formSubmitURL: DEFAULT_ORIGIN,
|
||||
usernameFieldValue: "username from recipe",
|
||||
newPasswordFieldValue: "pass2",
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
];
|
||||
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test() {
|
||||
let loginFrame = document.getElementById("loginFrame");
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
for (let scriptName of Object.keys(SCRIPTS)) {
|
||||
info("Starting testcase with script " + scriptName + ": " + JSON.stringify(tc));
|
||||
let loadedPromise = new Promise((resolve) => {
|
||||
loginFrame.addEventListener("load", function frameLoaded() {
|
||||
loginFrame.removeEventListener("load", frameLoaded);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
loginFrame.src = DEFAULT_ORIGIN + "/tests/toolkit/components/passwordmgr/test/mochitest/blank.html";
|
||||
yield loadedPromise;
|
||||
|
||||
let frameDoc = SpecialPowers.wrap(loginFrame.contentWindow).document;
|
||||
frameDoc.documentElement.innerHTML = tc.document;
|
||||
// Wait for the form to be processed before trying to submit.
|
||||
yield promiseFormsProcessed();
|
||||
let processedPromise = getSubmitMessage();
|
||||
info("Running " + scriptName + " script to cause a submission");
|
||||
frameDoc.defaultView.eval(SCRIPTS[scriptName]);
|
||||
|
||||
let submittedResult = yield processedPromise;
|
||||
|
||||
// Check data sent via RemoteLogins:onFormSubmit
|
||||
is(submittedResult.hostname, tc.hostname, "Check hostname");
|
||||
is(submittedResult.formSubmitURL, tc.formSubmitURL, "Check formSubmitURL");
|
||||
|
||||
if (tc.usernameFieldValue === null) {
|
||||
is(submittedResult.usernameField, tc.usernameFieldValue, "Check usernameField");
|
||||
} else {
|
||||
is(submittedResult.usernameField.value, tc.usernameFieldValue, "Check usernameField");
|
||||
}
|
||||
|
||||
is(submittedResult.newPasswordField.value, tc.newPasswordFieldValue, "Check newPasswordFieldValue");
|
||||
|
||||
if (tc.oldPasswordFieldValue === null) {
|
||||
is(submittedResult.oldPasswordField, tc.oldPasswordFieldValue, "Check oldPasswordFieldValue");
|
||||
} else {
|
||||
is(submittedResult.oldPasswordField.value, tc.oldPasswordFieldValue, "Check oldPasswordFieldValue");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<iframe id="loginFrame" src="http://test1.mochi.test:8888/tests/toolkit/components/passwordmgr/test/mochitest/blank.html"></iframe>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test no capturing of fields outside of a form due to navigation</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<script src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.8">
|
||||
const LMCBackstagePass = SpecialPowers.Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
|
||||
SimpleTest.requestFlakyTimeout("Testing that a message doesn't arrive");
|
||||
|
||||
let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
|
||||
|
||||
let loadPromise = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("loginFrame").addEventListener("load", (evt) => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function submissionProcessed(...args) {
|
||||
ok(false, "No formSubmissionProcessed should occur in this test");
|
||||
info("got: " + JSON.stringify(args));
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["signon.formlessCapture.enabled", true],
|
||||
],
|
||||
});
|
||||
|
||||
info("Waiting for page and frame loads");
|
||||
yield loadPromise;
|
||||
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", submissionProcessed);
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", submissionProcessed);
|
||||
});
|
||||
});
|
||||
|
||||
const DEFAULT_ORIGIN = "http://test1.mochi.test:8888";
|
||||
const SCRIPTS = {
|
||||
PUSHSTATE: `history.pushState({}, "Pushed state", "?pushed");`,
|
||||
WINDOW_LOCATION: `window.location = "data:text/html;charset=utf-8,window.location";`,
|
||||
WINDOW_LOCATION_RELOAD: `window.location.reload();`,
|
||||
HISTORY_BACK: `history.back();`,
|
||||
HISTORY_GO_MINUS1: `history.go(-1);`,
|
||||
};
|
||||
const TESTCASES = [
|
||||
// Begin test cases that shouldn't trigger capture.
|
||||
{
|
||||
// For now we don't trigger upon navigation if <form> is used.
|
||||
document: `<form><input type=password value="pass1"></form>`,
|
||||
},
|
||||
{
|
||||
// Empty password field
|
||||
document: `<input type=password value="">`,
|
||||
},
|
||||
{
|
||||
// Test with an input that would normally be captured but with SCRIPTS that
|
||||
// shouldn't trigger capture.
|
||||
document: `<input type=password value="pass2">`,
|
||||
wouldCapture: true,
|
||||
},
|
||||
];
|
||||
|
||||
add_task(function* test() {
|
||||
let loginFrame = document.getElementById("loginFrame");
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
for (let scriptName of Object.keys(SCRIPTS)) {
|
||||
if (tc.wouldCapture && ["PUSHSTATE", "WINDOW_LOCATION"].includes(scriptName)) {
|
||||
// Don't run scripts that should actually capture for this testcase.
|
||||
continue;
|
||||
}
|
||||
|
||||
info("Starting testcase with script " + scriptName + ": " + JSON.stringify(tc));
|
||||
let loadedPromise = new Promise((resolve) => {
|
||||
loginFrame.addEventListener("load", function frameLoaded() {
|
||||
loginFrame.removeEventListener("load", frameLoaded);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
loginFrame.src = DEFAULT_ORIGIN + "/tests/toolkit/components/passwordmgr/test/mochitest/blank.html";
|
||||
yield loadedPromise;
|
||||
|
||||
let frameDoc = SpecialPowers.wrap(loginFrame.contentWindow).document;
|
||||
frameDoc.documentElement.innerHTML = tc.document;
|
||||
|
||||
// Wait for the form to be processed before trying to submit.
|
||||
yield promiseFormsProcessed();
|
||||
|
||||
info("Running " + scriptName + " script to check for a submission");
|
||||
frameDoc.defaultView.eval(SCRIPTS[scriptName]);
|
||||
|
||||
// Wait for 5000ms to see if the promise above resolves.
|
||||
yield new Promise(resolve => setTimeout(resolve, 5000));
|
||||
ok(true, "Done waiting for captures");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<iframe id="loginFrame" src="http://test1.mochi.test:8888/tests/toolkit/components/passwordmgr/test/mochitest/blank.html"></iframe>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for input events in Login Manager</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="onNewEvent(event)">
|
||||
Login Manager test: input events should fire.
|
||||
|
||||
<script>
|
||||
runChecksAfterCommonInit();
|
||||
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
/** Test for Login Manager: form fill, should get input events. **/
|
||||
|
||||
var usernameInputFired = false;
|
||||
var passwordInputFired = false;
|
||||
var usernameChangeFired = false;
|
||||
var passwordChangeFired = false;
|
||||
var onloadFired = false;
|
||||
|
||||
function onNewEvent(e) {
|
||||
info("Got " + e.type + " event.");
|
||||
if (e.type == "load") {
|
||||
onloadFired = true;
|
||||
} else if (e.type == "input") {
|
||||
if (e.target.name == "uname") {
|
||||
is(e.target.value, "testuser", "Should get 'testuser' as username");
|
||||
ok(!usernameInputFired, "Should not have gotten an input event for the username field yet.");
|
||||
usernameInputFired = true;
|
||||
} else if (e.target.name == "pword") {
|
||||
is(e.target.value, "testpass", "Should get 'testpass' as password");
|
||||
ok(!passwordInputFired, "Should not have gotten an input event for the password field yet.");
|
||||
passwordInputFired = true;
|
||||
}
|
||||
} else if (e.type == "change") {
|
||||
if (e.target.name == "uname") {
|
||||
is(e.target.value, "testuser", "Should get 'testuser' as username");
|
||||
ok(usernameInputFired, "Should get input event before change event for username field.");
|
||||
ok(!usernameChangeFired, "Should not have gotten a change event for the username field yet.");
|
||||
usernameChangeFired = true;
|
||||
} else if (e.target.name == "pword") {
|
||||
is(e.target.value, "testpass", "Should get 'testpass' as password");
|
||||
ok(passwordInputFired, "Should get input event before change event for password field.");
|
||||
ok(!passwordChangeFired, "Should not have gotten a change event for the password field yet.");
|
||||
passwordChangeFired = true;
|
||||
}
|
||||
}
|
||||
if (onloadFired && usernameInputFired && passwordInputFired && usernameChangeFired && passwordChangeFired) {
|
||||
ok(true, "All events fired as expected, we're done.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.registerCleanupFunction(function cleanup() {
|
||||
clearTimeout(timeout);
|
||||
$_(1, "uname").removeAttribute("oninput");
|
||||
$_(1, "pword").removeAttribute("oninput");
|
||||
$_(1, "uname").removeAttribute("onchange");
|
||||
$_(1, "pword").removeAttribute("onchange");
|
||||
document.body.removeAttribute("onload");
|
||||
});
|
||||
|
||||
var timeout = setTimeout(function() {
|
||||
ok(usernameInputFired, "Username input event should have fired by now.");
|
||||
ok(passwordInputFired, "Password input event should have fired by now.");
|
||||
ok(usernameChangeFired, "Username change event should have fired by now.");
|
||||
ok(passwordChangeFired, "Password change event should have fired by now.");
|
||||
ok(onloadFired, "Window load event should have fired by now.");
|
||||
ok(false, "Not all events fired yet.");
|
||||
SimpleTest.finish();
|
||||
}, 10000);
|
||||
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="display: none">
|
||||
|
||||
<form id="form1" action="formtest.js">
|
||||
<p>This is form 1.</p>
|
||||
<input type="text" name="uname" oninput="onNewEvent(event)" onchange="onNewEvent(event)">
|
||||
<input type="password" name="pword" oninput="onNewEvent(event)" onchange="onNewEvent(event)">
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
<button type="reset"> Reset </button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue