зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 5 changesets (bug 993197, bug 949617, bug 995489, bug 1029128) for Android test failures.
Backed out changeset 033d6271fd19 (bug 1029128) Backed out changeset f568b1dc9880 (bug 1029128) Backed out changeset daa9c46d19dc (bug 949617) Backed out changeset 2baf5c61cbf5 (bug 995489) Backed out changeset 654c1f0c88bc (bug 993197)
This commit is contained in:
Родитель
0d80c18130
Коммит
323ddc70d4
|
@ -34,22 +34,22 @@ addMessageListener("Browser:HideSessionRestoreButton", function (message) {
|
|||
}
|
||||
});
|
||||
|
||||
addEventListener("DOMFormHasPassword", function(event) {
|
||||
InsecurePasswordUtils.checkForInsecurePasswords(event.target);
|
||||
LoginManagerContent.onFormPassword(event);
|
||||
});
|
||||
addEventListener("DOMAutoComplete", function(event) {
|
||||
LoginManagerContent.onUsernameInput(event);
|
||||
});
|
||||
addEventListener("blur", function(event) {
|
||||
LoginManagerContent.onUsernameInput(event);
|
||||
});
|
||||
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
addEventListener("contextmenu", function (event) {
|
||||
sendAsyncMessage("contextmenu", {}, { event: event });
|
||||
}, false);
|
||||
} else {
|
||||
addEventListener("DOMFormHasPassword", function(event) {
|
||||
InsecurePasswordUtils.checkForInsecurePasswords(event.target);
|
||||
LoginManagerContent.onFormPassword(event);
|
||||
});
|
||||
addEventListener("DOMAutoComplete", function(event) {
|
||||
LoginManagerContent.onUsernameInput(event);
|
||||
});
|
||||
addEventListener("blur", function(event) {
|
||||
LoginManagerContent.onUsernameInput(event);
|
||||
});
|
||||
|
||||
addEventListener("mozUITour", function(event) {
|
||||
if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
|
||||
return;
|
||||
|
|
|
@ -93,9 +93,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
|
||||
"resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
|
||||
"resource://gre/modules/LoginManagerParent.jsm");
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
|
||||
"resource:///modules/SignInToWebsite.jsm");
|
||||
|
@ -510,8 +507,6 @@ BrowserGlue.prototype = {
|
|||
RemotePrompt.init();
|
||||
}
|
||||
|
||||
LoginManagerParent.init();
|
||||
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
||||
},
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
/* vim: set ts=4 sts=4 sw=4 et tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "LoginManagerContent",
|
||||
"UserAutoCompleteResult" ];
|
||||
this.EXPORTED_SYMBOLS = ["LoginManagerContent"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
@ -16,7 +12,6 @@ const Cu = Components.utils;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
// These mirror signon.* prefs.
|
||||
var gEnabled, gDebug, gAutofillForms, gStoreWhenAutocompleteOff;
|
||||
|
@ -67,7 +62,7 @@ var observer = {
|
|||
try {
|
||||
LoginManagerContent._onFormSubmit(formElement);
|
||||
} catch (e) {
|
||||
log("Caught error in onFormSubmit(", e.lineNumber, "):", e.message);
|
||||
log("Caught error in onFormSubmit:", e);
|
||||
}
|
||||
|
||||
return true; // Always return true, or form submit will be canceled.
|
||||
|
@ -88,15 +83,6 @@ prefBranch.addObserver("", observer.onPrefChange, false);
|
|||
observer.onPrefChange(); // read initial values
|
||||
|
||||
|
||||
function messageManagerFromWindow(win) {
|
||||
return win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager)
|
||||
}
|
||||
|
||||
// This object maps to the "child" process (even in the single-process case).
|
||||
var LoginManagerContent = {
|
||||
|
||||
__formFillService : null, // FormFillController, for username autocompleting
|
||||
|
@ -108,125 +94,6 @@ var LoginManagerContent = {
|
|||
return this.__formFillService;
|
||||
},
|
||||
|
||||
_getRandomId: function() {
|
||||
return Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator).generateUUID().toString();
|
||||
},
|
||||
|
||||
_messages: [ "RemoteLogins:loginsFound",
|
||||
"RemoteLogins:loginsAutoCompleted" ],
|
||||
|
||||
// Map from form login requests to information about that request.
|
||||
_requests: new Map(),
|
||||
|
||||
// Number of outstanding requests to each manager.
|
||||
_managers: new Map(),
|
||||
|
||||
_takeRequest: function(msg) {
|
||||
let data = msg.data;
|
||||
let request = this._requests.get(data.requestId);
|
||||
|
||||
this._requests.delete(data.requestId);
|
||||
|
||||
let count = this._managers.get(msg.target);
|
||||
if (--count === 0) {
|
||||
this._managers.delete(msg.target);
|
||||
|
||||
for (let message of this._messages)
|
||||
msg.target.removeMessageListener(message, this);
|
||||
} else {
|
||||
this._managers.set(msg.target, count);
|
||||
}
|
||||
|
||||
return request;
|
||||
},
|
||||
|
||||
_sendRequest: function(messageManager, requestData,
|
||||
name, messageData) {
|
||||
let count;
|
||||
if (!(count = this._managers.get(messageManager))) {
|
||||
this._managers.set(messageManager, 1);
|
||||
|
||||
for (let message of this._messages)
|
||||
messageManager.addMessageListener(message, this);
|
||||
} else {
|
||||
this._managers.set(messageManager, ++count);
|
||||
}
|
||||
|
||||
let requestId = this._getRandomId();
|
||||
messageData.requestId = requestId;
|
||||
|
||||
messageManager.sendAsyncMessage(name, messageData);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
requestData.promise = deferred;
|
||||
this._requests.set(requestId, requestData);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
receiveMessage: function (msg) {
|
||||
let request = this._takeRequest(msg);
|
||||
switch (msg.name) {
|
||||
case "RemoteLogins:loginsFound": {
|
||||
request.promise.resolve({ form: request.form,
|
||||
loginsFound: msg.data.logins });
|
||||
break;
|
||||
}
|
||||
|
||||
case "RemoteLogins:loginsAutoCompleted": {
|
||||
request.promise.resolve(msg.data.logins);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_asyncFindLogins: function(form, options) {
|
||||
let doc = form.ownerDocument;
|
||||
let win = doc.defaultView;
|
||||
|
||||
let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
|
||||
let actionOrigin = LoginUtils._getActionOrigin(form);
|
||||
|
||||
let messageManager = messageManagerFromWindow(win);
|
||||
|
||||
// XXX Weak??
|
||||
let requestData = { form: form };
|
||||
let messageData = { formOrigin: formOrigin,
|
||||
actionOrigin: actionOrigin,
|
||||
options: options };
|
||||
|
||||
return this._sendRequest(messageManager, requestData,
|
||||
"RemoteLogins:findLogins",
|
||||
messageData);
|
||||
},
|
||||
|
||||
_autoCompleteSearchAsync: function(aSearchString, aPreviousResult,
|
||||
aElement, aRect) {
|
||||
let doc = aElement.ownerDocument;
|
||||
let form = aElement.form;
|
||||
let win = doc.defaultView;
|
||||
|
||||
let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
|
||||
let actionOrigin = LoginUtils._getActionOrigin(form);
|
||||
|
||||
let messageManager = messageManagerFromWindow(win);
|
||||
|
||||
let remote = (Services.appinfo.processType ===
|
||||
Services.appinfo.PROCESS_TYPE_CONTENT);
|
||||
|
||||
let requestData = {};
|
||||
let messageData = { formOrigin: formOrigin,
|
||||
actionOrigin: actionOrigin,
|
||||
searchString: aSearchString,
|
||||
previousResult: aPreviousResult,
|
||||
rect: aRect,
|
||||
remote: remote };
|
||||
|
||||
return this._sendRequest(messageManager, requestData,
|
||||
"RemoteLogins:autoCompleteLogins",
|
||||
messageData);
|
||||
},
|
||||
|
||||
/*
|
||||
* onFormPassword
|
||||
*
|
||||
|
@ -240,18 +107,51 @@ var LoginManagerContent = {
|
|||
return;
|
||||
|
||||
let form = event.target;
|
||||
log("onFormPassword for", form.ownerDocument.documentURI);
|
||||
this._asyncFindLogins(form, { showMasterPassword: true })
|
||||
.then(this.loginsFound.bind(this))
|
||||
.then(null, Cu.reportError);
|
||||
let doc = form.ownerDocument;
|
||||
|
||||
log("onFormPassword for", doc.documentURI);
|
||||
|
||||
// If there are no logins for this site, bail out now.
|
||||
let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
|
||||
if (!Services.logins.countLogins(formOrigin, "", null))
|
||||
return;
|
||||
|
||||
// If we're currently displaying a master password prompt, defer
|
||||
// processing this form until the user handles the prompt.
|
||||
if (Services.logins.uiBusy) {
|
||||
log("deferring onFormPassword for", doc.documentURI);
|
||||
let self = this;
|
||||
let observer = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
log("Got deferred onFormPassword notification:", topic);
|
||||
// Only run observer once.
|
||||
Services.obs.removeObserver(this, "passwordmgr-crypto-login");
|
||||
Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled");
|
||||
if (topic == "passwordmgr-crypto-loginCanceled")
|
||||
return;
|
||||
self.onFormPassword(event);
|
||||
},
|
||||
handleEvent : function (event) {
|
||||
// Not expected to be called
|
||||
}
|
||||
};
|
||||
// Trickyness follows: We want an observer, but don't want it to
|
||||
// cause leaks. So add the observer with a weak reference, and use
|
||||
// a dummy event listener (a strong reference) to keep it alive
|
||||
// until the form is destroyed.
|
||||
Services.obs.addObserver(observer, "passwordmgr-crypto-login", true);
|
||||
Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", true);
|
||||
form.addEventListener("mozCleverClosureHack", observer);
|
||||
return;
|
||||
}
|
||||
|
||||
let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView);
|
||||
|
||||
this._fillForm(form, autofillForm, false, false, false, null);
|
||||
},
|
||||
|
||||
loginsFound: function({ form, loginsFound }) {
|
||||
let doc = form.ownerDocument;
|
||||
let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView);
|
||||
|
||||
this._fillForm(form, autofillForm, false, false, false, loginsFound);
|
||||
},
|
||||
|
||||
/*
|
||||
* onUsernameInput
|
||||
|
@ -291,11 +191,12 @@ var LoginManagerContent = {
|
|||
var [usernameField, passwordField, ignored] =
|
||||
this._getFormFields(acForm, false);
|
||||
if (usernameField == acInputField && passwordField) {
|
||||
this._asyncFindLogins(acForm, { showMasterPassword: false })
|
||||
.then(({ form, loginsFound }) => {
|
||||
this._fillForm(form, true, true, true, true, loginsFound);
|
||||
})
|
||||
.then(null, Cu.reportError);
|
||||
// If the user has a master password but itsn't logged in, bail
|
||||
// out now to prevent annoying prompts.
|
||||
if (!Services.logins.isLoggedIn)
|
||||
return;
|
||||
|
||||
this._fillForm(acForm, true, true, true, true, null);
|
||||
} else {
|
||||
// Ignore the event, it's for some input we don't care about.
|
||||
}
|
||||
|
@ -478,6 +379,15 @@ var LoginManagerContent = {
|
|||
* our stored password.
|
||||
*/
|
||||
_onFormSubmit : function (form) {
|
||||
|
||||
// For E10S this will need to move.
|
||||
function getPrompter(aWindow) {
|
||||
var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
|
||||
createInstance(Ci.nsILoginManagerPrompter);
|
||||
prompterSvc.init(aWindow);
|
||||
return prompterSvc;
|
||||
}
|
||||
|
||||
var doc = form.ownerDocument;
|
||||
var win = doc.defaultView;
|
||||
|
||||
|
@ -507,6 +417,11 @@ var LoginManagerContent = {
|
|||
}
|
||||
|
||||
var formSubmitURL = LoginUtils._getActionOrigin(form)
|
||||
if (!Services.logins.getLoginSavingEnabled(hostname)) {
|
||||
log("(form submission ignored -- saving is disabled for:", hostname, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get the appropriate fields from the form.
|
||||
var [usernameField, newPasswordField, oldPasswordField] =
|
||||
|
@ -514,7 +429,7 @@ var LoginManagerContent = {
|
|||
|
||||
// Need at least 1 valid password field to do anything.
|
||||
if (newPasswordField == null)
|
||||
return;
|
||||
return;
|
||||
|
||||
// Check for autocomplete=off attribute. We don't use it to prevent
|
||||
// autofilling (for existing logins), but won't save logins when it's
|
||||
|
@ -529,27 +444,103 @@ var LoginManagerContent = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Don't try to send DOM nodes over IPC.
|
||||
let mockUsername = usernameField ?
|
||||
{ name: usernameField.name,
|
||||
value: usernameField.value } :
|
||||
null;
|
||||
let mockPassword = { name: newPasswordField.name,
|
||||
value: newPasswordField.value };
|
||||
let mockOldPassword = oldPasswordField ?
|
||||
{ name: oldPasswordField.name,
|
||||
value: oldPasswordField.value } :
|
||||
null;
|
||||
|
||||
let messageManager = messageManagerFromWindow(win);
|
||||
messageManager.sendAsyncMessage("RemoteLogins:onFormSubmit",
|
||||
{ hostname: hostname,
|
||||
formSubmitURL: formSubmitURL,
|
||||
usernameField: mockUsername,
|
||||
newPasswordField: mockPassword,
|
||||
oldPasswordField: mockOldPassword });
|
||||
var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
formLogin.init(hostname, formSubmitURL, null,
|
||||
(usernameField ? usernameField.value : ""),
|
||||
newPasswordField.value,
|
||||
(usernameField ? usernameField.name : ""),
|
||||
newPasswordField.name);
|
||||
|
||||
// If we didn't find a username field, but seem to be changing a
|
||||
// password, allow the user to select from a list of applicable
|
||||
// logins to update the password for.
|
||||
if (!usernameField && oldPasswordField) {
|
||||
|
||||
var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
|
||||
|
||||
if (logins.length == 0) {
|
||||
// Could prompt to save this as a new password-only login.
|
||||
// This seems uncommon, and might be wrong, so ignore.
|
||||
log("(no logins for this host -- pwchange ignored)");
|
||||
return;
|
||||
}
|
||||
|
||||
var prompter = getPrompter(win);
|
||||
|
||||
if (logins.length == 1) {
|
||||
var oldLogin = logins[0];
|
||||
formLogin.username = oldLogin.username;
|
||||
formLogin.usernameField = oldLogin.usernameField;
|
||||
|
||||
prompter.promptToChangePassword(oldLogin, formLogin);
|
||||
} else {
|
||||
prompter.promptToChangePasswordWithUsernames(
|
||||
logins, logins.length, formLogin);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Look for an existing login that matches the form login.
|
||||
var existingLogin = null;
|
||||
var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
|
||||
|
||||
for (var i = 0; i < logins.length; i++) {
|
||||
var same, login = logins[i];
|
||||
|
||||
// If one login has a username but the other doesn't, ignore
|
||||
// the username when comparing and only match if they have the
|
||||
// same password. Otherwise, compare the logins and match even
|
||||
// if the passwords differ.
|
||||
if (!login.username && formLogin.username) {
|
||||
var restoreMe = formLogin.username;
|
||||
formLogin.username = "";
|
||||
same = formLogin.matches(login, false);
|
||||
formLogin.username = restoreMe;
|
||||
} else if (!formLogin.username && login.username) {
|
||||
formLogin.username = login.username;
|
||||
same = formLogin.matches(login, false);
|
||||
formLogin.username = ""; // we know it's always blank.
|
||||
} else {
|
||||
same = formLogin.matches(login, true);
|
||||
}
|
||||
|
||||
if (same) {
|
||||
existingLogin = login;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingLogin) {
|
||||
log("Found an existing login matching this form submission");
|
||||
|
||||
// Change password if needed.
|
||||
if (existingLogin.password != formLogin.password) {
|
||||
log("...passwords differ, prompting to change.");
|
||||
prompter = getPrompter(win);
|
||||
prompter.promptToChangePassword(existingLogin, formLogin);
|
||||
} else {
|
||||
// Update the lastUsed timestamp.
|
||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
propBag.setProperty("timeLastUsed", Date.now());
|
||||
propBag.setProperty("timesUsedIncrement", 1);
|
||||
Services.logins.modifyLogin(existingLogin, propBag);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Prompt user to save login (via dialog or notification bar)
|
||||
prompter = getPrompter(win);
|
||||
prompter.promptToSavePassword(formLogin);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* _fillform
|
||||
*
|
||||
|
@ -585,6 +576,17 @@ var LoginManagerContent = {
|
|||
return [false, foundLogins];
|
||||
}
|
||||
|
||||
// Need to get a list of logins if we weren't given them
|
||||
if (foundLogins == null) {
|
||||
var formOrigin =
|
||||
LoginUtils._getPasswordOrigin(form.ownerDocument.documentURI);
|
||||
var actionOrigin = LoginUtils._getActionOrigin(form);
|
||||
foundLogins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
|
||||
log("found", foundLogins.length, "matching logins.");
|
||||
} else {
|
||||
log("reusing logins from last form.");
|
||||
}
|
||||
|
||||
// Discard logins which have username/password values that don't
|
||||
// fit into the fields (as specified by the maxlength attribute).
|
||||
// The user couldn't enter these values anyway, and it helps
|
||||
|
@ -598,15 +600,6 @@ var LoginManagerContent = {
|
|||
if (passwordField.maxLength >= 0)
|
||||
maxPasswordLen = passwordField.maxLength;
|
||||
|
||||
foundLogins = foundLogins.map(login => {
|
||||
var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
formLogin.init(login.hostname, login.formSubmitURL,
|
||||
login.httpRealm, login.username,
|
||||
login.password, login.usernameField,
|
||||
login.passwordField);
|
||||
return formLogin;
|
||||
});
|
||||
var logins = foundLogins.filter(function (l) {
|
||||
var fit = (l.username.length <= maxUsernameLen &&
|
||||
l.password.length <= maxPasswordLen);
|
||||
|
@ -799,7 +792,10 @@ var LoginManagerContent = {
|
|||
|
||||
};
|
||||
|
||||
var LoginUtils = {
|
||||
|
||||
|
||||
|
||||
LoginUtils = {
|
||||
/*
|
||||
* _getPasswordOrigin
|
||||
*
|
||||
|
@ -845,93 +841,3 @@ var LoginUtils = {
|
|||
},
|
||||
|
||||
};
|
||||
|
||||
// nsIAutoCompleteResult implementation
|
||||
function UserAutoCompleteResult (aSearchString, matchingLogins) {
|
||||
function loginSort(a,b) {
|
||||
var userA = a.username.toLowerCase();
|
||||
var userB = b.username.toLowerCase();
|
||||
|
||||
if (userA < userB)
|
||||
return -1;
|
||||
|
||||
if (userB > userA)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
this.searchString = aSearchString;
|
||||
this.logins = matchingLogins.sort(loginSort);
|
||||
this.matchCount = matchingLogins.length;
|
||||
|
||||
if (this.matchCount > 0) {
|
||||
this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
|
||||
this.defaultIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
UserAutoCompleteResult.prototype = {
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
// private
|
||||
logins : null,
|
||||
|
||||
// Allow autoCompleteSearch to get at the JS object so it can
|
||||
// modify some readonly properties for internal use.
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
},
|
||||
|
||||
// Interfaces from idl...
|
||||
searchString : null,
|
||||
searchResult : Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
|
||||
defaultIndex : -1,
|
||||
errorDescription : "",
|
||||
matchCount : 0,
|
||||
|
||||
getValueAt : function (index) {
|
||||
if (index < 0 || index >= this.logins.length)
|
||||
throw "Index out of range.";
|
||||
|
||||
return this.logins[index].username;
|
||||
},
|
||||
|
||||
getLabelAt: function(index) {
|
||||
return this.getValueAt(index);
|
||||
},
|
||||
|
||||
getCommentAt : function (index) {
|
||||
return "";
|
||||
},
|
||||
|
||||
getStyleAt : function (index) {
|
||||
return "";
|
||||
},
|
||||
|
||||
getImageAt : function (index) {
|
||||
return "";
|
||||
},
|
||||
|
||||
getFinalCompleteValueAt : function (index) {
|
||||
return this.getValueAt(index);
|
||||
},
|
||||
|
||||
removeValueAt : function (index, removeFromDB) {
|
||||
if (index < 0 || index >= this.logins.length)
|
||||
throw "Index out of range.";
|
||||
|
||||
var [removedLogin] = this.logins.splice(index, 1);
|
||||
|
||||
this.matchCount--;
|
||||
if (this.defaultIndex > this.logins.length)
|
||||
this.defaultIndex--;
|
||||
|
||||
if (removeFromDB) {
|
||||
var pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
pwmgr.removeLogin(removedLogin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,321 +0,0 @@
|
|||
/* vim: set ts=4 sts=4 sw=4 et tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UserAutoCompleteResult",
|
||||
"resource://gre/modules/LoginManagerContent.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AutoCompleteE10S",
|
||||
"resource://gre/modules/AutoCompleteE10S.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "LoginManagerParent" ];
|
||||
|
||||
var gDebug;
|
||||
|
||||
function log(...pieces) {
|
||||
function generateLogMessage(args) {
|
||||
let strings = ['Login Manager (parent):'];
|
||||
|
||||
args.forEach(function(arg) {
|
||||
if (typeof arg === 'string') {
|
||||
strings.push(arg);
|
||||
} else if (typeof arg === 'undefined') {
|
||||
strings.push('undefined');
|
||||
} else if (arg === null) {
|
||||
strings.push('null');
|
||||
} else {
|
||||
try {
|
||||
strings.push(JSON.stringify(arg, null, 2));
|
||||
} catch(err) {
|
||||
strings.push("<<something>>");
|
||||
}
|
||||
}
|
||||
});
|
||||
return strings.join(' ');
|
||||
}
|
||||
|
||||
if (!gDebug)
|
||||
return;
|
||||
|
||||
let message = generateLogMessage(pieces);
|
||||
dump(message + "\n");
|
||||
Services.console.logStringMessage(message);
|
||||
}
|
||||
|
||||
function prefChanged() {
|
||||
gDebug = Services.prefs.getBoolPref("signon.debug");
|
||||
}
|
||||
|
||||
Services.prefs.addObserver("signon.debug", prefChanged, false);
|
||||
prefChanged();
|
||||
|
||||
var LoginManagerParent = {
|
||||
init: function() {
|
||||
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageListenerManager);
|
||||
mm.addMessageListener("RemoteLogins:findLogins", this);
|
||||
mm.addMessageListener("RemoteLogins:onFormSubmit", this);
|
||||
mm.addMessageListener("RemoteLogins:autoCompleteLogins", this);
|
||||
},
|
||||
|
||||
receiveMessage: function (msg) {
|
||||
let data = msg.data;
|
||||
|
||||
switch (msg.name) {
|
||||
case "RemoteLogins:findLogins": {
|
||||
// TODO Verify msg.target's principals against the formOrigin?
|
||||
this.findLogins(data.options.showMasterPassword,
|
||||
data.formOrigin,
|
||||
data.actionOrigin,
|
||||
data.requestId,
|
||||
msg.target.messageManager);
|
||||
break;
|
||||
}
|
||||
|
||||
case "RemoteLogins:onFormSubmit": {
|
||||
// TODO Verify msg.target's principals against the formOrigin?
|
||||
this.onFormSubmit(data.hostname,
|
||||
data.formSubmitURL,
|
||||
data.usernameField,
|
||||
data.newPasswordField,
|
||||
data.oldPasswordField,
|
||||
msg.target);
|
||||
break;
|
||||
}
|
||||
|
||||
case "RemoteLogins:autoCompleteLogins": {
|
||||
this.doAutocompleteSearch(data, msg.target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
findLogins: function(showMasterPassword, formOrigin, actionOrigin,
|
||||
requestId, target) {
|
||||
if (!showMasterPassword && !Services.logins.isLoggedIn) {
|
||||
target.sendAsyncMessage("RemoteLogins:loginsFound",
|
||||
{ requestId: requestId, logins: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are no logins for this site, bail out now.
|
||||
if (!Services.logins.countLogins(formOrigin, "", null)) {
|
||||
target.sendAsyncMessage("RemoteLogins:loginsFound",
|
||||
{ requestId: requestId, logins: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're currently displaying a master password prompt, defer
|
||||
// processing this form until the user handles the prompt.
|
||||
if (Services.logins.uiBusy) {
|
||||
log("deferring onFormPassword for", formOrigin);
|
||||
let self = this;
|
||||
let observer = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
log("Got deferred onFormPassword notification:", topic);
|
||||
// Only run observer once.
|
||||
Services.obs.removeObserver(this, "passwordmgr-crypto-login");
|
||||
Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled");
|
||||
if (topic == "passwordmgr-crypto-loginCanceled") {
|
||||
target.sendAsyncMessage("RemoteLogins:loginsFound",
|
||||
{ requestId: requestId, logins: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
self.findLogins(showMasterPassword, formOrigin, actionOrigin,
|
||||
requestId, target);
|
||||
},
|
||||
};
|
||||
|
||||
// Possible leak: it's possible that neither of these notifications
|
||||
// will fire, and if that happens, we'll leak the observer (and
|
||||
// never return). We should guarantee that at least one of these
|
||||
// will fire.
|
||||
// See bug XXX.
|
||||
Services.obs.addObserver(observer, "passwordmgr-crypto-login", false);
|
||||
Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", false);
|
||||
return;
|
||||
}
|
||||
|
||||
var logins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
|
||||
target.sendAsyncMessage("RemoteLogins:loginsFound",
|
||||
{ requestId: requestId, logins: logins });
|
||||
},
|
||||
|
||||
doAutocompleteSearch: function({ formOrigin, actionOrigin,
|
||||
searchString, previousResult,
|
||||
rect, requestId, remote }, target) {
|
||||
// Note: previousResult is a regular object, not an
|
||||
// nsIAutoCompleteResult.
|
||||
var result;
|
||||
var matchingLogins;
|
||||
|
||||
let searchStringLower = searchString.toLowerCase();
|
||||
let logins;
|
||||
if (previousResult &&
|
||||
searchStringLower.startsWith(previousResult.searchString.toLowerCase())) {
|
||||
log("Using previous autocomplete result");
|
||||
|
||||
// We have a list of results for a shorter search string, so just
|
||||
// filter them further based on the new search string.
|
||||
logins = previousResult.logins;
|
||||
} else {
|
||||
log("Creating new autocomplete search result.");
|
||||
|
||||
// Grab the logins from the database.
|
||||
logins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
|
||||
}
|
||||
|
||||
let matchingLogins = logins.filter(function(fullMatch) {
|
||||
let match = fullMatch.username;
|
||||
|
||||
// Remove results that are too short, or have different prefix.
|
||||
// Also don't offer empty usernames as possible results.
|
||||
return match && match.toLowerCase().startsWith(searchStringLower);
|
||||
});
|
||||
|
||||
// XXX In the E10S case, we're responsible for showing our own
|
||||
// autocomplete popup here because the autocomplete protocol hasn't
|
||||
// been e10s-ized yet. In the non-e10s case, our caller is responsible
|
||||
// for showing the autocomplete popup (via the regular
|
||||
// nsAutoCompleteController).
|
||||
if (remote) {
|
||||
result = new UserAutoCompleteResult(searchString, matchingLogins);
|
||||
AutoCompleteE10S.showPopupWithResults(target.ownerDocument.defaultView, rect, result);
|
||||
}
|
||||
|
||||
target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted",
|
||||
{ requestId: requestId,
|
||||
logins: matchingLogins });
|
||||
},
|
||||
|
||||
onFormSubmit: function(hostname, formSubmitURL,
|
||||
usernameField, newPasswordField,
|
||||
oldPasswordField,
|
||||
target) {
|
||||
function getPrompter() {
|
||||
var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"].
|
||||
createInstance(Ci.nsILoginManagerPrompter);
|
||||
// XXX For E10S, we don't want to use the browser's contentWindow
|
||||
// because it's in another process, so we use our chrome window as
|
||||
// the window parent (the content process is responsible for
|
||||
// making sure that its window is not in private browsing mode).
|
||||
// In the same-process case, we can simply use the content window.
|
||||
prompterSvc.init(target.isRemoteBrowser ?
|
||||
target.ownerDocument.defaultView :
|
||||
target.contentWindow);
|
||||
return prompterSvc;
|
||||
}
|
||||
|
||||
if (!Services.logins.getLoginSavingEnabled(hostname)) {
|
||||
log("(form submission ignored -- saving is disabled for:", hostname, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
var formLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
||||
createInstance(Ci.nsILoginInfo);
|
||||
formLogin.init(hostname, formSubmitURL, null,
|
||||
(usernameField ? usernameField.value : ""),
|
||||
newPasswordField.value,
|
||||
(usernameField ? usernameField.name : ""),
|
||||
newPasswordField.name);
|
||||
|
||||
// If we didn't find a username field, but seem to be changing a
|
||||
// password, allow the user to select from a list of applicable
|
||||
// logins to update the password for.
|
||||
if (!usernameField && oldPasswordField) {
|
||||
|
||||
var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
|
||||
|
||||
if (logins.length == 0) {
|
||||
// Could prompt to save this as a new password-only login.
|
||||
// This seems uncommon, and might be wrong, so ignore.
|
||||
log("(no logins for this host -- pwchange ignored)");
|
||||
return;
|
||||
}
|
||||
|
||||
var prompter = getPrompter();
|
||||
|
||||
if (logins.length == 1) {
|
||||
var oldLogin = logins[0];
|
||||
formLogin.username = oldLogin.username;
|
||||
formLogin.usernameField = oldLogin.usernameField;
|
||||
|
||||
prompter.promptToChangePassword(oldLogin, formLogin);
|
||||
} else {
|
||||
prompter.promptToChangePasswordWithUsernames(
|
||||
logins, logins.length, formLogin);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Look for an existing login that matches the form login.
|
||||
var existingLogin = null;
|
||||
var logins = Services.logins.findLogins({}, hostname, formSubmitURL, null);
|
||||
|
||||
for (var i = 0; i < logins.length; i++) {
|
||||
var same, login = logins[i];
|
||||
|
||||
// If one login has a username but the other doesn't, ignore
|
||||
// the username when comparing and only match if they have the
|
||||
// same password. Otherwise, compare the logins and match even
|
||||
// if the passwords differ.
|
||||
if (!login.username && formLogin.username) {
|
||||
var restoreMe = formLogin.username;
|
||||
formLogin.username = "";
|
||||
same = formLogin.matches(login, false);
|
||||
formLogin.username = restoreMe;
|
||||
} else if (!formLogin.username && login.username) {
|
||||
formLogin.username = login.username;
|
||||
same = formLogin.matches(login, false);
|
||||
formLogin.username = ""; // we know it's always blank.
|
||||
} else {
|
||||
same = formLogin.matches(login, true);
|
||||
}
|
||||
|
||||
if (same) {
|
||||
existingLogin = login;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingLogin) {
|
||||
log("Found an existing login matching this form submission");
|
||||
|
||||
// Change password if needed.
|
||||
if (existingLogin.password != formLogin.password) {
|
||||
log("...passwords differ, prompting to change.");
|
||||
prompter = getPrompter();
|
||||
prompter.promptToChangePassword(existingLogin, formLogin);
|
||||
} else {
|
||||
// Update the lastUsed timestamp.
|
||||
var propBag = Cc["@mozilla.org/hash-property-bag;1"].
|
||||
createInstance(Ci.nsIWritablePropertyBag);
|
||||
propBag.setProperty("timeLastUsed", Date.now());
|
||||
propBag.setProperty("timesUsedIncrement", 1);
|
||||
Services.logins.modifyLogin(existingLogin, propBag);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Prompt user to save login (via dialog or notification bar)
|
||||
prompter = getPrompter();
|
||||
prompter.promptToSavePassword(formLogin);
|
||||
}
|
||||
};
|
|
@ -189,14 +189,6 @@ LoginStore.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
// In some rare cases it's possible for logins to have been added to
|
||||
// our database between the call to OS.File.read and when we've been
|
||||
// notified that there was a problem with it. In that case, leave the
|
||||
// synchronously-added data alone. See bug 1029128, comment 4.
|
||||
if (this.dataReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In any case, initialize a new object to host the data.
|
||||
this.data = {
|
||||
nextId: 1,
|
||||
|
|
|
@ -35,7 +35,6 @@ EXTRA_JS_MODULES += [
|
|||
'InsecurePasswordUtils.jsm',
|
||||
'LoginHelper.jsm',
|
||||
'LoginManagerContent.jsm',
|
||||
'LoginManagerParent.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
interface nsIURI;
|
||||
interface nsILoginInfo;
|
||||
interface nsIAutoCompleteResult;
|
||||
interface nsIFormAutoCompleteObserver;
|
||||
interface nsIDOMHTMLInputElement;
|
||||
interface nsIDOMHTMLFormElement;
|
||||
interface nsIPropertyBag;
|
||||
|
||||
[scriptable, uuid(f0c5ca21-db71-4b32-993e-ab63054cc6f5)]
|
||||
[scriptable, uuid(f5f2a39a-dffe-4eb9-ad28-340afd53b1a3)]
|
||||
|
||||
interface nsILoginManager : nsISupports {
|
||||
/**
|
||||
* This promise is resolved when initialization is complete, and is rejected
|
||||
|
@ -210,10 +210,9 @@ interface nsILoginManager : nsISupports {
|
|||
* which calls it directly. This isn't really ideal, it should
|
||||
* probably be callback registered through the FFC.
|
||||
*/
|
||||
void autoCompleteSearchAsync(in AString aSearchString,
|
||||
in nsIAutoCompleteResult aPreviousResult,
|
||||
in nsIDOMHTMLInputElement aElement,
|
||||
in nsIFormAutoCompleteObserver aListener);
|
||||
nsIAutoCompleteResult autoCompleteSearch(in AString aSearchString,
|
||||
in nsIAutoCompleteResult aPreviousResult,
|
||||
in nsIDOMHTMLInputElement aElement);
|
||||
|
||||
/**
|
||||
* Fill a form with login information if we have it. This method will fill
|
||||
|
@ -221,9 +220,9 @@ interface nsILoginManager : nsISupports {
|
|||
*
|
||||
* @param aForm
|
||||
* The form to fill
|
||||
* @return Promise that is resolved with whether or not the form was filled.
|
||||
* @return Success of attempt fill form
|
||||
*/
|
||||
jsval fillForm(in nsIDOMHTMLFormElement aForm);
|
||||
boolean fillForm(in nsIDOMHTMLFormElement aForm);
|
||||
|
||||
/**
|
||||
* Search for logins in the login manager. An array is always returned;
|
||||
|
|
|
@ -5,20 +5,17 @@
|
|||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
Cu.import("resource://gre/modules/LoginManagerContent.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
|
||||
"resource://gre/modules/LoginManagerContent.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
|
||||
var debug = false;
|
||||
function log(...pieces) {
|
||||
|
@ -119,17 +116,11 @@ LoginManager.prototype = {
|
|||
|
||||
// Form submit observer checks forms for new logins and pw changes.
|
||||
Services.obs.addObserver(this._observer, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this._observer, "passwordmgr-storage-replace",
|
||||
false);
|
||||
|
||||
// TODO: Make this class useful in the child process (in addition to
|
||||
// autoCompleteSearchAsync and fillForm).
|
||||
if (Services.appinfo.processType ===
|
||||
Services.appinfo.PROCESS_TYPE_DEFAULT) {
|
||||
Services.obs.addObserver(this._observer, "passwordmgr-storage-replace",
|
||||
false);
|
||||
|
||||
// Initialize storage so that asynchronous data loading can start.
|
||||
this._initStorage();
|
||||
}
|
||||
// Initialize storage so that asynchronous data loading can start.
|
||||
this._initStorage();
|
||||
},
|
||||
|
||||
|
||||
|
@ -417,53 +408,91 @@ LoginManager.prototype = {
|
|||
return this._storage.setLoginSavingEnabled(hostname, enabled);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* autoCompleteSearchAsync
|
||||
* autoCompleteSearch
|
||||
*
|
||||
* Yuck. This is called directly by satchel:
|
||||
* nsFormFillController::StartSearch()
|
||||
* [toolkit/components/satchel/nsFormFillController.cpp]
|
||||
* [toolkit/components/satchel/src/nsFormFillController.cpp]
|
||||
*
|
||||
* We really ought to have a simple way for code to register an
|
||||
* auto-complete provider, and not have satchel calling pwmgr directly.
|
||||
*/
|
||||
autoCompleteSearchAsync : function (aSearchString, aPreviousResult,
|
||||
aElement, aCallback) {
|
||||
// aPreviousResult is an nsIAutoCompleteResult, aElement is
|
||||
// nsIDOMHTMLInputElement
|
||||
autoCompleteSearch : function (aSearchString, aPreviousResult, aElement) {
|
||||
// aPreviousResult & aResult are nsIAutoCompleteResult,
|
||||
// aElement is nsIDOMHTMLInputElement
|
||||
|
||||
if (!this._remember) {
|
||||
setTimeout(function() {
|
||||
aCallback.onSearchCompletion(new UserAutoCompleteResult(aSearchString, []));
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
if (!this._remember)
|
||||
return null;
|
||||
|
||||
log("AutoCompleteSearch invoked. Search is:", aSearchString);
|
||||
|
||||
var previousResult;
|
||||
if (aPreviousResult) {
|
||||
previousResult = { searchString: aPreviousResult.searchString,
|
||||
logins: aPreviousResult.wrappedJSObject.logins };
|
||||
var result = null;
|
||||
|
||||
if (aPreviousResult &&
|
||||
aSearchString.substr(0, aPreviousResult.searchString.length) == aPreviousResult.searchString) {
|
||||
log("Using previous autocomplete result");
|
||||
result = aPreviousResult;
|
||||
result.wrappedJSObject.searchString = aSearchString;
|
||||
|
||||
// We have a list of results for a shorter search string, so just
|
||||
// filter them further based on the new search string.
|
||||
// Count backwards, because result.matchCount is decremented
|
||||
// when we remove an entry.
|
||||
for (var i = result.matchCount - 1; i >= 0; i--) {
|
||||
var match = result.getValueAt(i);
|
||||
|
||||
// Remove results that are too short, or have different prefix.
|
||||
if (aSearchString.length > match.length ||
|
||||
aSearchString.toLowerCase() !=
|
||||
match.substr(0, aSearchString.length).toLowerCase())
|
||||
{
|
||||
log("Removing autocomplete entry:", match);
|
||||
result.removeValueAt(i, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
previousResult = null;
|
||||
log("Creating new autocomplete search result.");
|
||||
|
||||
var doc = aElement.ownerDocument;
|
||||
var origin = this._getPasswordOrigin(doc.documentURI);
|
||||
var actionOrigin = this._getActionOrigin(aElement.form);
|
||||
|
||||
// This shouldn't trigger a master password prompt, because we
|
||||
// don't attach to the input until after we successfully obtain
|
||||
// logins for the form.
|
||||
var logins = this.findLogins({}, origin, actionOrigin, null);
|
||||
var matchingLogins = [];
|
||||
|
||||
// Filter out logins that don't match the search prefix. Also
|
||||
// filter logins without a username, since that's confusing to see
|
||||
// in the dropdown and we can't autocomplete them anyway.
|
||||
for (i = 0; i < logins.length; i++) {
|
||||
var username = logins[i].username.toLowerCase();
|
||||
if (username &&
|
||||
aSearchString.length <= username.length &&
|
||||
aSearchString.toLowerCase() ==
|
||||
username.substr(0, aSearchString.length))
|
||||
{
|
||||
matchingLogins.push(logins[i]);
|
||||
}
|
||||
}
|
||||
log(matchingLogins.length, "autocomplete logins avail.");
|
||||
result = new UserAutoCompleteResult(aSearchString, matchingLogins);
|
||||
}
|
||||
|
||||
let rect = BrowserUtils.getElementBoundingScreenRect(aElement);
|
||||
LoginManagerContent._autoCompleteSearchAsync(aSearchString, previousResult,
|
||||
aElement, rect)
|
||||
.then(function(logins) {
|
||||
let results =
|
||||
new UserAutoCompleteResult(aSearchString, logins);
|
||||
aCallback.onSearchCompletion(results);
|
||||
})
|
||||
.then(null, Cu.reportError);
|
||||
return result;
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------- Internal methods / callbacks for document integration ------- */
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* _getPasswordOrigin
|
||||
*
|
||||
|
@ -516,13 +545,102 @@ LoginManager.prototype = {
|
|||
*/
|
||||
fillForm : function (form) {
|
||||
log("fillForm processing form[ id:", form.id, "]");
|
||||
return LoginManagerContent._asyncFindLogins(form, { showMasterPassword: true })
|
||||
.then(function({ form, loginsFound }) {
|
||||
return LoginManagerContent._fillForm(form, true, true,
|
||||
false, false, loginsFound)[0];
|
||||
});
|
||||
return LoginManagerContent._fillForm(form, true, true, false, false, null)[0];
|
||||
},
|
||||
|
||||
}; // end of LoginManager implementation
|
||||
|
||||
|
||||
|
||||
|
||||
// nsIAutoCompleteResult implementation
|
||||
function UserAutoCompleteResult (aSearchString, matchingLogins) {
|
||||
function loginSort(a,b) {
|
||||
var userA = a.username.toLowerCase();
|
||||
var userB = b.username.toLowerCase();
|
||||
|
||||
if (userA < userB)
|
||||
return -1;
|
||||
|
||||
if (userB > userA)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
this.searchString = aSearchString;
|
||||
this.logins = matchingLogins.sort(loginSort);
|
||||
this.matchCount = matchingLogins.length;
|
||||
|
||||
if (this.matchCount > 0) {
|
||||
this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
|
||||
this.defaultIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
UserAutoCompleteResult.prototype = {
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
// private
|
||||
logins : null,
|
||||
|
||||
// Allow autoCompleteSearch to get at the JS object so it can
|
||||
// modify some readonly properties for internal use.
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
},
|
||||
|
||||
// Interfaces from idl...
|
||||
searchString : null,
|
||||
searchResult : Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
|
||||
defaultIndex : -1,
|
||||
errorDescription : "",
|
||||
matchCount : 0,
|
||||
|
||||
getValueAt : function (index) {
|
||||
if (index < 0 || index >= this.logins.length)
|
||||
throw "Index out of range.";
|
||||
|
||||
return this.logins[index].username;
|
||||
},
|
||||
|
||||
getLabelAt: function(index) {
|
||||
return this.getValueAt(index);
|
||||
},
|
||||
|
||||
getCommentAt : function (index) {
|
||||
return "";
|
||||
},
|
||||
|
||||
getStyleAt : function (index) {
|
||||
return "";
|
||||
},
|
||||
|
||||
getImageAt : function (index) {
|
||||
return "";
|
||||
},
|
||||
|
||||
getFinalCompleteValueAt : function (index) {
|
||||
return this.getValueAt(index);
|
||||
},
|
||||
|
||||
removeValueAt : function (index, removeFromDB) {
|
||||
if (index < 0 || index >= this.logins.length)
|
||||
throw "Index out of range.";
|
||||
|
||||
var [removedLogin] = this.logins.splice(index, 1);
|
||||
|
||||
this.matchCount--;
|
||||
if (this.defaultIndex > this.logins.length)
|
||||
this.defaultIndex--;
|
||||
|
||||
if (removeFromDB) {
|
||||
var pwmgr = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
pwmgr.removeLogin(removedLogin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([LoginManager]);
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -139,10 +137,12 @@ this.LoginManagerStorage_json.prototype = {
|
|||
addLogin : function (login) {
|
||||
this._store.ensureDataReady();
|
||||
|
||||
let encUsername, encPassword;
|
||||
|
||||
// Throws if there are bogus values.
|
||||
LoginHelper.checkLoginValues(login);
|
||||
|
||||
let [encUsername, encPassword, encType] = this._encryptLogin(login);
|
||||
[encUsername, encPassword, encType] = this._encryptLogin(login);
|
||||
|
||||
// Clone the login, so we don't modify the caller's object.
|
||||
let loginClone = login.clone();
|
||||
|
|
|
@ -128,10 +128,7 @@ function doKey(aKey, modifier) {
|
|||
}
|
||||
|
||||
// Init with a common login
|
||||
// If selfFilling is true or non-undefined, fires an event at the page so that
|
||||
// the test can start checking filled-in values. Tests that check observer
|
||||
// notifications might be confused by this.
|
||||
function commonInit(selfFilling) {
|
||||
function commonInit() {
|
||||
var pwmgr = SpecialPowers.Cc["@mozilla.org/login-manager;1"].
|
||||
getService(SpecialPowers.Ci.nsILoginManager);
|
||||
ok(pwmgr != null, "Access LoginManager");
|
||||
|
@ -162,41 +159,6 @@ function commonInit(selfFilling) {
|
|||
is(logins.length, 1, "Checking for successful init login");
|
||||
disabledHosts = pwmgr.getAllDisabledHosts();
|
||||
is(disabledHosts.length, 0, "Checking for no disabled hosts");
|
||||
|
||||
if (selfFilling)
|
||||
return;
|
||||
|
||||
// We provide a general mechanism for our tests to know when they can
|
||||
// safely run: we add a final form that we know will be filled in, wait
|
||||
// for the login manager to tell us that it's filled in and then continue
|
||||
// with the rest of the tests.
|
||||
window.addEventListener("DOMContentLoaded", (event) => {
|
||||
var form = document.createElement('form');
|
||||
form.id = 'observerforcer';
|
||||
var username = document.createElement('input');
|
||||
username.name = 'testuser';
|
||||
form.appendChild(username);
|
||||
var password = document.createElement('input');
|
||||
password.name = 'testpass';
|
||||
password.type = 'password';
|
||||
form.appendChild(password);
|
||||
|
||||
var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
|
||||
var bag = subject.QueryInterface(SpecialPowers.Ci.nsIPropertyBag2);
|
||||
var username = bag.get("usernameField");
|
||||
if (!username || username.form.id !== 'observerforcer')
|
||||
return;
|
||||
SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
|
||||
form.parentNode.removeChild(form);
|
||||
SimpleTest.executeSoon(() => {
|
||||
var event = new Event("runTests");
|
||||
window.dispatchEvent(event);
|
||||
});
|
||||
});
|
||||
SpecialPowers.addObserver(observer, "passwordmgr-found-logins", false);
|
||||
|
||||
document.body.appendChild(form);
|
||||
});
|
||||
}
|
||||
|
||||
const masterPassword = "omgsecret!";
|
||||
|
|
|
@ -19,8 +19,8 @@ function submitForm() {
|
|||
form.submit();
|
||||
}
|
||||
|
||||
window.onload = submitForm;
|
||||
var form = document.getElementById("form");
|
||||
window.addEventListener('message', () => { submitForm(); });
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -28,11 +28,10 @@ function submitForm() {
|
|||
setTimeout(function(){ form.submit(); }, 100);
|
||||
}
|
||||
|
||||
window.onload = startAutocomplete;
|
||||
var form = document.getElementById("form");
|
||||
var userField = document.getElementById("user");
|
||||
|
||||
window.addEventListener('message', () => { startAutocomplete(); });
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -22,7 +22,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
|
|
@ -65,7 +65,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
|
|
@ -161,7 +161,7 @@ function startTest() {
|
|||
}
|
||||
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
|
|
@ -102,7 +102,7 @@ function startTest() {
|
|||
}
|
||||
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
|
|
@ -44,21 +44,19 @@ function startTest(){
|
|||
is($_(1, "pword").value, "", "Checking for blank password");
|
||||
|
||||
// Call the public method, check return value
|
||||
pwmgr.fillForm(document.getElementById("form1"))
|
||||
.then(function(result) {
|
||||
is(result, true, "Checking return value of fillForm");
|
||||
is(pwmgr.fillForm(document.getElementById("form1")), true,
|
||||
"Checking return value of fillForm");
|
||||
|
||||
// Check that the form was filled
|
||||
is($_(1, "uname").value, "testuser", "Checking for filled username");
|
||||
is($_(1, "pword").value, "testpass", "Checking for filled password");
|
||||
// Check that the form was filled
|
||||
is($_(1, "uname").value, "testuser", "Checking for filled username");
|
||||
is($_(1, "pword").value, "testpass", "Checking for filled password");
|
||||
|
||||
// Reset pref (since we assumed it was true to start)
|
||||
SpecialPowers.setBoolPref("signon.autofillForms", true);
|
||||
// Reset pref (since we assumed it was true to start)
|
||||
SpecialPowers.setBoolPref("signon.autofillForms", true);
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
SimpleTest.finish();
|
||||
}
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -182,7 +182,7 @@ function startTest() {
|
|||
}
|
||||
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
|
|
@ -10,6 +10,18 @@
|
|||
Login Manager test: (placeholder)
|
||||
<p id="display"></p>
|
||||
|
||||
<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>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
@ -106,23 +118,11 @@ var pwmgr = Cc_pwmgr.getService(Ci_pwmgr);
|
|||
ok(pwmgr != null, "pwmgr getService()");
|
||||
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</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>
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -164,7 +164,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -60,10 +60,8 @@ var TestObserver = {
|
|||
this.receivedNotification2 = true;
|
||||
this.data2 = data;
|
||||
}
|
||||
|
||||
// Now fill the form
|
||||
pwmgr.fillForm(subject)
|
||||
.then(startTest);
|
||||
pwmgr.fillForm(subject);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -97,6 +95,8 @@ function startTest(){
|
|||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<body>
|
||||
Login Manager test: simple form with autofillForms disabled and notifying observers
|
||||
<script>
|
||||
commonInit(true);
|
||||
commonInit();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const Cc = SpecialPowers.Cc;
|
||||
|
@ -31,16 +31,9 @@ var TestObserver = {
|
|||
this.receivedNotificationFoundForm = true;
|
||||
this.dataFoundForm = data;
|
||||
// Now fill the form
|
||||
pwmgr.fillForm(subject)
|
||||
.then(window.startTest)
|
||||
.then(null, function(e) { alert(e); });
|
||||
pwmgr.fillForm(subject);
|
||||
} else if (topic == "passwordmgr-found-logins") {
|
||||
info("got passwordmgr-found-logins");
|
||||
|
||||
// We only care about the first notification (the second comes from our
|
||||
// own call to pwmgr.fillForm.
|
||||
if (this.receivedNotificationFoundLogins)
|
||||
return;
|
||||
this.receivedNotificationFoundLogins = true;
|
||||
this.dataFoundLogins = subject.QueryInterface(Ci.nsIPropertyBag2);
|
||||
}
|
||||
|
@ -71,7 +64,7 @@ SpecialPowers.addObserver(TestObserver, "passwordmgr-found-logins", false);
|
|||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Login Manager: simple form with autofillForms disabled and notifying observers **/
|
||||
function startTest() {
|
||||
function startTest(){
|
||||
// Test that found-form observer is notified & got correct data
|
||||
is(TestObserver.receivedNotificationFoundForm, true, "Checking found-form observer was notified");
|
||||
is(TestObserver.dataFoundForm, "noAutofillForms", "Checking found-form observer got correct data");
|
||||
|
@ -101,7 +94,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -174,7 +174,7 @@ function startTest(){
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -14,14 +14,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=355063
|
|||
|
||||
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);
|
||||
setTimeout(checkForm, 300);
|
||||
});
|
||||
addForm();
|
||||
}
|
||||
|
||||
|
@ -38,12 +30,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=355063
|
|||
is(userField.value, "testuser", "checking filled username");
|
||||
is(passField.value, "testpass", "checking filled password");
|
||||
|
||||
SpecialPowers.removeChromeEventListener("DOMFormHasPassword", checkForm);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
commonInit();
|
||||
|
||||
window.addEventListener("runTests", 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", checkForm);
|
||||
window.addEventListener("load", startTest);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
@ -211,7 +211,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -235,7 +235,7 @@ function countLogins() {
|
|||
|
||||
return logins.length;
|
||||
}
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -118,7 +118,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
|
|
@ -10,10 +10,6 @@
|
|||
Login Manager test: form with JS submit action
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Note: Call this first so it doesn't override our login.
|
||||
commonInit();
|
||||
|
||||
var pwmgr = SpecialPowers.Cc["@mozilla.org/login-manager;1"]
|
||||
.getService(SpecialPowers.Ci.nsILoginManager);
|
||||
var jslogin = SpecialPowers.Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
|
@ -31,8 +27,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// XXX
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
|
||||
<p id="display"></p>
|
||||
|
|
|
@ -127,7 +127,7 @@ function startTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -66,7 +66,6 @@ try {
|
|||
|
||||
/** Test for Login Manager: multiple login autocomplete. **/
|
||||
|
||||
var tester;
|
||||
|
||||
var uname = $_(1, "uname");
|
||||
var pword = $_(1, "pword");
|
||||
|
@ -93,6 +92,8 @@ function sendFakeAutocompleteEvent(element) {
|
|||
element.dispatchEvent(acEvent);
|
||||
}
|
||||
|
||||
var gLastTest = 6;
|
||||
|
||||
function addPopupListener(eventName, func, capture) {
|
||||
autocompletePopup.addEventListener(eventName, func, capture);
|
||||
}
|
||||
|
@ -109,81 +110,83 @@ function removePopupListener(eventName, func, capture) {
|
|||
* setTimeout() calls. The case statements are executed in order, one per
|
||||
* timeout.
|
||||
*/
|
||||
function* runTest() {
|
||||
function runNextTest() {
|
||||
addPopupListener("popupshown", function() {
|
||||
removePopupListener("popupshown", arguments.callee, false);
|
||||
function runTest(testNum) {
|
||||
ok(true, "Starting test #" + testNum);
|
||||
|
||||
window.setTimeout(tester.next.bind(tester), 0);
|
||||
}, false);
|
||||
addPopupListener("popupshown", function() {
|
||||
removePopupListener("popupshown", arguments.callee, false);
|
||||
|
||||
if (testNum != gLastTest) {
|
||||
window.setTimeout(runTest, 0, testNum + 1);
|
||||
}
|
||||
}, false);
|
||||
|
||||
switch(testNum) {
|
||||
case 1:
|
||||
// Make sure initial form is empty.
|
||||
checkACForm("", "");
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
checkACForm("name", "pass");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Check second entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return"); // not "enter"!
|
||||
checkACForm("Name", "Pass");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Check third entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkACForm("USER", "PASS");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
uname.value = "user";
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Check that we don't clobber user-entered text when tabbing away
|
||||
doKey("tab");
|
||||
checkACForm("user", "PASS");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
function waitForCompletion() {
|
||||
var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
|
||||
SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
|
||||
tester.next();
|
||||
});
|
||||
SpecialPowers.addObserver(observer, "passwordmgr-found-logins", false);
|
||||
}
|
||||
|
||||
/* test 1 */
|
||||
// Make sure initial form is empty.
|
||||
checkACForm("", "");
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
yield runNextTest();
|
||||
|
||||
/* test 2 */
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkACForm("", ""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
yield waitForCompletion();
|
||||
checkACForm("name", "pass");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
yield runNextTest();
|
||||
|
||||
/* test 3 */
|
||||
// Check second entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return"); // not "enter"!
|
||||
yield waitForCompletion();
|
||||
checkACForm("Name", "Pass");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
yield runNextTest();
|
||||
|
||||
/* test 4 */
|
||||
// Check third entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
yield waitForCompletion();
|
||||
checkACForm("USER", "PASS");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
uname.value = "user";
|
||||
doKey("down");
|
||||
yield runNextTest();
|
||||
|
||||
/* test 5 */
|
||||
// Check that we don't clobber user-entered text when tabbing away
|
||||
doKey("tab");
|
||||
yield waitForCompletion();
|
||||
checkACForm("user", "PASS");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
||||
|
@ -203,11 +206,10 @@ function startTest() {
|
|||
// shouldn't assume ID is consistent across products
|
||||
autocompletePopup = chromeWin.document.getElementById("PopupAutoComplete");
|
||||
ok(autocompletePopup, "Got autocomplete popup");
|
||||
tester = runTest();
|
||||
tester.next();
|
||||
runTest(1);
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest);
|
||||
window.onload = startTest;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -115,17 +115,14 @@ function handleDialog(doc, testNum) {
|
|||
var outerWindowObserver = {
|
||||
observe: function(id) {
|
||||
SpecialPowers.removeObserver(outerWindowObserver, "outer-window-destroyed");
|
||||
var func;
|
||||
if (testNum == 1)
|
||||
func = startTest2;
|
||||
startTest2();
|
||||
else if (testNum == 2)
|
||||
func = startTest3;
|
||||
startTest3();
|
||||
else if (testNum == 3)
|
||||
func = checkTest3;
|
||||
checkTest3();
|
||||
else if (testNum == 5)
|
||||
func = checkTest4C;
|
||||
|
||||
setTimeout(func, 300);
|
||||
checkTest4C();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -272,7 +269,7 @@ function finishTest() {
|
|||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("runTests", startTest1);
|
||||
window.onload = startTest1;
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -40,7 +40,6 @@ var subtests = [
|
|||
"subtst_privbrowsing_4.html", // 9
|
||||
"subtst_privbrowsing_3.html" // 10
|
||||
];
|
||||
var observer;
|
||||
|
||||
var testNum = 0;
|
||||
function loadNextTest() {
|
||||
|
@ -96,17 +95,11 @@ function loadNextTest() {
|
|||
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-found-logins", false);
|
||||
}
|
||||
|
||||
ok(true, "Starting test #" + testNum);
|
||||
iframe.src = prefix + subtests[testNum-1];
|
||||
}
|
||||
|
||||
|
||||
function checkTest() {
|
||||
var popup;
|
||||
|
||||
|
@ -255,7 +248,6 @@ function handleLoad(aEvent) {
|
|||
aWin.close();
|
||||
});
|
||||
|
||||
SpecialPowers.removeObserver(observer, "passwordmgr-found-logins");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,18 +75,29 @@ this.AutoCompleteE10S = {
|
|||
messageManager.addMessageListener("FormAutoComplete:ClosePopup", this);
|
||||
},
|
||||
|
||||
_initPopup: function(browserWindow, rect) {
|
||||
search: function(message) {
|
||||
let browserWindow = message.target.ownerDocument.defaultView;
|
||||
this.browser = browserWindow.gBrowser.selectedBrowser;
|
||||
this.popup = this.browser.autoCompletePopup;
|
||||
this.popup.hidden = false;
|
||||
this.popup.setAttribute("width", rect.width);
|
||||
this.popup.setAttribute("width", message.data.width);
|
||||
|
||||
let rect = message.data;
|
||||
let {x, y} = this.browser.mapScreenCoordinatesFromContent(rect.left, rect.top + rect.height);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
||||
let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"]
|
||||
.getService(Ci.nsIFormAutoComplete);
|
||||
|
||||
formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
|
||||
message.data.untrimmedSearchString,
|
||||
null,
|
||||
null,
|
||||
this.onSearchComplete.bind(this));
|
||||
},
|
||||
|
||||
_showPopup: function(results) {
|
||||
onSearchComplete: function(results) {
|
||||
AutoCompleteE10SView.clearResults();
|
||||
|
||||
let resultsArray = [];
|
||||
|
@ -99,6 +110,11 @@ this.AutoCompleteE10S = {
|
|||
|
||||
this.popup.view = AutoCompleteE10SView;
|
||||
|
||||
this.browser.messageManager.sendAsyncMessage(
|
||||
"FormAutoComplete:AutoCompleteSearchAsyncResult",
|
||||
{results: resultsArray}
|
||||
);
|
||||
|
||||
this.popup.selectedIndex = -1;
|
||||
this.popup.invalidate();
|
||||
|
||||
|
@ -112,46 +128,6 @@ this.AutoCompleteE10S = {
|
|||
} else {
|
||||
this.popup.closePopup();
|
||||
}
|
||||
|
||||
return resultsArray;
|
||||
},
|
||||
|
||||
// This function is used by the login manager, which uses a single message
|
||||
// to fill in the autocomplete results. See
|
||||
// "RemoteLogins:autoCompleteLogins".
|
||||
showPopupWithResults: function(browserWindow, rect, results) {
|
||||
this._initPopup(browserWindow, rect);
|
||||
this._showPopup(results);
|
||||
},
|
||||
|
||||
// This function is called in response to AutoComplete requests from the
|
||||
// child (received via the message manager, see
|
||||
// "FormHistory:AutoCompleteSearchAsync").
|
||||
search: function(message) {
|
||||
let browserWindow = message.target.ownerDocument.defaultView;
|
||||
let rect = message.data;
|
||||
|
||||
this._initPopup(browserWindow, rect);
|
||||
|
||||
let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"]
|
||||
.getService(Ci.nsIFormAutoComplete);
|
||||
|
||||
formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
|
||||
message.data.untrimmedSearchString,
|
||||
null,
|
||||
null,
|
||||
this.onSearchComplete.bind(this));
|
||||
},
|
||||
|
||||
// The second half of search, this fills in the popup and returns the
|
||||
// results to the child.
|
||||
onSearchComplete: function(results) {
|
||||
let resultsArray = this._showPopup(results);
|
||||
|
||||
this.browser.messageManager.sendAsyncMessage(
|
||||
"FormAutoComplete:AutoCompleteSearchAsyncResult",
|
||||
{results: resultsArray}
|
||||
);
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
|
|
|
@ -438,7 +438,7 @@ FormAutoCompleteResult.prototype = {
|
|||
searchString : null,
|
||||
errorDescription : "",
|
||||
get defaultIndex() {
|
||||
if (this.entries.length == 0)
|
||||
if (entries.length == 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
|
|
|
@ -599,6 +599,7 @@ nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAStrin
|
|||
nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIAutoCompleteResult> result;
|
||||
|
||||
// If the login manager has indicated it's responsible for this field, let it
|
||||
// handle the autocomplete. Otherwise, handle with form history.
|
||||
|
@ -606,12 +607,14 @@ nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAStrin
|
|||
if (mPwmgrInputs.Get(mFocusedInputNode, &dummy)) {
|
||||
// XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
|
||||
// satchel manage the field?
|
||||
mLastListener = aListener;
|
||||
rv = mLoginManager->AutoCompleteSearchAsync(aSearchString,
|
||||
aPreviousResult,
|
||||
mFocusedInput,
|
||||
this);
|
||||
rv = mLoginManager->AutoCompleteSearch(aSearchString,
|
||||
aPreviousResult,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aListener) {
|
||||
aListener->OnSearchResult(this, result);
|
||||
}
|
||||
} else {
|
||||
mLastListener = aListener;
|
||||
|
||||
|
@ -650,42 +653,32 @@ nsFormFillController::PerformInputListAutoComplete(nsIAutoCompleteResult* aPrevi
|
|||
nsresult rv;
|
||||
nsCOMPtr<nsIAutoCompleteResult> result;
|
||||
|
||||
bool dummy;
|
||||
if (!mPwmgrInputs.Get(mFocusedInputNode, &dummy)) {
|
||||
nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
|
||||
do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inputListAutoComplete->AutoCompleteSearch(aPreviousResult,
|
||||
mLastSearchString,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
|
||||
do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inputListAutoComplete->AutoCompleteSearch(aPreviousResult,
|
||||
mLastSearchString,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mFocusedInput) {
|
||||
nsCOMPtr<nsIDOMHTMLElement> list;
|
||||
mFocusedInput->GetList(getter_AddRefs(list));
|
||||
if (mFocusedInput) {
|
||||
nsCOMPtr<nsIDOMHTMLElement> list;
|
||||
mFocusedInput->GetList(getter_AddRefs(list));
|
||||
|
||||
// Add a mutation observer to check for changes to the items in the <datalist>
|
||||
// and update the suggestions accordingly.
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(list);
|
||||
if (mListNode != node) {
|
||||
if (mListNode) {
|
||||
mListNode->RemoveMutationObserver(this);
|
||||
mListNode = nullptr;
|
||||
}
|
||||
if (node) {
|
||||
node->AddMutationObserverUnlessExists(this);
|
||||
mListNode = node;
|
||||
}
|
||||
// Add a mutation observer to check for changes to the items in the <datalist>
|
||||
// and update the suggestions accordingly.
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(list);
|
||||
if (mListNode != node) {
|
||||
if (mListNode) {
|
||||
mListNode->RemoveMutationObserver(this);
|
||||
mListNode = nullptr;
|
||||
}
|
||||
if (node) {
|
||||
node->AddMutationObserverUnlessExists(this);
|
||||
mListNode = node;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = aPreviousResult;
|
||||
|
||||
// If this is a password manager input mLastSearchResult will be a JS
|
||||
// object (wrapped in an XPConnect reflector), so we need to take care not
|
||||
// to hold onto it for too long.
|
||||
mLastSearchResult = nullptr;
|
||||
}
|
||||
|
||||
if (mLastListener) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче