Backed out 9 changesets (bug 1548381) for XPCShell failures in toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_doAutocompleteSearch.js

Backed out changeset 0e7d8f96bf12 (bug 1548381)
Backed out changeset 738ce5e88e05 (bug 1548381)
Backed out changeset 38e35b6d8d80 (bug 1548381)
Backed out changeset cddbcd92ec10 (bug 1548381)
Backed out changeset 60ff6e363acf (bug 1548381)
Backed out changeset 1e2300b95a59 (bug 1548381)
Backed out changeset e0cf735bdcf5 (bug 1548381)
Backed out changeset fde90ccfb570 (bug 1548381)
Backed out changeset 426750b88fc2 (bug 1548381)

--HG--
extra : rebase_source : af00f81d952ed46b6f140e3a1dd6b5ca5bf638ec
This commit is contained in:
Dorel Luca 2019-05-21 03:19:42 +03:00
parent dc2ad25275
commit 02d3c96fae
18 changed files with 189 additions and 826 deletions

View File

@ -147,6 +147,8 @@ var whitelist = [
{file: "chrome://pippki/content/resetpassword.xul"},
// Bug 1337345
{file: "resource://gre/modules/Manifest.jsm"},
// Bug 1548381
{file: "resource://gre/modules/PasswordGenerator.jsm"},
// Bug 1351097
{file: "resource://gre/modules/accessibility/AccessFu.jsm"},
// Bug 1356043

View File

@ -7,6 +7,7 @@
/* General popup rules */
#PopupAutoComplete > richlistbox > richlistitem {
height: 20px;
min-height: 20px;
border: 0;
border-radius: 0;
@ -38,42 +39,8 @@
color: HighlightText;
}
/* Autocomplete richlistitem support for a two-line label display */
#PopupAutoComplete > richlistbox > richlistitem > .two-line-wrapper {
box-sizing: border-box;
display: flex;
flex-direction: row;
margin: 0;
}
#PopupAutoComplete > richlistbox > richlistitem > .two-line-wrapper > .ac-site-icon {
margin-inline-start: auto;
margin-inline-end: 4px;
}
#PopupAutoComplete > richlistbox > richlistitem > .two-line-wrapper > .labels-wrapper {
/* The text should flex while the icon should not */
flex: 1;
/* width/min-width are needed to get the text-overflow: ellipsis to work for the children */
min-width: 0;
width: 0;
}
#PopupAutoComplete > richlistbox > richlistitem > .two-line-wrapper > .labels-wrapper > .label-row {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#PopupAutoComplete > richlistbox > richlistitem > .two-line-wrapper > .labels-wrapper > .line2-label {
padding-top: 2px !important;
opacity: .6;
}
/* Login form autocompletion (with and without origin showing) and generated passwords */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="generatedPassword"] > .two-line-wrapper > .ac-site-icon,
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .two-line-wrapper > .ac-site-icon,
/* Login form autocompletion with and without origin showing */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .login-wrapper > .ac-site-icon,
#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon {
display: initial;
list-style-image: url(chrome://browser/skin/login.svg);
@ -81,23 +48,46 @@
fill: GrayText;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="generatedPassword"][selected] > .two-line-wrapper > .ac-site-icon,
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"][selected] > .two-line-wrapper > .ac-site-icon,
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"][selected] > .login-wrapper > .ac-site-icon,
#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon[selected] {
fill: HighlightText;
}
/* Login form autocompletion with origin showing and generated passwords */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="generatedPassword"],
/* Login form autocompletion with origin showing */
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] {
height: auto;
padding: 4px;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .login-wrapper {
box-sizing: border-box;
display: flex;
flex-direction: row;
margin: 0;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] + richlistitem[originaltype="generatedPassword"],
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] + richlistitem[originaltype="generatedPassword"] {
/* Separator between logins and generated passwords */
border-top: 1px solid var(--panel-separator-color);
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .login-wrapper > .ac-site-icon {
margin-inline-start: auto;
margin-inline-end: 4px;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .login-wrapper > .login-text {
/* The text should flex while the icon should not */
flex: 1;
/* width/min-width are needed to get the text-overflow: ellipsis to work for the children */
min-width: 0;
width: 0;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .login-wrapper > .login-text > .login-row {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#PopupAutoComplete > richlistbox > richlistitem[originaltype="loginWithOrigin"] > .login-wrapper > .login-text > .login-origin {
padding-top: 2px !important;
opacity: .6;
}
/* Insecure field warning */

View File

@ -4724,8 +4724,6 @@ pref("signon.autofillForms.autocompleteOff", true);
pref("signon.autofillForms.http", false);
pref("signon.autologin.proxy", false);
pref("signon.formlessCapture.enabled", true);
pref("signon.generation.available", false);
pref("signon.generation.enabled", false);
pref("signon.privateBrowsingCapture.enabled", false);
pref("signon.storeWhenAutocompleteOff", true);
pref("signon.debug", false);

View File

@ -122,14 +122,9 @@ class LoginAutocompleteItem extends AutocompleteItem {
});
XPCOMUtils.defineLazyGetter(this, "comment", () => {
try {
let uri = Services.io.newURI(login.hostname);
// Fallback to handle file: URIs
return uri.displayHostPort || login.hostname;
} catch (ex) {
// Fallback to origin below
}
return login.hostname;
return JSON.stringify({
loginOrigin: login.hostname,
});
});
}
@ -144,25 +139,15 @@ class LoginAutocompleteItem extends AutocompleteItem {
}
}
class GeneratedPasswordAutocompleteItem extends AutocompleteItem {
constructor(generatedPassword) {
super("generatedPassword");
this.comment = generatedPassword;
this.value = generatedPassword;
XPCOMUtils.defineLazyGetter(this, "label", () => {
return getLocalizedString("useGeneratedPassword");
});
}
}
class LoginsFooterAutocompleteItem extends AutocompleteItem {
constructor(hostname) {
super("loginsFooter");
this.comment = hostname;
XPCOMUtils.defineLazyGetter(this, "label", () => {
return getLocalizedString("viewSavedLogins.label");
return JSON.stringify({
label: getLocalizedString("viewSavedLogins.label"),
hostname,
});
});
}
}
@ -170,7 +155,6 @@ class LoginsFooterAutocompleteItem extends AutocompleteItem {
// nsIAutoCompleteResult implementation
function LoginAutoCompleteResult(aSearchString, matchingLogins, {
generatedPassword,
isSecure,
messageManager,
isPasswordField,
@ -191,8 +175,7 @@ function LoginAutoCompleteResult(aSearchString, matchingLogins, {
return false;
}
if (!matchingLogins.length && !generatedPassword && isPasswordField
&& formFillController.passwordPopupAutomaticallyOpened) {
if (!matchingLogins.length && isPasswordField && formFillController.passwordPopupAutomaticallyOpened) {
hidingFooterOnPWFieldAutoOpened = true;
log.debug("Hiding footer: no logins and the popup was opened upon focus of the pw. field");
return false;
@ -224,9 +207,6 @@ function LoginAutoCompleteResult(aSearchString, matchingLogins, {
// The footer comes last if it's enabled
if (isFooterEnabled()) {
if (generatedPassword) {
this._rows.push(new GeneratedPasswordAutocompleteItem(generatedPassword));
}
this._rows.push(new LoginsFooterAutocompleteItem(hostname));
}
@ -364,11 +344,7 @@ LoginAutoComplete.prototype = {
let isPasswordField = aElement.type == "password";
let hostname = aElement.ownerDocument.documentURIObject.host;
let completeSearch = (autoCompleteLookupPromise, {
generatedPassword,
logins,
messageManager,
}) => {
let completeSearch = (autoCompleteLookupPromise, { logins, messageManager }) => {
// If the search was canceled before we got our
// results, don't bother reporting them.
if (this._autoCompleteLookupPromise !== autoCompleteLookupPromise) {
@ -377,7 +353,6 @@ LoginAutoComplete.prototype = {
this._autoCompleteLookupPromise = null;
let results = new LoginAutoCompleteResult(aSearchString, logins, {
generatedPassword,
messageManager,
isSecure,
isPasswordField,
@ -419,8 +394,10 @@ LoginAutoComplete.prototype = {
previousResult = null;
}
let rect = BrowserUtils.getElementBoundingScreenRect(aElement);
let acLookupPromise = this._autoCompleteLookupPromise =
LoginManagerContent._autoCompleteSearchAsync(aSearchString, previousResult, aElement);
LoginManagerContent._autoCompleteSearchAsync(aSearchString, previousResult,
aElement, rect);
acLookupPromise.then(completeSearch.bind(this, acLookupPromise)).catch(log.error);
},

View File

@ -28,8 +28,6 @@ var LoginHelper = {
debug: null,
enabled: null,
formlessCaptureEnabled: null,
generationAvailable: null,
generationEnabled: null,
insecureAutofill: null,
managementURI: null,
privateBrowsingCaptureEnabled: null,
@ -49,8 +47,6 @@ var LoginHelper = {
this.debug = Services.prefs.getBoolPref("signon.debug");
this.enabled = Services.prefs.getBoolPref("signon.rememberSignons");
this.formlessCaptureEnabled = Services.prefs.getBoolPref("signon.formlessCapture.enabled");
this.generationAvailable = Services.prefs.getBoolPref("signon.generation.available");
this.generationEnabled = Services.prefs.getBoolPref("signon.generation.enabled");
this.insecureAutofill = Services.prefs.getBoolPref("signon.autofillForms.http");
this.managementURI = Services.prefs.getStringPref("signon.management.overrideURI", null);
this.privateBrowsingCaptureEnabled =

View File

@ -293,11 +293,7 @@ var LoginManagerContent = {
let loginsFound = LoginHelper.vanillaObjectsToLogins(msg.data.logins);
let messageManager = msg.target;
let request = this._takeRequest(msg);
request.promise.resolve({
generatedPassword: msg.data.generatedPassword,
logins: loginsFound,
messageManager,
});
request.promise.resolve({ logins: loginsFound, messageManager });
break;
}
@ -349,14 +345,13 @@ var LoginManagerContent = {
},
_autoCompleteSearchAsync(aSearchString, aPreviousResult,
aElement) {
aElement, aRect) {
let doc = aElement.ownerDocument;
let form = LoginFormFactory.createFromField(aElement);
let win = doc.defaultView;
let formOrigin = LoginHelper.getLoginOrigin(doc.documentURI);
let actionOrigin = LoginHelper.getFormActionOrigin(form);
let autocompleteInfo = aElement.getAutocompleteInfo();
let messageManager = win.docShell.messageManager;
@ -366,15 +361,13 @@ var LoginManagerContent = {
null;
let requestData = {};
let messageData = {
autocompleteInfo,
browsingContextId: win.docShell.browsingContext.id,
formOrigin,
actionOrigin,
searchString: aSearchString,
previousResult,
isSecure: InsecurePasswordUtils.isFormSecure(form),
isPasswordField: aElement.type == "password",
let messageData = { formOrigin,
actionOrigin,
searchString: aSearchString,
previousResult,
rect: aRect,
isSecure: InsecurePasswordUtils.isFormSecure(form),
isPasswordField: aElement.type == "password",
};
if (LoginHelper.showAutoCompleteFooter) {

View File

@ -15,8 +15,6 @@ ChromeUtils.defineModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
ChromeUtils.defineModuleGetter(this, "LoginHelper",
"resource://gre/modules/LoginHelper.jsm");
ChromeUtils.defineModuleGetter(this, "PasswordGenerator",
"resource://gre/modules/PasswordGenerator.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
@ -28,15 +26,6 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
var EXPORTED_SYMBOLS = [ "LoginManagerParent" ];
var LoginManagerParent = {
/**
* A map of a principal's origin (including suffixes) to a generated password string so that we
* can offer the same password later (e.g. in a confirmation field).
*
* We don't currently evict from this cache so entries should last until the end of the browser
* session. That may change later but for now a typical session would max out at a few entries.
*/
_generatedPasswordsByPrincipalOrigin: new Map(),
/**
* Reference to the default LoginRecipesParent (instead of the initialization promise) for
* synchronous access. This is a temporary hack and new consumers should yield on
@ -255,16 +244,9 @@ var LoginManagerParent = {
});
},
doAutocompleteSearch({
autocompleteInfo,
browsingContextId,
formOrigin,
actionOrigin,
searchString,
previousResult,
requestId,
isSecure,
isPasswordField,
doAutocompleteSearch({ formOrigin, actionOrigin,
searchString, previousResult,
rect, requestId, isSecure, isPasswordField,
}, target) {
// Note: previousResult is a regular object, not an
// nsIAutoCompleteResult.
@ -306,56 +288,22 @@ var LoginManagerParent = {
// Remove results that are too short, or have different prefix.
// Also don't offer empty usernames as possible results except
// for on password fields.
// for password field.
if (isPasswordField) {
return true;
}
return match && match.toLowerCase().startsWith(searchStringLower);
});
let generatedPassword = null;
if (isPasswordField && autocompleteInfo.fieldName == "new-password") {
generatedPassword = this.getGeneratedPassword(browsingContextId);
}
// Convert the array of nsILoginInfo to vanilla JS objects since nsILoginInfo
// doesn't support structured cloning.
var jsLogins = LoginHelper.loginsToVanillaObjects(matchingLogins);
target.messageManager.sendAsyncMessage("PasswordManager:loginsAutoCompleted", {
requestId,
generatedPassword,
logins: jsLogins,
});
},
/**
* Expose `BrowsingContext` so we can stub it in tests.
*/
get _browsingContextGlobal() {
return BrowsingContext;
},
getGeneratedPassword(browsingContextId) {
if (!LoginHelper.enabled || !LoginHelper.generationAvailable || !LoginHelper.generationEnabled) {
return null;
}
let browsingContext = BrowsingContext.get(browsingContextId);
if (!browsingContext) {
return null;
}
let framePrincipalOrigin = browsingContext.currentWindowGlobal.documentPrincipal.origin;
// Use the same password if we already generated one for this origin so that it doesn't change
// with each search/keystroke and the user can easily re-enter a password in a confirmation field.
let generatedPW = this._generatedPasswordsByPrincipalOrigin.get(framePrincipalOrigin);
if (generatedPW) {
return generatedPW;
}
generatedPW = PasswordGenerator.generatePassword();
this._generatedPasswordsByPrincipalOrigin.set(framePrincipalOrigin, generatedPW);
return generatedPW;
},
onFormSubmit({hostname, formSubmitURL, autoFilledLoginGuid,
usernameField, newPasswordField,
oldPasswordField, openerTopWindowID,

View File

@ -32,9 +32,6 @@ scheme = https
skip-if = toolkit == 'android' # autocomplete
[test_autocomplete_https_upgrade.html]
skip-if = toolkit == 'android' # autocomplete
[test_autocomplete_new_password.html]
scheme = https
skip-if = toolkit == 'android' # autocomplete
[test_autocomplete_password_open.html]
scheme = https
skip-if = toolkit == 'android' # autocomplete
@ -114,6 +111,9 @@ support-files =
[test_munged_username.html]
scheme = https
skip-if = toolkit == 'android' # bug 1527403
[test_autocomplete_new_password.html]
scheme = https
skip-if = toolkit == 'android' # autocomplete
[test_one_doorhanger_per_un_pw.html]
scheme = https
skip-if = toolkit == 'android' # bug 1535505

View File

@ -4,8 +4,6 @@
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
const GENERATED_PASSWORD_LENGTH = 15;
const GENERATED_PASSWORD_REGEX = /^[a-km-np-zA-HJ-NP-Z2-9]{15}$/;
// Copied from LoginTestUtils.masterPassword.masterPassword to use from the content process.
const MASTER_PASSWORD = "omgsecret!";
const TESTS_DIR = "/tests/toolkit/components/passwordmgr/test/";
@ -42,7 +40,7 @@ function $_(formNum, name) {
/**
* Check autocomplete popup results to ensure that expected
* *labels* are being shown correctly as items in the popup.
* values are being shown correctly as items in the popup.
*/
function checkAutoCompleteResults(actualValues, expectedValues, hostname, msg) {
if (hostname !== null) {
@ -50,7 +48,8 @@ function checkAutoCompleteResults(actualValues, expectedValues, hostname, msg) {
// Check the footer first.
let footerResult = actualValues[actualValues.length - 1];
is(footerResult, "View Saved Logins", "the footer text is shown correctly");
ok(footerResult.includes("View Saved Logins"), "the footer text is shown correctly");
ok(footerResult.includes(hostname), "the footer has the correct hostname attribute");
}
if (hostname === null) {
@ -68,17 +67,6 @@ function checkAutoCompleteResults(actualValues, expectedValues, hostname, msg) {
checkArrayValues(actualValues.slice(0, -1), expectedValues, msg);
}
/**
* Check for expected username/password in form.
* @see `checkForm` below for a similar function.
*/
function checkLoginForm(usernameField, expectedUsername, passwordField, expectedPassword) {
let formID = usernameField.parentNode.id;
is(usernameField.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
is(passwordField.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
}
/**
* Check a form for expected values. If an argument is null, a field's
* expected value will be the default value.
@ -222,7 +210,7 @@ function logoutMasterPassword() {
}
/**
* Resolves when a specified number of forms have been processed for (potential) filling.
* Resolves when a specified number of forms have been processed.
*/
function promiseFormsProcessed(expectedCount = 1) {
var processedCount = 0;
@ -230,7 +218,6 @@ function promiseFormsProcessed(expectedCount = 1) {
function onProcessedForm(subject, topic, data) {
processedCount++;
if (processedCount == expectedCount) {
info(`${processedCount} form(s) processed`);
SpecialPowers.removeObserver(onProcessedForm, "passwordmgr-processed-form");
resolve(SpecialPowers.Cu.waiveXrays(subject), data);
}
@ -409,3 +396,9 @@ this.LoginManager = new Proxy({}, {
},
});
// Check for expected username/password in form.
function checkLoginForm(usernameField, expectedUsername, passwordField, expectedPassword) {
let formID = usernameField.parentNode.id;
is(usernameField.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
is(passwordField.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
}

View File

@ -2,12 +2,12 @@
<html>
<head>
<meta charset="utf-8">
<title>Test autofill and autocomplete on autocomplete=new-password fields</title>
<title>Test autofill with autocomplete=new-password fields</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="pwmgr_common.js"></script>
<script src="../../../satchel/test/satchel_common.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="pwmgr_common.js"></script>
<script type="text/javascript" src="../../../satchel/test/satchel_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Login Manager test: autofill with autocomplete=new-password fields
@ -38,10 +38,10 @@ let readyPromise = registerRunTests();
<button type="submit">Submit</button>
</form>
<!-- form2 uses a new-password type=password field -->
<!-- form2 uses the new-password field -->
<form id="form2" action="https://autofill" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword" autocomplete="new-password">
<input type="password" autocomplete="new-password">
<button type="submit">Submit</button>
</form>
</div>
@ -51,149 +51,55 @@ let readyPromise = registerRunTests();
const {ContentTaskUtils} =
SpecialPowers.Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
async function getAutocompleteResult(input, expectedValues) {
input.focus();
const shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
await shownPromise;
synthesizeKey("KEY_ArrowDown");
await synthesizeKey("KEY_Enter");
let didAutocomplete;
try {
await ContentTaskUtils.waitForCondition(() => {
for (let [selector, expectedValue] of Object.entries(expectedValues)) {
if (document.querySelector(selector).value !== expectedValue) {
return false;
}
}
return true;
});
didAutocomplete = true;
} catch (ex) {
info("waitForCondition exception: " + ex.message);
didAutocomplete = false;
}
return didAutocomplete;
}
add_task(async function setup() {
ok(readyPromise, "check promise is available");
await readyPromise;
});
add_task(async function test_autofillAutocompleteUsername_noGeneration() {
add_task(async function test_autofillAutocompleteNewPassword() {
// reference form was filled as expected?
checkForm(1, "user1", "pass1");
// 2nd form should not be filled
checkForm(2, "", "");
$_(2, "uname").focus();
const shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
let results = await shownPromise;
let expectedACLabels = ["user1"];
checkAutoCompleteResults(results, expectedACLabels, "example.com", "Check all rows are correct");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await promiseFormsProcessed();
checkForm(2, "user1", "pass1");
let form = document.getElementById("form2");
let userInput = form.querySelector("[name='uname']");
document.getElementById("form2").reset();
const didAutocomplete = await getAutocompleteResult(userInput, {
"#form2 [name='uname']": "user1",
"#form2 [type='password']": "pass1",
});
ok(didAutocomplete, "Autocomplete of user and password fields should happen");
});
add_task(async function test_autofillAutocompletePassword_noGeneration() {
// 2nd form should not be filled
checkForm(2, "", "");
let pword = $_(2, "pword");
pword.focus();
const shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
let results = await shownPromise;
let expectedACLabels = ["user1"];
checkAutoCompleteResults(results, expectedACLabels, "example.com", "Check all rows are correct");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
// Can't use promiseFormsProcessed() when autocomplete fills the field directly.
await SimpleTest.promiseWaitForCondition(() => pword.value == "pass1", "Check pw filled");
checkForm(2, "", "pass1");
// No autocomplete results should appear for non-empty pw fields.
synthesizeKey("KEY_ArrowDown");
await promiseNoUnexpectedPopupShown();
document.getElementById("form2").reset();
});
// All tests below this are with generation prefs enabled.
add_task(async function test_autofillAutocompleteUsername_noGeneration() {
await SpecialPowers.pushPrefEnv({"set": [
["signon.generation.available", true],
["signon.generation.enabled", true],
["signon.showAutoCompleteOrigins", true],
]});
// 2nd form should not be filled
checkForm(2, "", "");
$_(2, "uname").focus();
const shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
let results = await shownPromise;
// No generation option on username fields.
let expectedACLabels = ["user1"];
checkAutoCompleteResults(results, expectedACLabels, "example.com", "Check all rows are correct");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await promiseFormsProcessed();
checkForm(2, "user1", "pass1");
document.getElementById("form2").reset();
});
add_task(async function test_autofillAutocompletePassword_withGeneration() {
// 2nd form should not be filled
checkForm(2, "", "");
let pword = $_(2, "pword");
pword.focus();
let shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
let results = await shownPromise;
let expectedACLabels = [
"user1",
"Use Generated Password",
];
checkAutoCompleteResults(results, expectedACLabels, "example.com", "Check all rows are correct");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
// Can't use promiseFormsProcessed() when autocomplete fills the field directly.
await SimpleTest.promiseWaitForCondition(() => pword.value == "pass1", "Check pw filled");
checkForm(2, "", "pass1");
// No autocomplete results should appear for non-empty pw fields.
synthesizeKey("KEY_ArrowDown");
await promiseNoUnexpectedPopupShown();
while (pword.value) {
synthesizeKey("KEY_Backspace");
}
info("This time select the generated password");
shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
results = await shownPromise;
expectedACLabels = [
"user1",
"Use Generated Password",
];
checkAutoCompleteResults(results, expectedACLabels, "example.com", "Check all rows are correct");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await SimpleTest.promiseWaitForCondition(() => !!pword.value, "Check generated pw filled");
let generatedPW = pword.value;
is(generatedPW.length, GENERATED_PASSWORD_LENGTH, "Check generated password length");
ok(generatedPW.match(GENERATED_PASSWORD_REGEX), "Check generated password format");
while (pword.value) {
synthesizeKey("KEY_Backspace");
}
shownPromise = promiseACShown();
synthesizeKey("KEY_ArrowDown");
results = await shownPromise;
expectedACLabels = [
"user1",
"Use Generated Password",
];
checkAutoCompleteResults(results, expectedACLabels, "example.com", "Check all rows are correct");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await SimpleTest.promiseWaitForCondition(() => !!pword.value, "Check generated pw filled");
// Same generated password should be used.
checkForm(2, "", generatedPW);
document.getElementById("form2").reset();
});
</script>
</pre>
</body>

View File

@ -1,92 +0,0 @@
/**
* Test LoginManagerParent.doAutocompleteSearch()
*/
"use strict";
const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
const {LoginManagerParent: LMP} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
add_task(async function test_doAutocompleteSearch_generated_noLogins() {
Services.prefs.setBoolPref("signon.generation.available", true); // TODO: test both with false
Services.prefs.setBoolPref("signon.generation.enabled", true);
ok(LMP.doAutocompleteSearch, "doAutocompleteSearch exists");
// Default to the happy path
let arg1 = {
autocompleteInfo: {
section: "",
addressType: "",
contactType: "",
fieldName: "new-password",
canAutomaticallyPersist: false,
},
browsingContextId: 123,
formOrigin: "https://example.com",
actionOrigin: "https://mozilla.org",
searchString: "",
previousResult: null,
requestId: "foo",
isSecure: true,
isPasswordField: true,
};
let sendMessageStub = sinon.stub();
let fakeBrowser = {
messageManager: {
sendAsyncMessage: sendMessageStub,
},
};
sinon.stub(LMP._browsingContextGlobal, "get").withArgs(123).callsFake(() => {
return {
currentWindowGlobal: {
documentPrincipal: Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://www.example.com^userContextId=1"),
},
};
});
LMP.doAutocompleteSearch(arg1, fakeBrowser);
ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
let msg1 = sendMessageStub.firstCall.args[1];
equal(msg1.requestId, arg1.requestId, "requestId matches");
equal(msg1.logins.length, 0, "no logins");
ok(msg1.generatedPassword, "has a generated password");
equal(msg1.generatedPassword.length, 15, "generated password length");
sendMessageStub.resetHistory();
info("repeat the search and ensure the same password was used");
LMP.doAutocompleteSearch(arg1, fakeBrowser);
ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
let msg2 = sendMessageStub.firstCall.args[1];
equal(msg2.requestId, arg1.requestId, "requestId matches");
equal(msg2.logins.length, 0, "no logins");
equal(msg2.generatedPassword, msg1.generatedPassword, "same generated password");
sendMessageStub.resetHistory();
info("Check cases where a password shouldn't be generated");
LMP.doAutocompleteSearch({...arg1, ...{isPasswordField: false}}, fakeBrowser);
ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
let msg = sendMessageStub.firstCall.args[1];
equal(msg.requestId, arg1.requestId, "requestId matches");
equal(msg.generatedPassword, null, "no generated password when not a pw. field");
sendMessageStub.resetHistory();
let arg1_2 = {...arg1};
arg1_2.autocompleteInfo.fieldName = "";
LMP.doAutocompleteSearch(arg1_2, fakeBrowser);
ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
msg = sendMessageStub.firstCall.args[1];
equal(msg.requestId, arg1.requestId, "requestId matches");
equal(msg.generatedPassword, null, "no generated password when not autocomplete=new-password");
sendMessageStub.resetHistory();
LMP.doAutocompleteSearch({...arg1, ...{browsingContextId: 999}}, fakeBrowser);
ok(sendMessageStub.calledOnce, "sendAsyncMessage was called");
msg = sendMessageStub.firstCall.args[1];
equal(msg.requestId, arg1.requestId, "requestId matches");
equal(msg.generatedPassword, null, "no generated password with a missing browsingContextId");
sendMessageStub.resetHistory();
});

View File

@ -1,69 +0,0 @@
/**
* Test LoginManagerParent.getGeneratedPassword()
*/
"use strict";
const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
const {LoginManagerParent: LMP} = ChromeUtils.import("resource://gre/modules/LoginManagerParent.jsm");
add_task(async function test_getGeneratedPassword() {
// Force the feature to be enabled.
Services.prefs.setBoolPref("signon.generation.available", true);
Services.prefs.setBoolPref("signon.generation.enabled", true);
ok(LMP.getGeneratedPassword, "LMP.getGeneratedPassword exists");
equal(LMP._generatedPasswordsByPrincipalOrigin.size, 0, "Empty cache to start");
equal(LMP.getGeneratedPassword(99), null, "Null with no BrowsingContext");
ok(LMP._browsingContextGlobal, "Check _browsingContextGlobal exists");
ok(!LMP._browsingContextGlobal.get(99), "BrowsingContext 99 shouldn't exist yet");
info("Stubbing BrowsingContext.get(99)");
sinon.stub(LMP._browsingContextGlobal, "get").withArgs(99).callsFake(() => {
return {
currentWindowGlobal: {
documentPrincipal: Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://www.example.com^userContextId=6"),
},
};
});
ok(LMP._browsingContextGlobal.get(99), "Checking BrowsingContext.get(99) stub");
let password1 = LMP.getGeneratedPassword(99);
notEqual(password1, null, "Check password was returned");
equal(password1.length, 15, "Check password length");
equal(LMP._generatedPasswordsByPrincipalOrigin.size, 1, "1 added to cache");
equal(LMP._generatedPasswordsByPrincipalOrigin.get("https://www.example.com^userContextId=6"),
password1, "Cache key and value");
let password2 = LMP.getGeneratedPassword(99);
equal(password1, password2, "Same password should be returned for the same origin");
info("Changing the documentPrincipal to simulate a navigation in the frame");
LMP._browsingContextGlobal.get.restore();
sinon.stub(LMP._browsingContextGlobal, "get").withArgs(99).callsFake(() => {
return {
currentWindowGlobal: {
documentPrincipal: Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("https://www.mozilla.org^userContextId=2"),
},
};
});
let password3 = LMP.getGeneratedPassword(99);
notEqual(password2, password3, "Different password for a different origin for the same BC");
equal(password3.length, 15, "Check password3 length");
info("Now checks cases where null should be returned");
Services.prefs.setBoolPref("signon.rememberSignons", false);
equal(LMP.getGeneratedPassword(99), null, "Prevented when pwmgr disabled");
Services.prefs.setBoolPref("signon.rememberSignons", true);
Services.prefs.setBoolPref("signon.generation.available", false);
equal(LMP.getGeneratedPassword(99), null, "Prevented when unavailable");
Services.prefs.setBoolPref("signon.generation.available", true);
Services.prefs.setBoolPref("signon.generation.enabled", false);
equal(LMP.getGeneratedPassword(99), null, "Prevented when disabled");
Services.prefs.setBoolPref("signon.generation.enabled", true);
equal(LMP.getGeneratedPassword(123), null, "Prevented when browsingContext is missing");
});

View File

@ -3,6 +3,7 @@ var nsLoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1
Ci.nsILoginInfo, "init");
const PREF_INSECURE_FIELD_WARNING_ENABLED = "security.insecure_field_warning.contextual.enabled";
const PREF_INSECURE_AUTOFILLFORMS_ENABLED = "signon.autofillForms.http";
let matchingLogins = [];
matchingLogins.push(new nsLoginInfo("http://mochi.test:8888", "http://autocomplete:8888", null,
@ -29,6 +30,7 @@ const LABEL_NO_USERNAME = "No username (" + time + ")";
let expectedResults = [
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: true,
isSecure: true,
isPasswordField: false,
matchingLogins,
@ -36,53 +38,27 @@ let expectedResults = [
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
isSecure: false,
isPasswordField: false,
matchingLogins: [],
items: [{
value: "",
label: "This connection is not secure. Logins entered here could be compromised. Learn More",
style: "insecureWarning",
comment: "",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: true,
isSecure: false,
isPasswordField: false,
matchingLogins,
@ -90,41 +66,31 @@ let expectedResults = [
value: "",
label: "This connection is not secure. Logins entered here could be compromised. Learn More",
style: "insecureWarning",
comment: "",
}, {
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: true,
isSecure: true,
isPasswordField: true,
matchingLogins,
@ -132,36 +98,27 @@ let expectedResults = [
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: true,
isSecure: false,
isPasswordField: true,
matchingLogins,
@ -169,41 +126,31 @@ let expectedResults = [
value: "",
label: "This connection is not secure. Logins entered here could be compromised. Learn More",
style: "insecureWarning",
comment: "",
}, {
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: true,
isSecure: true,
isPasswordField: false,
matchingLogins,
@ -211,36 +158,27 @@ let expectedResults = [
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: true,
isSecure: false,
isPasswordField: false,
matchingLogins,
@ -248,36 +186,27 @@ let expectedResults = [
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: true,
isSecure: true,
isPasswordField: true,
matchingLogins,
@ -285,36 +214,27 @@ let expectedResults = [
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: true,
isSecure: false,
isPasswordField: true,
matchingLogins,
@ -322,36 +242,27 @@ let expectedResults = [
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: false,
isSecure: true,
isPasswordField: false,
matchingLogins,
@ -359,36 +270,27 @@ let expectedResults = [
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: false,
isSecure: false,
isPasswordField: false,
matchingLogins,
@ -396,41 +298,31 @@ let expectedResults = [
value: "",
label: "This connection is not secure. Logins entered here could be compromised. Learn More",
style: "insecureWarning",
comment: "",
}, {
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: false,
isSecure: true,
isPasswordField: true,
matchingLogins,
@ -438,36 +330,27 @@ let expectedResults = [
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
insecureAutoFillFormsEnabled: false,
isSecure: false,
isPasswordField: true,
matchingLogins,
@ -475,41 +358,31 @@ let expectedResults = [
value: "",
label: "This connection is not secure. Logins entered here could be compromised. Learn More",
style: "insecureWarning",
comment: "",
}, {
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: false,
isSecure: true,
isPasswordField: false,
matchingLogins,
@ -517,240 +390,87 @@ let expectedResults = [
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
isSecure: false,
isPasswordField: false,
matchingLogins: [],
items: [{
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
isSecure: false,
isPasswordField: false,
matchingLogins: [],
searchString: "foo",
items: [{
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: false,
isSecure: false,
isPasswordField: false,
matchingLogins,
items: [{
value: "",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "tempuser1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testuser3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzuser4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
isSecure: true,
isPasswordField: true,
matchingLogins,
items: [{
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: false,
isSecure: false,
isPasswordField: true,
matchingLogins,
items: [{
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
comment: "mochi.test:8888",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
isSecure: true,
isPasswordField: true,
matchingLogins: [],
items: [{
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
}],
},
{
insecureFieldWarningEnabled: true,
isSecure: true,
isPasswordField: true,
matchingLogins: [],
searchString: "foo",
items: [],
},
{
generatedPassword: "9ljgfd4shyktb45",
insecureFieldWarningEnabled: true,
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: false,
isSecure: true,
isPasswordField: true,
matchingLogins: [],
matchingLogins,
items: [{
value: "9ljgfd4shyktb45",
label: "Use Generated Password",
style: "generatedPassword",
comment: "9ljgfd4shyktb45",
value: "emptypass1",
label: LABEL_NO_USERNAME,
style: "loginWithOrigin",
}, {
value: "",
label: "View Saved Logins",
style: "loginsFooter",
comment: "mochi.test",
value: "temppass1",
label: "tempuser1",
style: "loginWithOrigin",
}, {
value: "testpass2",
label: "testuser2",
style: "loginWithOrigin",
}, {
value: "testpass3",
label: "testuser3",
style: "loginWithOrigin",
}, {
value: "zzzpass4",
label: "zzzuser4",
style: "loginWithOrigin",
}],
},
{
generatedPassword: "9ljgfd4shyktb45",
insecureFieldWarningEnabled: true,
isSecure: true,
insecureFieldWarningEnabled: false,
insecureAutoFillFormsEnabled: false,
isSecure: false,
isPasswordField: true,
matchingLogins: [],
searchString: "9ljgfd4shyktb45",
matchingLogins,
items: [],
},
];
add_task(async function test_all_patterns() {
LoginHelper.createLogger("LoginAutoCompleteResult");
Services.prefs.setBoolPref("signon.showAutoCompleteFooter", true);
Services.prefs.setBoolPref("signon.showAutoCompleteOrigins", true);
expectedResults.forEach(pattern => {
info(JSON.stringify(pattern, null, 2));
Services.prefs.setBoolPref(PREF_INSECURE_FIELD_WARNING_ENABLED,
pattern.insecureFieldWarningEnabled);
let actual = new LoginAutoCompleteResult(pattern.searchString || "", pattern.matchingLogins, {
hostname: "mochi.test",
generatedPassword: pattern.generatedPassword,
Services.prefs.setBoolPref(PREF_INSECURE_AUTOFILLFORMS_ENABLED,
pattern.insecureAutoFillFormsEnabled);
let actual = new LoginAutoCompleteResult("", pattern.matchingLogins, {
isSecure: pattern.isSecure,
isPasswordField: pattern.isPasswordField,
});
equal(actual.matchCount, pattern.items.length, "Check matching row count");
pattern.items.forEach((item, index) => {
equal(actual.getValueAt(index), item.value, `Value ${index}`);
equal(actual.getLabelAt(index), item.label, `Label ${index}`);
equal(actual.getStyleAt(index), item.style, `Style ${index}`);
equal(actual.getCommentAt(index), item.comment, `Comment ${index}`);
equal(actual.getValueAt(index), item.value);
equal(actual.getLabelAt(index), item.label);
equal(actual.getStyleAt(index), item.style);
});
if (pattern.items.length != 0) {

View File

@ -27,8 +27,6 @@ run-if = buildapp == "browser"
[test_isOriginMatching.js]
[test_isUsernameFieldType.js]
[test_legacy_empty_formSubmitURL.js]
[test_LoginManagerParent_doAutocompleteSearch.js]
[test_LoginManagerParent_getGeneratedPassword.js]
[test_legacy_validation.js]
[test_login_autocomplete_result.js]
skip-if = os == "android"

View File

@ -377,7 +377,6 @@ MozElements.MozAutocompleteRichlistboxPopup = class MozAutocompleteRichlistboxPo
"autofill-footer",
"autofill-clear-button",
"autofill-insecureWarning",
"generatedPassword",
"insecureWarning",
"loginsFooter",
"loginWithOrigin",
@ -410,9 +409,8 @@ MozElements.MozAutocompleteRichlistboxPopup = class MozAutocompleteRichlistboxPo
case "loginsFooter":
options = { is: "autocomplete-richlistitem-logins-footer" };
break;
case "generatedPassword":
case "loginWithOrigin":
options = { is: "autocomplete-two-line-richlistitem" };
options = { is: "autocomplete-richlistitem-login-with-origin" };
break;
default:
options = { is: "autocomplete-richlistitem" };

View File

@ -988,20 +988,25 @@ class MozAutocompleteRichlistitemLoginsFooter extends MozElements.MozAutocomplet
return;
}
// ac-label gets populated from getCommentAt despite the attribute name.
// The "comment" is used to populate additional visible text.
let formHostname = this.getAttribute("ac-label");
LoginHelper.openPasswordManager(this.ownerGlobal, {
filterString: formHostname,
filterString: this._data.hostname,
entryPoint: "autocomplete",
});
}
this.addEventListener("click", handleEvent);
}
get _data() {
return JSON.parse(this.getAttribute("ac-value"));
}
_adjustAcItem() {
this._titleText.textContent = this._data.label;
}
}
class MozAutocompleteTwoLineRichlistitem extends MozElements.MozRichlistitem {
class MozAutocompleteRichlistitemLoginWithOrigin extends MozElements.MozRichlistitem {
connectedCallback() {
if (this.delayConnectedCallback()) {
return;
@ -1016,10 +1021,7 @@ class MozAutocompleteTwoLineRichlistitem extends MozElements.MozRichlistitem {
static get inheritedAttributes() {
return {
// getLabelAt:
".line1-label": "text=ac-value",
// getCommentAt:
".line2-label": "text=ac-label",
".login-username": "text=ac-value",
};
}
@ -1027,11 +1029,11 @@ class MozAutocompleteTwoLineRichlistitem extends MozElements.MozRichlistitem {
return `
<div xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
class="two-line-wrapper">
class="login-wrapper">
<xul:image class="ac-site-icon"></xul:image>
<div class="labels-wrapper">
<div class="label-row line1-label"></div>
<div class="label-row line2-label"></div>
<div class="login-text">
<div class="login-row login-username"></div>
<div class="login-row login-origin"></div>
</div>
</div>
`;
@ -1043,6 +1045,16 @@ class MozAutocompleteTwoLineRichlistitem extends MozElements.MozRichlistitem {
// Make item fit in popup as XUL box could not constrain
// item's width
this.firstElementChild.style.width = outerBoxRect.width + "px";
let data = JSON.parse(this.getAttribute("ac-label"));
let originElement = this.querySelector(".login-origin");
try {
let uri = Services.io.newURI(data.loginOrigin);
// Fallback to handle file: URIs
originElement.textContent = uri.displayHostPort || data.loginOrigin;
} catch (ex) {
originElement.textContent = data.loginOrigin;
}
}
_onOverflow() {}
@ -1064,7 +1076,7 @@ customElements.define("autocomplete-richlistitem-logins-footer", MozAutocomplete
extends: "richlistitem",
});
customElements.define("autocomplete-two-line-richlistitem", MozAutocompleteTwoLineRichlistitem, {
customElements.define("autocomplete-richlistitem-login-with-origin", MozAutocompleteRichlistitemLoginWithOrigin, {
extends: "richlistitem",
});
}

View File

@ -1015,7 +1015,6 @@
"autofill-footer",
"autofill-clear-button",
"autofill-insecureWarning",
"generatedPassword",
"insecureWarning",
"loginsFooter",
"loginWithOrigin",
@ -1048,9 +1047,8 @@
case "loginsFooter":
options = { is: "autocomplete-richlistitem-logins-footer" };
break;
case "generatedPassword":
case "loginWithOrigin":
options = { is: "autocomplete-two-line-richlistitem" };
options = { is: "autocomplete-richlistitem-login-with-origin" };
break;
default:
options = { is: "autocomplete-richlistitem" };

View File

@ -45,10 +45,6 @@ userSelectText2 = Select which login to update:
removeLoginPrompt=Are you sure you wish to remove this login?
removeLoginTitle=Remove login
loginsDescriptionAll2=Logins for the following sites are stored on your computer
# LOCALIZATION NOTE (useGeneratedPassword):
# Shown in the autocomplete popup to allow filling a generated password into a password field.
useGeneratedPassword=Use Generated Password
# LOCALIZATION NOTE (loginHostAge):
# This is used to show the context menu login items with their age.
# 1st string is the username for the login, 2nd is the login's age.
@ -56,7 +52,6 @@ loginHostAge=%1$S (%2$S)
# LOCALIZATION NOTE (noUsername):
# String is used on the context menu when a login doesn't have a username.
noUsername=No username
duplicateLoginTitle=Login already exists
duplicateLogin=A duplicate login already exists.