From bfa352d71bfe3658b04e48852d7704f8ee1880cb Mon Sep 17 00:00:00 2001 From: Paul Sawaya Date: Thu, 10 Jan 2013 14:56:33 -0800 Subject: [PATCH] first take on storage refactor --- background/browser_action_interface.js | 12 +- background/first_run.js | 11 +- background/infobar_hooks.js | 8 +- background/main.js | 324 +++++++++++---------- background/storage.js | 379 ++++++++++++++----------- infobar | 2 +- manifest.json | 2 +- pages/first_run/create_account.js | 4 +- 8 files changed, 380 insertions(+), 362 deletions(-) diff --git a/background/browser_action_interface.js b/background/browser_action_interface.js index 87fbf3f..5e28bbc 100644 --- a/background/browser_action_interface.js +++ b/background/browser_action_interface.js @@ -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] }); } \ No newline at end of file diff --git a/background/first_run.js b/background/first_run.js index 9155cbf..8fa90a0 100644 --- a/background/first_run.js +++ b/background/first_run.js @@ -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); -} +} \ No newline at end of file diff --git a/background/infobar_hooks.js b/background/infobar_hooks.js index be3e22e..f2c53ac 100644 --- a/background/infobar_hooks.js +++ b/background/infobar_hooks.js @@ -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); } } diff --git a/background/main.js b/background/main.js index cb9e7f4..54166da 100644 --- a/background/main.js +++ b/background/main.js @@ -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); } diff --git a/background/storage.js b/background/storage.js index cf39157..269067c 100644 --- a/background/storage.js +++ b/background/storage.js @@ -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 + }); } diff --git a/infobar b/infobar index 0e3bb26..fbe957e 160000 --- a/infobar +++ b/infobar @@ -1 +1 @@ -Subproject commit 0e3bb2627c521bf79c69a8f60e4bfa9a8e9bc82e +Subproject commit fbe957e8631755100661d321a4eb640d797b6254 diff --git a/manifest.json b/manifest.json index 9abc055..868a605 100644 --- a/manifest.json +++ b/manifest.json @@ -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", diff --git a/pages/first_run/create_account.js b/pages/first_run/create_account.js index db1039a..cf5a667 100644 --- a/pages/first_run/create_account.js +++ b/pages/first_run/create_account.js @@ -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); } }); });