got citi.com working
This commit is contained in:
Родитель
ed46aaa232
Коммит
90e96ba9d4
3
TODO
3
TODO
|
@ -1,5 +1,8 @@
|
||||||
|
get multistage working
|
||||||
HTTP auth capture/fill
|
HTTP auth capture/fill
|
||||||
capturing and filling lone password fields
|
capturing and filling lone password fields
|
||||||
tdameritrade may need some type like a human for lone password field on transfer page
|
tdameritrade may need some type like a human for lone password field on transfer page
|
||||||
AJAX login: https://www.select-a-spot.com
|
AJAX login: https://www.select-a-spot.com
|
||||||
bayareacurling has annoying change password page
|
bayareacurling has annoying change password page
|
||||||
|
|
||||||
|
start capturing login url and title
|
||||||
|
|
|
@ -169,7 +169,7 @@ var CommandHandler = function(Messaging, CapturedCredentialStorage) {
|
||||||
//
|
//
|
||||||
Messaging.addContentMessageListener(function(request, sender, sendResponse) {
|
Messaging.addContentMessageListener(function(request, sender, sendResponse) {
|
||||||
if (request.type && commandHandlers[request.type]) {
|
if (request.type && commandHandlers[request.type]) {
|
||||||
console.log("Msg received", request, sender);
|
//console.log("Msg received", request, sender);
|
||||||
return commandHandlers[request.type].call(commandHandlers,request.message,sender,sendResponse);
|
return commandHandlers[request.type].call(commandHandlers,request.message,sender,sendResponse);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,3 +24,5 @@
|
||||||
# fields. The real fields appear after clicking on the specified element.
|
# fields. The real fields appear after clicking on the specified element.
|
||||||
hulu.com:
|
hulu.com:
|
||||||
clickOn: "input.inactive.dummy.user"
|
clickOn: "input.inactive.dummy.user"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var PasswordForm = function($, DomMonitor) {
|
var PasswordForm = function($, DomMonitor) {
|
||||||
|
const VALID_USERNAME_INPUT_TYPES = ['text','email','url','tel','number'];
|
||||||
|
|
||||||
function notifyObserver(fn) {
|
function notifyObserver(fn) {
|
||||||
var args;
|
var args;
|
||||||
|
@ -28,17 +29,122 @@ var PasswordForm = function($, DomMonitor) {
|
||||||
notifyObserver.call(this, "credentialsCaptured");
|
notifyObserver.call(this, "credentialsCaptured");
|
||||||
}
|
}
|
||||||
|
|
||||||
var PasswordForm = function(id, usernameField, passwordField, containingEl, siteConfig) {
|
function typeValueInElementHelper($el, value, currentIndex, callback) {
|
||||||
|
if (currentIndex >= value.length) {
|
||||||
|
if (callback) callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var partialValue = value.substring(0, currentIndex+1);
|
||||||
|
setTimeout(function() {
|
||||||
|
$el.keydown();
|
||||||
|
$el.val(value);
|
||||||
|
setTimeout(function() {
|
||||||
|
$el.keyup();
|
||||||
|
setTimeout(function() {
|
||||||
|
typeValueInElementHelper($el, value, currentIndex+1, callback);
|
||||||
|
},0);
|
||||||
|
},0);
|
||||||
|
},0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillField(el, value, callback) {
|
||||||
|
var $el = $(el);
|
||||||
|
$el.focus();
|
||||||
|
var blur = function() {
|
||||||
|
$el.blur();
|
||||||
|
setTimeout(callback, 0);
|
||||||
|
}
|
||||||
|
value = value || "";
|
||||||
|
// if typeValueInElementHelper is too slow, then use this code below.
|
||||||
|
// $el.val(value);
|
||||||
|
// blur();
|
||||||
|
setTimeout(function() {
|
||||||
|
// After focusing on the given element, the page's javascript may
|
||||||
|
// change the focus (for example, swap out a fake field for a real one)
|
||||||
|
// and we should then type the value into the newly focused field
|
||||||
|
var activeElement = document.activeElement;
|
||||||
|
if (inputIsPossibleUsernameField(activeElement) || inputIsPasswordField(activeElement)) {
|
||||||
|
$el = $(activeElement);
|
||||||
|
}
|
||||||
|
typeValueInElementHelper($el, value, 0, blur);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputIsPossibleUsernameField(input) {
|
||||||
|
return input.tagName.toLowerCase() === "input" &&
|
||||||
|
VALID_USERNAME_INPUT_TYPES.indexOf(input.type.toLowerCase()) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputIsPasswordField(input) {
|
||||||
|
return input.tagName.toLowerCase() === "input" && input.type.toLowerCase() === "password";
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeGetConfiguredUsernameField() {
|
||||||
|
var $username = $(this.config.un);
|
||||||
|
if ($username.length > 0 && this.$containingForm.has($username)) {
|
||||||
|
return $username.get(0);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findBestUsernameFieldCandidate() {
|
||||||
|
var inputsList = this.$containingEl.find('input').get();
|
||||||
|
var pwFieldIdx = inputsList.indexOf(this.passwordField.el);
|
||||||
|
for (var inputIdx = pwFieldIdx-1; inputIdx >= 0; inputIdx--) {
|
||||||
|
if (inputIsPossibleUsernameField(inputsList[inputIdx])) {
|
||||||
|
return inputsList[inputIdx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Couldn't find a valid username input field.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findUsernameField() {
|
||||||
|
// If the username field is explicitly configured, then get it
|
||||||
|
var usernameEl = maybeGetConfiguredUsernameField.call(this);
|
||||||
|
// If we didn't find a configured username field for the form, then
|
||||||
|
// look for one
|
||||||
|
if (!usernameEl) {
|
||||||
|
usernameEl = findBestUsernameFieldCandidate.call(this);
|
||||||
|
}
|
||||||
|
return { el: usernameEl, $el: $(usernameEl) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePossibleUsernameFieldChange() {
|
||||||
|
var activeElement = document.activeElement;
|
||||||
|
// If the user focuses on a username element and the active element changes, then
|
||||||
|
// we should update what our notion of the username field is.
|
||||||
|
// See https://online.citibank.com for where this is needed.
|
||||||
|
if (activeElement !== this.usernameField.el &&
|
||||||
|
inputIsPossibleUsernameField(activeElement)) {
|
||||||
|
this.usernameField.$el.off(this.focusEvents);
|
||||||
|
this.usernameField = { el: activeElement, $el: $(activeElement) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var PasswordForm = function(id, passwordEl, $containingEl, siteConfig) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.usernameField = usernameField;
|
this.passwordField = { el: passwordEl };
|
||||||
this.passwordField = passwordField;
|
this.$containingEl = $containingEl;
|
||||||
this.containingEl = containingEl;
|
|
||||||
this.$containingEl = $(this.containingEl);
|
|
||||||
this.config = siteConfig || {};
|
this.config = siteConfig || {};
|
||||||
|
|
||||||
// "input" event will capture paste input and key by key input on modern browsers
|
// "input" event will capture paste input and key by key input on modern browsers
|
||||||
// Note: this will not trigger when values are filled by javsacript or the browser
|
// Note: this will not trigger when values are filled by javsacript or the browser
|
||||||
this.inputEvents = "input."+this.id;
|
this.inputEvents = "input."+this.id;
|
||||||
this.submitEvents = "submit."+this.id;
|
this.submitEvents = "submit."+this.id;
|
||||||
|
this.focusEvents = "focus.username"+this.id;
|
||||||
|
|
||||||
|
// Setting 'autocomplete' to 'off' will signal to the native
|
||||||
|
// password manager to ignore this login wrt filling and capturing.
|
||||||
|
// This solves the "double infobar" problem when linking.
|
||||||
|
this.passwordField.el.setAttribute('autocomplete', 'off');
|
||||||
|
|
||||||
|
// This must be called after passwordField and $containingEl are set
|
||||||
|
this.usernameField = findUsernameField.call(this);
|
||||||
|
this.usernameField.$el.on(this.focusEvents, handlePossibleUsernameFieldChange.bind(this));
|
||||||
|
|
||||||
// This is an external observer interested in events on the PasswordForm,
|
// This is an external observer interested in events on the PasswordForm,
|
||||||
// most likely the PasswordFormInspector.
|
// most likely the PasswordFormInspector.
|
||||||
this.observer = null;
|
this.observer = null;
|
||||||
|
@ -58,6 +164,7 @@ var PasswordForm = function($, DomMonitor) {
|
||||||
PasswordForm.prototype.unobserve = function() {
|
PasswordForm.prototype.unobserve = function() {
|
||||||
this.$containingEl.off(this.inputEvents);
|
this.$containingEl.off(this.inputEvents);
|
||||||
this.$containingEl.off(this.submitEvents);
|
this.$containingEl.off(this.submitEvents);
|
||||||
|
this.usernameField.$el.off(this.focusEvents);
|
||||||
this.observer = null;
|
this.observer = null;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -67,8 +174,9 @@ var PasswordForm = function($, DomMonitor) {
|
||||||
if (clickOn) {
|
if (clickOn) {
|
||||||
$(clickOn).click();
|
$(clickOn).click();
|
||||||
}
|
}
|
||||||
this.usernameField.el.value = credentials.username;
|
fillField(this.usernameField.el, credentials.username, (function() {
|
||||||
this.passwordField.el.value = credentials.password;
|
fillField(this.passwordField.el, credentials.password);
|
||||||
|
}).bind(this));
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,6 +200,7 @@ var PasswordForm = function($, DomMonitor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PasswordForm.prototype.highlight = function() {
|
PasswordForm.prototype.highlight = function() {
|
||||||
|
var containingEl = this.$containingEl.get(0);
|
||||||
if (this.usernameField.el) {
|
if (this.usernameField.el) {
|
||||||
highlightEl(this.usernameField.el, "blue");
|
highlightEl(this.usernameField.el, "blue");
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,8 +211,8 @@ var PasswordForm = function($, DomMonitor) {
|
||||||
} else {
|
} else {
|
||||||
console.log("No password field found for", this);
|
console.log("No password field found for", this);
|
||||||
}
|
}
|
||||||
if (this.containingEl) {
|
if (containingEl) {
|
||||||
highlightEl(this.containingEl, "red");
|
highlightEl(containingEl, "red");
|
||||||
} else {
|
} else {
|
||||||
console.log("No containing form field found for", this);
|
console.log("No containing form field found for", this);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
var PasswordFormInspector = function($, PasswordForm, DomMonitor) {
|
var PasswordFormInspector = function($, PasswordForm, DomMonitor) {
|
||||||
const VALID_USERNAME_INPUT_TYPES = ['text','email','url','tel','number'];
|
|
||||||
|
|
||||||
var running = false;
|
var running = false;
|
||||||
|
|
||||||
var observers = [];
|
var observers = [];
|
||||||
|
@ -16,18 +14,6 @@ var PasswordFormInspector = function($, PasswordForm, DomMonitor) {
|
||||||
return idCounter;
|
return idCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUsernameFieldForPasswordField(containerForm,passwordEl) {
|
|
||||||
var inputsList = containerForm.find('input').get();
|
|
||||||
var pwFieldIdx = inputsList.indexOf(passwordEl);
|
|
||||||
for (var inputIdx = pwFieldIdx-1; inputIdx >= 0; inputIdx--) {
|
|
||||||
if (VALID_USERNAME_INPUT_TYPES.indexOf(inputsList[inputIdx].type.toLowerCase()) != -1) {
|
|
||||||
return inputsList[inputIdx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Couldn't find a valid username input field.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findForms() {
|
function findForms() {
|
||||||
var $passwordInputs = $('input[type=password]');
|
var $passwordInputs = $('input[type=password]');
|
||||||
var oldPasswordForms = passwordForms; // TODO: clean these up
|
var oldPasswordForms = passwordForms; // TODO: clean these up
|
||||||
|
@ -39,7 +25,7 @@ var PasswordFormInspector = function($, PasswordForm, DomMonitor) {
|
||||||
usernameFields = {},
|
usernameFields = {},
|
||||||
numPasswordInputs;
|
numPasswordInputs;
|
||||||
if ($containingForm.length === 0) {
|
if ($containingForm.length === 0) {
|
||||||
console.log("Could not find form element, passwordEl=", $passwordEl);
|
console.log("Could not find form element, passwordEl=", passwordEl);
|
||||||
// Could not find an HTML form, so now just look for any element that
|
// Could not find an HTML form, so now just look for any element that
|
||||||
// contains both the password field and some other input field with type=text.
|
// contains both the password field and some other input field with type=text.
|
||||||
// Note: this will also find inputs with no type specified, which defaults to text.
|
// Note: this will also find inputs with no type specified, which defaults to text.
|
||||||
|
@ -49,19 +35,13 @@ var PasswordFormInspector = function($, PasswordForm, DomMonitor) {
|
||||||
if ($containingForm.length === 0) {
|
if ($containingForm.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Setting 'autocomplete' to 'off' will signal to the native
|
|
||||||
// password manager to ignore this login wrt filling and capturing.
|
|
||||||
// This solves the "double infobar" problem when linking.
|
|
||||||
passwordEl.setAttribute('autocomplete', 'off');
|
|
||||||
numPasswordInputs = $containingForm.find('input[type=password]').length;
|
numPasswordInputs = $containingForm.find('input[type=password]').length;
|
||||||
// If the containing form contains multiple password field, then ignore
|
// If the containing form contains multiple password field, then ignore
|
||||||
// for now. This is probably a change password field.
|
// for now. This is probably a change password field.
|
||||||
if (numPasswordInputs > 1) return;
|
if (numPasswordInputs > 1) return;
|
||||||
usernameEl = getUsernameFieldForPasswordField($containingForm,passwordEl);
|
|
||||||
passwordForms.push(new PasswordForm(generateId(),
|
passwordForms.push(new PasswordForm(generateId(),
|
||||||
{ el: usernameEl },
|
passwordEl,
|
||||||
{ el: passwordEl },
|
$containingForm,
|
||||||
$containingForm.get(0),
|
|
||||||
siteConfig));
|
siteConfig));
|
||||||
});
|
});
|
||||||
observeForms();
|
observeForms();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче