first take on storage refactor
This commit is contained in:
Родитель
3ccf3962d1
Коммит
bfa352d71b
|
@ -37,18 +37,14 @@ function formFillCurrentTab() {
|
|||
function getPageDataForPopup(callback) {
|
||||
getActiveTab(function(tab) {
|
||||
var newURL = new Uri(tab.url);
|
||||
getLoginsForSite(newURL.host(),function(logins) {
|
||||
callback(logins);
|
||||
});
|
||||
callback(User.Logins.getForHostname(newURL.host()));
|
||||
});
|
||||
}
|
||||
|
||||
function formFillTab(tab) {
|
||||
var newURL = new Uri(tab.url);
|
||||
getLoginsForSite(newURL.host(),function(logins) {
|
||||
chrome.tabs.sendMessage(tab.id,{
|
||||
type: 'fill_form',
|
||||
login: logins[0]
|
||||
});
|
||||
chrome.tabs.sendMessage(tab.id,{
|
||||
type: 'fill_form',
|
||||
login: User.Logins.getForHostname(newURL.host())[0]
|
||||
});
|
||||
}
|
|
@ -18,17 +18,10 @@ function startFirstRunFlow() {
|
|||
});
|
||||
// Updates the browserAction popup, stores that we haven't
|
||||
// yet completed first run in localStorage.
|
||||
setIfDidFirstRun(false);
|
||||
User.firstRun.setIfCompleted(false);
|
||||
}
|
||||
|
||||
function closeFirstRunTab() {
|
||||
chrome.tabs.remove(firstRunTab);
|
||||
firstRunTab = -1;
|
||||
}
|
||||
|
||||
function firstRunFinished() {
|
||||
// Save the fact that the first run flow has been completed,
|
||||
// so that the splash screen doesn't reopen the next time the
|
||||
// add-on starts, and the regular interface appears in the browserAction.
|
||||
setIfDidFirstRun(true);
|
||||
}
|
||||
}
|
|
@ -10,16 +10,16 @@ var infobarHooks = {
|
|||
'password_observed': function (notificationObj,infobarResponse) {
|
||||
switch (infobarResponse.user_action) {
|
||||
case 'save':
|
||||
saveLoginToStorage(notificationObj.notification);
|
||||
User.Logins.add(notificationObj.notification);
|
||||
break;
|
||||
|
||||
case 'pin_lock':
|
||||
notificationObj.notification.pinLocked = true;
|
||||
saveLoginToStorage(notificationObj.notification);
|
||||
User.Logins.add(notificationObj.notification);
|
||||
break;
|
||||
|
||||
case 'never_for_this_site':
|
||||
neverSaveOnSite(notificationObj.notification.hostname);
|
||||
User.neverAsk.add(notificationObj.notification.hostname);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -41,6 +41,6 @@ var infobarHooks = {
|
|||
}
|
||||
},
|
||||
'update_password': function(notificationObj,infobarResponse) {
|
||||
saveLoginToStorage(notificationObj.notification);
|
||||
User.Logins.add(notificationObj.notification);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,9 @@
|
|||
initSkyCrane();
|
||||
|
||||
function initSkyCrane() {
|
||||
// Load blacklisted sites from localStorage
|
||||
loadNeverSaveOnSites();
|
||||
// Load PIN lock state from localStorage
|
||||
loadLoginsLock();
|
||||
checkIfDidFirstRun(function(didFirstRun) {
|
||||
console.log('didFirstRun: ', didFirstRun);
|
||||
if (!didFirstRun) {
|
||||
startFirstRunFlow();
|
||||
}
|
||||
});
|
||||
if (!User.firstRun.wasCompleted()) {
|
||||
startFirstRunFlow();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -26,119 +19,121 @@ function initSkyCrane() {
|
|||
//
|
||||
|
||||
var messageHandlers = {
|
||||
'add_login': function(message,tabID) {
|
||||
var notificationObj = message;
|
||||
// Check to see if the user disabled password saving on this site
|
||||
if (neverSaveOnSites.indexOf(notificationObj.hostname) != -1) return;
|
||||
notificationObj.type = 'password_observed';
|
||||
notificationObj.hash = SHA1(notificationObj.password);
|
||||
// Look for passwords in use on the current site
|
||||
getLoginsForSite(notificationObj.hostname,function(logins) {
|
||||
if (logins === undefined) logins = [];
|
||||
var loginsForSameUsername = logins.filter(
|
||||
function(l) { return l.username == notificationObj.username; }
|
||||
);
|
||||
if (loginsForSameUsername.length == 1) {
|
||||
// User already has a login saved for this site.
|
||||
if (loginsForSameUsername[0].password == notificationObj.password) {
|
||||
// We're just logging into a site with an existing login. Bail.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Prompt user to update password
|
||||
notificationObj.type = 'update_password';
|
||||
// If the existing login stored for this site was PIN locked,
|
||||
// make sure this new one will be also.
|
||||
notificationObj.pinLocked = logins[0].pinLocked;
|
||||
}
|
||||
}
|
||||
// Has the user signed up for a Gombot account?
|
||||
checkIfDidFirstRun(function(didFirstRun) {
|
||||
if (didFirstRun) {
|
||||
// Prompt the user to save the login
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: notificationObj
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Browser not associated with Gombot account, offer
|
||||
// to create one/log in.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'signup_nag'
|
||||
}
|
||||
});
|
||||
}
|
||||
'add_login': function(message,tabID) {
|
||||
var notificationObj = message;
|
||||
// Check to see if the user disabled password saving on this site
|
||||
User.NeverAsk.checkForHostname(notificationObj.hostname, function(shouldAsk) {
|
||||
if (!shouldAsk) return;
|
||||
notificationObj.type = 'password_observed';
|
||||
notificationObj.hash = SHA1(notificationObj.password);
|
||||
// Look for passwords in use on the current site
|
||||
getLoginsForSite(notificationObj.hostname,function(logins) {
|
||||
if (logins === undefined) logins = [];
|
||||
var loginsForSameUsername = logins.filter(
|
||||
function(l) { return l.username == notificationObj.username; }
|
||||
);
|
||||
if (loginsForSameUsername.length == 1) {
|
||||
// User already has a login saved for this site.
|
||||
if (loginsForSameUsername[0].password == notificationObj.password) {
|
||||
// We're just logging into a site with an existing login. Bail.
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Prompt user to update password
|
||||
notificationObj.type = 'update_password';
|
||||
// If the existing login stored for this site was PIN locked,
|
||||
// make sure this new one will be also.
|
||||
notificationObj.pinLocked = logins[0].pinLocked;
|
||||
}
|
||||
}
|
||||
// Has the user signed up for a Gombot account?
|
||||
checkIfDidFirstRun(function(didFirstRun) {
|
||||
if (didFirstRun) {
|
||||
// Prompt the user to save the login
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: notificationObj
|
||||
});
|
||||
// TODO: Send new login to server
|
||||
}
|
||||
else {
|
||||
// Browser not associated with Gombot account, offer
|
||||
// to create one/log in.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'signup_nag'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
'observing_page': function(message,tabID) {
|
||||
// See if there are login forms on this page. If not, there's nothing to do
|
||||
// on the observing_page notification, so bail.
|
||||
if (message.num_login_forms == 0) return;
|
||||
// TODO: Send new login to server
|
||||
});
|
||||
})
|
||||
},
|
||||
'observing_page': function(message,tabID) {
|
||||
// See if there are login forms on this page. If not, there's nothing to do
|
||||
// on the observing_page notification, so bail.
|
||||
if (message.num_login_forms == 0) return;
|
||||
|
||||
// Search for logins for this particular site
|
||||
getLoginsForSite(message.hostname, function(logins) {
|
||||
if (logins.length == 0) return;
|
||||
|
||||
if (logins.length == 1) {
|
||||
// Is the login for this site PIN locked?
|
||||
if (logins[0].pinLocked) {
|
||||
// If it is, show the PIN entry infobar.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
// Include the tabID in the notification so the infobar handler
|
||||
// can trigger autofill in the correct tab.
|
||||
tabID: tabID
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// If it's not, form fill the page now.
|
||||
chrome.tabs.sendMessage(tabID,{
|
||||
type: 'fill_form',
|
||||
login: logins[0]
|
||||
});
|
||||
}
|
||||
|
||||
// Search for logins for this particular site
|
||||
getLoginsForSite(message.hostname, function(logins) {
|
||||
if (logins.length == 0) return;
|
||||
// If there's only a single login form on the page, we're fine. Otherwise,
|
||||
// see if we were able to record an id or a name for the form when we observed
|
||||
// the login.
|
||||
// TODO: Check to see if the id/name is still valid
|
||||
|
||||
if (logins.length == 1) {
|
||||
// Is the login for this site PIN locked?
|
||||
if (logins[0].pinLocked) {
|
||||
// If it is, show the PIN entry infobar.
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tabID,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
// Include the tabID in the notification so the infobar handler
|
||||
// can trigger autofill in the correct tab.
|
||||
tabID: tabID
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// If it's not, form fill the page now.
|
||||
chrome.tabs.sendMessage(tabID,{
|
||||
type: 'fill_form',
|
||||
login: logins[0]
|
||||
});
|
||||
}
|
||||
|
||||
// If there's only a single login form on the page, we're fine. Otherwise,
|
||||
// see if we were able to record an id or a name for the form when we observed
|
||||
// the login.
|
||||
// TODO: Check to see if the id/name is still valid
|
||||
|
||||
// If we remember specifics about a login form, check to see if it's there.
|
||||
// If it is, offer to autologin.
|
||||
if (logins[0].formEl && (logins[0].formEl.name || logins[0].formEl.id)) {
|
||||
chrome.tabs.sendMessage(tabID,{
|
||||
type: 'confirm_form_exists',
|
||||
login: logins[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: Prompt user for choice of logins
|
||||
}
|
||||
});
|
||||
},
|
||||
'validate_pin': function(message,tabID,sendResponse) {
|
||||
sendResponse({
|
||||
'is_valid': validatePIN(message.pin)
|
||||
});
|
||||
}
|
||||
// If we remember specifics about a login form, check to see if it's there.
|
||||
// If it is, offer to autologin.
|
||||
if (logins[0].formEl && (logins[0].formEl.name || logins[0].formEl.id)) {
|
||||
chrome.tabs.sendMessage(tabID,{
|
||||
type: 'confirm_form_exists',
|
||||
login: logins[0]
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: Prompt user for choice of logins
|
||||
}
|
||||
});
|
||||
},
|
||||
'validate_pin': function(message,tabID,sendResponse) {
|
||||
sendResponse({
|
||||
'is_valid': validatePIN(message.pin)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
||||
if (request.type && messageHandlers[request.type]) {
|
||||
messageHandlers[request.type].call(messageHandlers,request.message,sender.tab.id,sendResponse);
|
||||
}
|
||||
if (request.type && messageHandlers[request.type]) {
|
||||
messageHandlers[request.type].call(messageHandlers,request.message,sender.tab.id,sendResponse);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
|
@ -146,58 +141,58 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
//
|
||||
|
||||
function displayInfobar(notificationObj) {
|
||||
var infobarPaths = {
|
||||
password_observed: "/infobars/remember_password_infobar.html",
|
||||
update_password: "/infobars/update_password_infobar.html",
|
||||
signup_nag: "/infobars/signup_nag_infobar.html",
|
||||
pin_entry: "/infobars/pin_entry_infobar.html"
|
||||
var infobarPaths = {
|
||||
password_observed: "/infobars/remember_password_infobar.html",
|
||||
update_password: "/infobars/update_password_infobar.html",
|
||||
signup_nag: "/infobars/signup_nag_infobar.html",
|
||||
pin_entry: "/infobars/pin_entry_infobar.html"
|
||||
};
|
||||
// Make sure we have a HTML infobar for this type of notification
|
||||
if (!infobarPaths[notificationObj.notification.type]) return;
|
||||
InfobarManager.run({
|
||||
path: infobarPaths[notificationObj.notification.type],
|
||||
tabId: notificationObj.tabID,
|
||||
height: '32px'
|
||||
}, genHandlerForNotification(notificationObj));
|
||||
|
||||
function genHandlerForNotification(notificationObj) {
|
||||
return function(err,response) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (!response.type) return;
|
||||
if (!infobarHooks[response.type]) {
|
||||
console.log('Infobar returned unknown response type!');
|
||||
return;
|
||||
}
|
||||
infobarHooks[response.type].call(infobarHooks,notificationObj,response);
|
||||
};
|
||||
// Make sure we have a HTML infobar for this type of notification
|
||||
if (!infobarPaths[notificationObj.notification.type]) return;
|
||||
InfobarManager.run({
|
||||
path: infobarPaths[notificationObj.notification.type],
|
||||
tabId: notificationObj.tabID,
|
||||
height: '32px'
|
||||
}, genHandlerForNotification(notificationObj));
|
||||
|
||||
function genHandlerForNotification(notificationObj) {
|
||||
return function(err,response) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
if (!response.type) return;
|
||||
if (!infobarHooks[response.type]) {
|
||||
console.log('Infobar returned unknown response type!');
|
||||
return;
|
||||
}
|
||||
infobarHooks[response.type].call(infobarHooks,notificationObj,response);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test function that spawns an example infobar on the current active tab.
|
||||
function testInfobarNotification() {
|
||||
getActiveTab(function(tab) {
|
||||
console.log("tab url: ", tab.url,' ', tab);
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tab.id,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
formEl: {},
|
||||
formSubmitURL: "",
|
||||
hash: "bc74f4f071a5a33f00ab88a6d6385b5e6638b86c",
|
||||
hostname: "t.nm.io",
|
||||
httpRealm: null,
|
||||
password: "green",
|
||||
passwordField: {},
|
||||
type: "password_observed",
|
||||
username: "gombottest",
|
||||
usernameField: {}
|
||||
}
|
||||
});
|
||||
getActiveTab(function(tab) {
|
||||
console.log("tab url: ", tab.url,' ', tab);
|
||||
displayInfobar({
|
||||
notify: true,
|
||||
tabID: tab.id,
|
||||
notification: {
|
||||
type: 'pin_entry',
|
||||
formEl: {},
|
||||
formSubmitURL: "",
|
||||
hash: "bc74f4f071a5a33f00ab88a6d6385b5e6638b86c",
|
||||
hostname: "t.nm.io",
|
||||
httpRealm: null,
|
||||
password: "green",
|
||||
passwordField: {},
|
||||
type: "password_observed",
|
||||
username: "gombottest",
|
||||
usernameField: {}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -205,7 +200,6 @@ function testInfobarNotification() {
|
|||
//
|
||||
|
||||
function validatePIN(_pin) {
|
||||
// If there's no PIN set, accept.
|
||||
if (!loginsLock || !loginsLock.pin) return true;
|
||||
return _pin == loginsLock.pin;
|
||||
// If there's no PIN set, accept. Otherwise, validate.
|
||||
return (!Boolean(User.PIN.get())) || User.PIN.validate(_pin);
|
||||
}
|
||||
|
|
|
@ -6,78 +6,93 @@
|
|||
*
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// Getting and saving login data to/from localStorage
|
||||
//
|
||||
|
||||
// List of hostnames the user has asked passwords not to be saved on.
|
||||
// Kept in sync with local storage.
|
||||
var neverSaveOnSites = [];
|
||||
// This handles the low-level localStorage
|
||||
var LLStorage = (function(){
|
||||
return {
|
||||
get: function(key, callback) {
|
||||
chrome.storage.local.get(key, function(storageObj) {
|
||||
callback(storageObj[key]);
|
||||
});
|
||||
},
|
||||
save: function(key, data) {
|
||||
var updatedObj = {};
|
||||
updatedObj[key] = data;
|
||||
chrome.storage.local.set(updatedObj);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
// The key for the document in localStorage which holds all of the user
|
||||
// data synced with the server.
|
||||
const LOCAL_STORAGE_KEY = 'gombot_user_data';
|
||||
|
||||
// Other type: 'pin' when PIN locking is enabled.
|
||||
var loginsLock = {
|
||||
type: 'none'
|
||||
};
|
||||
|
||||
// Takes a callback, and passes it a boolean indicating whether
|
||||
// or not the user has completed the first run flow.
|
||||
// Note that this will return true if the user started the first
|
||||
// run flow but did not complete it.
|
||||
function checkIfDidFirstRun(callback) {
|
||||
chrome.storage.local.get('did_first_run', function(storageObj) {
|
||||
callback(Boolean(storageObj.did_first_run));
|
||||
});
|
||||
}
|
||||
|
||||
// Set (or unset) the flag indicating the user has finished the first
|
||||
// run flow.
|
||||
function setIfDidFirstRun(firstRunFinished) {
|
||||
chrome.storage.local.set({
|
||||
'did_first_run': Boolean(firstRunFinished)
|
||||
var User = (function(Storage){
|
||||
|
||||
// In-memory storage for user data that gets synced.
|
||||
var _logins = {};
|
||||
var _neverAsk = {};
|
||||
var _pin = null;
|
||||
|
||||
// In-memory storasge for user data that's persisted to localStorage, but not synced.
|
||||
var _didFirstRun = false;
|
||||
|
||||
// The key for the document in localStorage which holds all of the user
|
||||
// data synced with the server.
|
||||
const SYNC_DOCUMENT_KEY = 'gombot_user_data';
|
||||
|
||||
const DATA_VERSION = 'identity.mozilla.com/gombot/v1/userData';
|
||||
|
||||
// NB: remove every call to fetchStoredData and updateStoredData before commit!
|
||||
|
||||
function fetchStoredData(callback) {
|
||||
var keysToFetch = [
|
||||
SYNC_DOCUMENT_KEY,
|
||||
'did_first_run'
|
||||
];
|
||||
chrome.storage.local.get(SYNC_DOCUMENT_KEY, function(storageObj) {
|
||||
var userData = storageObj;//[SYNC_DOCUMENT_KEY];
|
||||
if (userData === undefined) {
|
||||
userData = {
|
||||
version: DATA_VERSION,
|
||||
logins: {},
|
||||
pin: loginsLock.pin || null,
|
||||
neverAsk: {}
|
||||
};
|
||||
}
|
||||
callback(userData);
|
||||
});
|
||||
}
|
||||
|
||||
// Updates the user's PIN in loginsLock and also updates localStorage.
|
||||
function setAndSavePIN(pin) {
|
||||
loginsLock = {
|
||||
'type': 'pin',
|
||||
'pin': pin
|
||||
};
|
||||
saveLoginsLock();
|
||||
}
|
||||
|
||||
function loadLoginsLock() {
|
||||
// Load PIN lock state from localStorage
|
||||
}
|
||||
|
||||
// function updateStoredData(obj) {
|
||||
// var updatedObj = {};
|
||||
// updatedObj[SYNC_DOCUMENT_KEY] = obj;
|
||||
// chrome.storage.local.set(updatedObj);
|
||||
// }
|
||||
|
||||
function saveToLocalStorage() {
|
||||
Storage.save(SYNC_DOCUMENT_KEY, {
|
||||
version: DATA_VERSION,
|
||||
logins: _logins || {},
|
||||
pin: _pin || null,
|
||||
neverAsk: _neverAsk || {}
|
||||
});
|
||||
Storage.save('did_first_run',_didFirstRun);
|
||||
}
|
||||
|
||||
function loadFromLocalStorage() {
|
||||
fetchStoredData(function(userData) {
|
||||
if (userData['pin']) {
|
||||
loginsLock = {
|
||||
type: 'pin',
|
||||
pin: userData.pin
|
||||
};
|
||||
}
|
||||
else {
|
||||
loginsLock = {
|
||||
type: 'none'
|
||||
};
|
||||
}
|
||||
_logins = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
_pin = userData[SYNC_DOCUMENT_KEY].logins;
|
||||
_neverAsk = userData[SYNC_DOCUMENT_KEY].neverAsk;
|
||||
_didFirstRun = userData['did_first_run'];
|
||||
});
|
||||
}
|
||||
|
||||
function saveLoginsLock() {
|
||||
// Persist loginsLock to localStorage, to preserve PIN across sessions
|
||||
fetchStoredData(function(userData) {
|
||||
userData['pin'] = loginsLock.pin || null;
|
||||
updateStoredData(userData);
|
||||
});
|
||||
}
|
||||
|
||||
function formatStoredLogin(login) {
|
||||
return {
|
||||
}
|
||||
|
||||
// Load from localStorage into memory when extension starts.
|
||||
loadFromLocalStorage();
|
||||
|
||||
var userData = {};
|
||||
|
||||
var loginsObj = (function() {
|
||||
function formatStoredLogin(login) {
|
||||
return {
|
||||
username: login.username,
|
||||
password: login.password,
|
||||
hostname: login.hostname,
|
||||
|
@ -87,120 +102,140 @@ function formatStoredLogin(login) {
|
|||
url: login['url'] || '',
|
||||
pinLocked: login['pinLocked'] || false,
|
||||
supplementalInformation: login['supplementalInformation'] || {}
|
||||
};
|
||||
}
|
||||
|
||||
function fetchStoredData(callback) {
|
||||
chrome.storage.local.get(LOCAL_STORAGE_KEY, function(storageObj) {
|
||||
var userData = storageObj[LOCAL_STORAGE_KEY];
|
||||
if (userData === undefined) {
|
||||
userData = {
|
||||
version: "identity.mozilla.com/gombot/v1/userData",
|
||||
logins: {},
|
||||
pin: loginsLock.pin || null,
|
||||
neverAsk: {}
|
||||
};
|
||||
}
|
||||
callback(userData);
|
||||
});
|
||||
}
|
||||
|
||||
function updateStoredData(obj) {
|
||||
var updatedObj = {};
|
||||
updatedObj[LOCAL_STORAGE_KEY] = obj;
|
||||
chrome.storage.local.set(updatedObj);
|
||||
}
|
||||
|
||||
// Save a new login object to localStorage
|
||||
function saveLoginToStorage(newLogin) {
|
||||
var loginObj = formatStoredLogin(newLogin);
|
||||
fetchStoredData(function(userData) {
|
||||
};
|
||||
}
|
||||
return {
|
||||
// Save a new login object to localStorage
|
||||
add: function(newLogin) {
|
||||
var loginObj = formatStoredLogin(newLogin);
|
||||
// Filter for logins with the same username and hostname.
|
||||
var existingLoginsForHost = userData.logins[newLogin.hostname] || [];
|
||||
userData.logins[newLogin.hostname] =
|
||||
existingLoginsForHost.filter(function(_login) {
|
||||
return _login.username != loginObj.username;
|
||||
});
|
||||
userData.logins[newLogin.hostname].push(loginObj);
|
||||
updateStoredData(userData);
|
||||
});
|
||||
}
|
||||
|
||||
function loadNeverSaveOnSites() {
|
||||
getNeverSaveOnSites(function(siteNames) { neverSaveOnSites = siteNames; });
|
||||
}
|
||||
|
||||
// Add a hostname to the list of sites for which we never
|
||||
// prompt to save passwords
|
||||
function neverSaveOnSite(siteHostname) {
|
||||
fetchStoredData(function(userData) {
|
||||
if (!(siteHostname in userData.neverAsk)) {
|
||||
userData.neverAsk[siteHostname] = 'all';
|
||||
updateStoredData(userData);
|
||||
loadNeverSaveOnSites();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Takes a callback, and passes it a list of domains the user
|
||||
// has elected to never save logins on.
|
||||
function getNeverSaveOnSites(callback) {
|
||||
fetchStoredData(function(userData) {
|
||||
callback(_.keys(userData.neverAsk));
|
||||
});
|
||||
}
|
||||
|
||||
// Takes a hostname and a callback, and passes it a list of login
|
||||
// objects the user has saved for that domain.
|
||||
function getLoginsForSite(hostname,callback) {
|
||||
fetchStoredData(function(userData) {
|
||||
callback(userData.logins[hostname] || []);
|
||||
});
|
||||
}
|
||||
var existingLoginsForHost = _logins[newLogin.hostname] || [];
|
||||
_logins[newLogin.hostname] =
|
||||
existingLoginsForHost.filter(function(_login) {
|
||||
return _login.username != loginObj.username;
|
||||
});
|
||||
_logins[newLogin.hostname].push(loginObj);
|
||||
saveToLocalStorage();
|
||||
},
|
||||
// Takes a hostname and a callback, and passes it a list of login
|
||||
// objects the user has saved for that domain.
|
||||
getForHostname: function(hostname/*,callback*/) {
|
||||
return (_logins[hostname] || []);
|
||||
},
|
||||
|
||||
// Mainly for debugging purposes.
|
||||
function deleteLoginsForSite(hostname) {
|
||||
fetchStoredData(function(userData) {
|
||||
delete userData[hostname];
|
||||
updateStoredData(userData);
|
||||
});
|
||||
}
|
||||
// Mainly for debugging purposes.
|
||||
deleteForHostname: function(hostname) {
|
||||
delete _logins[hostname];
|
||||
saveToLocalStorage();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var neverAskObj = (function() {
|
||||
return {
|
||||
// Add a hostname to the list of sites for which we never
|
||||
// prompt to save passwords
|
||||
add: function(siteHostname) {
|
||||
if (!(siteHostname in _neverAsk)) {
|
||||
_neverAsk[siteHostname] = 'all';
|
||||
saveToLocalStorage();
|
||||
}
|
||||
},
|
||||
// Takes a callback, and passes it a list of domains the user
|
||||
// has elected to never save logins on.
|
||||
get: function(/*callback*/) {
|
||||
return _.keys(userData.neverAsk);
|
||||
},
|
||||
// Takes a hostname and a callback. Passes callback a boolean,
|
||||
// indicating if the user *should* be asked about logins
|
||||
// on that domain.
|
||||
checkForHostname: function(hostname/*,callback*/) {
|
||||
return !(hostname in this.get());
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var firstRunObj = (function(){
|
||||
return {
|
||||
wasCompleted: function() {
|
||||
// Takes a callback, and passes it a boolean indicating whether
|
||||
// or not the user has completed the first run flow.
|
||||
// Note that this will return true if the user started the first
|
||||
// run flow but did not complete it.
|
||||
return _didFirstRun;
|
||||
},
|
||||
setIfCompleted: function(firstRunFinished) {
|
||||
//setIfDidFirstRun
|
||||
// Set (or unset) the flag indicating the user has finished the first
|
||||
// run flow.
|
||||
_didFirstRun = firstRunFinished;
|
||||
saveToLocalStorage();
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var pinObj = (function() {
|
||||
return {
|
||||
validate: function(testPin) {
|
||||
return testPin == _pin;
|
||||
},
|
||||
get: function() {
|
||||
return _pin;
|
||||
},
|
||||
set: function(newPIN) {
|
||||
_pin = newPIN || null;
|
||||
saveToLocalStorage();
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
Logins: loginsObj,
|
||||
neverAsk: neverAskObj,
|
||||
firstRun: firstRunObj,
|
||||
PIN: pinObj
|
||||
};
|
||||
})(LLStorage);
|
||||
|
||||
//
|
||||
// Getting and saving login data to/from localStorage
|
||||
//
|
||||
|
||||
|
||||
// Returns a string of a comma separated value file containing the hostname, username,
|
||||
// and password for each login the user has saved.
|
||||
function getLoginsCSV(callback) {
|
||||
// Add header
|
||||
var retVal = "hostname,username,password\n";
|
||||
fetchStoredData(function(userData) {
|
||||
for (var item in _.keys(userData.logins)) {
|
||||
for (var login in userData.logins[item]) {
|
||||
retVal += login.hostname + ',' + login.username
|
||||
+ ',' + login.password + '\n';
|
||||
}
|
||||
}
|
||||
callback(retVal);
|
||||
});
|
||||
// Add header
|
||||
var retVal = "hostname,username,password\n";
|
||||
fetchStoredData(function(userData) {
|
||||
for (var item in _.keys(userData.logins)) {
|
||||
for (var login in userData.logins[item]) {
|
||||
retVal += login.hostname + ',' + login.username
|
||||
+ ',' + login.password + '\n';
|
||||
}
|
||||
}
|
||||
callback(retVal);
|
||||
});
|
||||
}
|
||||
|
||||
// Dump localStorage to CSV file, for debugging purposes.
|
||||
function downloadExportDataFile() {
|
||||
// Get entire content of localStorage
|
||||
// NB: This contains all of the user's passwords in plaintext, as well as
|
||||
// their PIN and not-so-useful flags like did_first_run.
|
||||
getLoginsCSV(function(loginsCSV) {
|
||||
// Turn storageObj into a blob
|
||||
var blob = new window.Blob([loginsCSV], {type: 'text/csv'});
|
||||
// Get entire content of localStorage
|
||||
// NB: This contains all of the user's passwords in plaintext, as well as
|
||||
// their PIN and not-so-useful flags like did_first_run.
|
||||
getLoginsCSV(function(loginsCSV) {
|
||||
// Turn storageObj into a blob
|
||||
var blob = new window.Blob([loginsCSV], {type: 'text/csv'});
|
||||
|
||||
// Creates a link that opens the blob on the background page,
|
||||
// and then clicks it. Cribbed from:
|
||||
// http://stackoverflow.com/questions/4845215/making-a-chrome-extension-download-a-file
|
||||
var a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = 'passwords_dump.csv';
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
delete a;// we don't need this anymore
|
||||
});
|
||||
// Creates a link that opens the blob on the background page,
|
||||
// and then clicks it. Cribbed from:
|
||||
// http://stackoverflow.com/questions/4845215/making-a-chrome-extension-download-a-file
|
||||
var a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.download = 'passwords_dump.csv';
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
delete a;// we don't need this anymore
|
||||
});
|
||||
}
|
||||
|
|
2
infobar
2
infobar
|
@ -1 +1 @@
|
|||
Subproject commit 0e3bb2627c521bf79c69a8f60e4bfa9a8e9bc82e
|
||||
Subproject commit fbe957e8631755100661d321a4eb640d797b6254
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "Gombot Alpha",
|
||||
"manifest_version": 2,
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.7",
|
||||
"description": "Tired of remembering your usernames and passwords? Let Gombot do the work for you!",
|
||||
"permissions": [
|
||||
"tabs",
|
||||
|
|
|
@ -38,8 +38,8 @@ $(document).ready(function() {
|
|||
|
||||
var backgroundPage = chrome.extension.getBackgroundPage();
|
||||
// Set user PIN
|
||||
backgroundPage.setAndSavePIN($('[name="pin"]').get()[0].value);
|
||||
backgroundPage.firstRunFinished();
|
||||
backgroundPage.User.PIN.set($('[name="pin"]').get()[0].value);
|
||||
backgroundPage.User.firstRun.setIfCompleted(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче