1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-06-27 10:46:02 +02:00

refactor autofill. add auto-fill on page load

This commit is contained in:
Kyle Spearrin 2017-08-28 13:00:46 -04:00
parent c40465f292
commit ad544e5240
12 changed files with 225 additions and 111 deletions

View File

@ -729,5 +729,14 @@
}, },
"environmentSaved": { "environmentSaved": {
"message": "The environment URLs have been saved." "message": "The environment URLs have been saved."
},
"enableAutoFillOnPageLoad": {
"message": "Enable Auto-fill On Page Load."
},
"enableAutoFillOnPageLoadDesc": {
"message": "If a login form is detected, automatically perform an auto-fill when the web page loads."
},
"experimentalFeature": {
"message": "This is currently an experimental feature. Use at your own risk."
} }
} }

View File

@ -15,9 +15,9 @@ var bg_lockService = new LockService(bg_constantsService, bg_cryptoService, bg_f
refreshBadgeAndMenu); refreshBadgeAndMenu);
var bg_syncService = new SyncService(bg_loginService, bg_folderService, bg_userService, bg_apiService, bg_settingsService, var bg_syncService = new SyncService(bg_loginService, bg_folderService, bg_userService, bg_apiService, bg_settingsService,
bg_cryptoService, logout); bg_cryptoService, logout);
var bg_autofillService = new AutofillService();
var bg_passwordGenerationService = new PasswordGenerationService(); var bg_passwordGenerationService = new PasswordGenerationService();
var bg_totpService = new TotpService(bg_constantsService); var bg_totpService = new TotpService(bg_constantsService);
var bg_autofillService = new AutofillService(bg_utilsService, bg_totpService, bg_tokenService, bg_loginService);
if (chrome.commands) { if (chrome.commands) {
chrome.commands.onCommand.addListener(function (command) { chrome.commands.onCommand.addListener(function (command) {
@ -63,7 +63,7 @@ chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
messageTab(sender.tab.id, 'closeNotificationBar'); messageTab(sender.tab.id, 'closeNotificationBar');
} }
else if (msg.command === 'bgCollectPageDetails') { else if (msg.command === 'bgCollectPageDetails') {
collectPageDetailsForContentScript(sender.tab); collectPageDetailsForContentScript(sender.tab, msg.sender);
} }
else if (msg.command === 'bgAddLogin') { else if (msg.command === 'bgAddLogin') {
addLogin(msg.login, sender.tab); addLogin(msg.login, sender.tab);
@ -78,13 +78,18 @@ chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
saveNever(sender.tab); saveNever(sender.tab);
} }
else if (msg.command === 'collectPageDetailsResponse') { else if (msg.command === 'collectPageDetailsResponse') {
if (msg.contentScript) { if (msg.sender === 'notificationBar') {
var forms = bg_autofillService.getFormsWithPasswordFields(msg.details); var forms = bg_autofillService.getFormsWithPasswordFields(msg.details);
messageTab(msg.tabId, 'pageDetails', { details: msg.details, forms: forms }); messageTab(msg.tab.id, 'pageDetails', { details: msg.details, forms: forms });
}
else if (msg.sender === 'autofiller') {
bg_autofillService.doAutoFillForFirstLogin([{
frameId: sender.frameId, tab: msg.tab, details: msg.details
}]);
} }
else { else {
clearTimeout(autofillTimeout); clearTimeout(autofillTimeout);
pageDetailsToAutoFill.push({ frameId: sender.frameId, tabId: msg.tabId, details: msg.details }); pageDetailsToAutoFill.push({ frameId: sender.frameId, tab: msg.tab, details: msg.details });
autofillTimeout = setTimeout(autofillPage, 300); autofillTimeout = setTimeout(autofillPage, 300);
} }
} else if (msg.command === 'bgUpdateContextMenu') { } else if (msg.command === 'bgUpdateContextMenu') {
@ -396,8 +401,8 @@ function messageTab(tabId, command, data, callback) {
}); });
} }
function collectPageDetailsForContentScript(tab) { function collectPageDetailsForContentScript(tab, sender) {
chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tabId: tab.id, contentScript: true }, function () { chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tab: tab, sender: sender }, function () {
}); });
} }
@ -555,74 +560,28 @@ function checkbg_loginsToAdd(tab, callback) {
function startAutofillPage(login) { function startAutofillPage(login) {
loginToAutoFill = login; loginToAutoFill = login;
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
var tabId = null; var tab = null;
if (tabs.length > 0) { if (tabs.length > 0) {
tabId = tabs[0].id; tab = tabs[0];
} }
else { else {
return; return;
} }
if (!tabId) { if (!tab) {
return; return;
} }
chrome.tabs.sendMessage(tabId, { command: 'collectPageDetails', tabId: tabId }, function () { chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tab: tab }, function () {
}); });
}); });
} }
function autofillPage() { function autofillPage() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { bg_autofillService.doAutoFill(loginToAutoFill, pageDetailsToAutoFill, true);
var tabId = null; // reset
if (tabs.length > 0) { loginToAutoFill = null;
tabId = tabs[0].id; pageDetailsToAutoFill = [];
}
else {
return;
}
if (!tabId) {
return;
}
if (loginToAutoFill && pageDetailsToAutoFill && pageDetailsToAutoFill.length) {
for (var i = 0; i < pageDetailsToAutoFill.length; i++) {
// make sure we're still on correct tab
if (pageDetailsToAutoFill[i].tabId !== tabId) {
continue;
}
var fillScript = bg_autofillService.generateFillScript(pageDetailsToAutoFill[i].details,
loginToAutoFill.username, loginToAutoFill.password);
if (tabId && fillScript && fillScript.script && fillScript.script.length) {
chrome.tabs.sendMessage(tabId, {
command: 'fillForm',
fillScript: fillScript
}, { frameId: pageDetailsToAutoFill[i].frameId });
if (!bg_utilsService.isFirefox() && loginToAutoFill.totp && bg_tokenService.getPremium()) {
var totpKey = loginToAutoFill.totp;
bg_totpService.isAutoCopyEnabled().then(function (enabled) {
if (enabled) {
return bg_totpService.getCode(totpKey);
}
return null;
}).then(function (code) {
if (code) {
bg_utilsService.copyToClipboard(code);
}
});
}
}
}
}
// reset
loginToAutoFill = null;
pageDetailsToAutoFill = [];
});
} }
function sortLogins(logins) { function sortLogins(logins) {

View File

@ -962,9 +962,9 @@
var pageDetailsObj = JSON.parse(pageDetails); var pageDetailsObj = JSON.parse(pageDetails);
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
command: 'collectPageDetailsResponse', command: 'collectPageDetailsResponse',
tabId: msg.tabId, tab: msg.tab,
details: pageDetailsObj, details: pageDetailsObj,
contentScript: msg.contentScript ? true : false sender: msg.sender
}); });
sendResponse(); sendResponse();
return true; return true;

15
src/content/autofiller.js Normal file
View File

@ -0,0 +1,15 @@
document.addEventListener('DOMContentLoaded', function (event) {
chrome.storage.local.get('enableAutoFillOnPageLoad', function (obj) {
if (obj && obj.enableAutoFillOnPageLoad === true) {
setTimeout(fill, 500);
window.addEventListener('popstate', fill);
}
});
function fill() {
chrome.runtime.sendMessage({
command: 'bgCollectPageDetails',
sender: 'autofiller'
});
}
});

View File

@ -36,7 +36,8 @@
chrome.storage.local.get('disableAddLoginNotification', function (obj) { chrome.storage.local.get('disableAddLoginNotification', function (obj) {
if (!obj || !obj.disableAddLoginNotification) { if (!obj || !obj.disableAddLoginNotification) {
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
command: 'bgCollectPageDetails' command: 'bgCollectPageDetails',
sender: 'notificationBar'
}); });
} }
}); });

View File

@ -27,6 +27,18 @@
], ],
"run_at": "document_start" "run_at": "document_start"
}, },
{
"all_frames": true,
"js": [
"content/autofiller.js"
],
"matches": [
"http://*/*",
"https://*/*",
"file:///*"
],
"run_at": "document_start"
},
{ {
"all_frames": false, "all_frames": false,
"js": [ "js": [

View File

@ -6,7 +6,6 @@ angular
$scope.i18n = i18nService; $scope.i18n = i18nService;
var pageDetails = [], var pageDetails = [],
tabId = null,
url = null, url = null,
domain = null, domain = null,
canAutofill = false; canAutofill = false;
@ -22,7 +21,6 @@ angular
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
if (tabs.length > 0) { if (tabs.length > 0) {
url = tabs[0].url; url = tabs[0].url;
tabId = tabs[0].id;
} }
else { else {
$scope.loaded = true; $scope.loaded = true;
@ -37,9 +35,10 @@ angular
return; return;
} }
chrome.tabs.sendMessage(tabId, { command: 'collectPageDetails', tabId: tabId }, function () { chrome.tabs.sendMessage(tabs[0].id,
canAutofill = true; { command: 'collectPageDetails', tab: tabs[0], sender: 'currentController' }, function () {
}); canAutofill = true;
});
$q.when(loginService.getAllDecryptedForDomain(domain)).then(function (logins) { $q.when(loginService.getAllDecryptedForDomain(domain)).then(function (logins) {
$scope.loaded = true; $scope.loaded = true;
@ -68,49 +67,18 @@ angular
}; };
$scope.fillLogin = function (login) { $scope.fillLogin = function (login) {
var didAutofill = false; if (!canAutofill) {
if (login && canAutofill && pageDetails && pageDetails.length) {
for (var i = 0; i < pageDetails.length; i++) {
if (pageDetails[i].tabId !== tabId) {
continue;
}
var fillScript = autofillService.generateFillScript(pageDetails[i].details, login.username, login.password);
if (tabId && fillScript && fillScript.script && fillScript.script.length) {
didAutofill = true;
$analytics.eventTrack('Autofilled');
chrome.tabs.sendMessage(tabId, {
command: 'fillForm',
fillScript: fillScript
}, { frameId: pageDetails[i].frameId }, function () {
if (login.totp && tokenService.getPremium()) {
totpService.isAutoCopyEnabled().then(function (enabled) {
if (enabled) {
return totpService.getCode(login.totp);
}
return null;
}).then(function (code) {
if (code) {
utilsService.copyToClipboard(code);
}
$window.close();
});
}
else {
$window.close();
}
});
}
}
}
if (!didAutofill) {
$analytics.eventTrack('Autofilled Error'); $analytics.eventTrack('Autofilled Error');
toastr.error(i18nService.autofillError); toastr.error(i18nService.autofillError);
} }
autofillService.doAutoFill(login, pageDetails, false).then(function () {
$analytics.eventTrack('Autofilled');
$window.close();
}, function () {
$analytics.eventTrack('Autofilled Error');
toastr.error(i18nService.autofillError);
});
}; };
$scope.viewLogin = function (login) { $scope.viewLogin = function (login) {

View File

@ -38,7 +38,7 @@ angular
else if (msg.command === 'collectPageDetailsResponse') { else if (msg.command === 'collectPageDetailsResponse') {
$scope.$broadcast('collectPageDetailsResponse', { $scope.$broadcast('collectPageDetailsResponse', {
frameId: sender.frameId, frameId: sender.frameId,
tabId: msg.tabId, tab: msg.tab,
details: msg.details details: msg.details
}); });
} }

View File

@ -7,6 +7,14 @@
$scope.disableGa = false; $scope.disableGa = false;
$scope.disableAddLoginNotification = false; $scope.disableAddLoginNotification = false;
$scope.disableContextMenuItem = false; $scope.disableContextMenuItem = false;
$scope.disableAutoTotpCopy = false;
$scope.enableAutoFillOnPageLoad = false;
chrome.storage.local.get(constantsService.enableAutoFillOnPageLoadKey, function (obj) {
$timeout(function () {
$scope.enableAutoFillOnPageLoad = obj && obj[constantsService.enableAutoFillOnPageLoadKey] === true;
});
});
chrome.storage.local.get(constantsService.disableGaKey, function (obj) { chrome.storage.local.get(constantsService.disableGaKey, function (obj) {
$timeout(function () { $timeout(function () {
@ -145,4 +153,27 @@
}); });
}); });
}; };
$scope.updateAutoFillOnPageLoad = function () {
chrome.storage.local.get(constantsService.enableAutoFillOnPageLoadKey, function (obj) {
if (obj[constantsService.enableAutoFillOnPageLoadKey]) {
// disable
obj[constantsService.enableAutoFillOnPageLoadKey] = false;
}
else {
// enable
$analytics.eventTrack('Enable Auto-fill Page Load');
obj[constantsService.enableAutoFillOnPageLoadKey] = true;
}
chrome.storage.local.set(obj, function () {
$timeout(function () {
$scope.enableAutoFillOnPageLoad = obj[constantsService.enableAutoFillOnPageLoadKey];
});
if (!obj[constantsService.enableAutoFillOnPageLoadKey]) {
$analytics.eventTrack('Disable Auto-fill Page Load');
}
});
});
};
}); });

View File

@ -6,6 +6,19 @@
</div> </div>
<div class="content"> <div class="content">
<div class="list"> <div class="list">
<div class="list-section">
<div class="list-section-items">
<div class="list-section-item list-section-item-checkbox">
<label for="totp-copy">{{i18n.enableAutoFillOnPageLoad}}</label>
<input id="totp-copy" type="checkbox" ng-model="enableAutoFillOnPageLoad"
ng-change="updateAutoFillOnPageLoad()">
</div>
</div>
<div class="list-section-footer">
{{i18n.enableAutoFillOnPageLoadDesc}}
<b>{{i18n.warning}}</b>: {{i18n.experimentalFeature}}
</div>
</div>
<div class="list-section"> <div class="list-section">
<div class="list-section-items"> <div class="list-section-items">
<div class="list-section-item list-section-item-checkbox"> <div class="list-section-item list-section-item-checkbox">

View File

@ -1,4 +1,9 @@
function AutofillService() { function AutofillService(utilsService, totpService, tokenService, loginService) {
this.utilsService = utilsService;
this.totpService = totpService;
this.tokenService = tokenService;
this.loginService = loginService;
initAutofill(); initAutofill();
} }
@ -130,6 +135,106 @@ function initAutofill() {
return formData; return formData;
}; };
AutofillService.prototype.doAutoFill = function (login, pageDetails, fromBackground, skipTotp) {
var deferred = Q.defer();
var self = this;
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
var tab = null;
if (tabs.length > 0) {
tab = tabs[0];
}
else {
deferred.reject();
return;
}
if (!tab || !login || !pageDetails || !pageDetails.length) {
deferred.reject();
return;
}
var didAutofill = false;
for (var i = 0; i < pageDetails.length; i++) {
// make sure we're still on correct tab
if (pageDetails[i].tab.id !== tab.id || pageDetails[i].tab.url !== tab.url) {
continue;
}
var fillScript = self.generateFillScript(pageDetails[i].details,
login.username, login.password);
if (!fillScript || !fillScript.script || !fillScript.script.length) {
continue;
}
didAutofill = true;
chrome.tabs.sendMessage(tab.id, {
command: 'fillForm',
fillScript: fillScript
}, { frameId: pageDetails[i].frameId });
if ((fromBackground && self.utilsService.isFirefox()) ||
skipTotp || !login.totp || !self.tokenService.getPremium()) {
deferred.resolve();
return;
}
self.totpService.isAutoCopyEnabled().then(function (enabled) {
if (enabled) {
return self.totpService.getCode(login.totp);
}
return null;
}).then(function (code) {
if (code) {
self.utilsService.copyToClipboard(code);
}
deferred.resolve();
return;
});
break;
}
if (!didAutofill) {
deferred.reject();
return;
}
});
return deferred.promise;
};
AutofillService.prototype.doAutoFillForFirstLogin = function (pageDetails) {
var self = this;
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
var tab = null;
if (tabs.length > 0) {
tab = tabs[0];
}
if (!tab || !tab.url) {
return;
}
var tabDomain = self.utilsService.getDomain(tab.url);
if (!tabDomain) {
return;
}
self.loginService.getAllDecryptedForDomain(tabDomain).then(function (logins) {
if (!logins.length) {
return;
}
self.doAutoFill(logins[0], pageDetails, true, true);
});
});
}
function loadPasswordFields(pageDetails, canBeHidden) { function loadPasswordFields(pageDetails, canBeHidden) {
var arr = []; var arr = [];
for (var i = 0; i < pageDetails.fields.length; i++) { for (var i = 0; i < pageDetails.fields.length; i++) {

View File

@ -7,6 +7,7 @@ function ConstantsService(i18nService) {
disableAddLoginNotificationKey: 'disableAddLoginNotification', disableAddLoginNotificationKey: 'disableAddLoginNotification',
disableContextMenuItemKey: 'disableContextMenuItem', disableContextMenuItemKey: 'disableContextMenuItem',
disableAutoTotpCopyKey: 'disableAutoTotpCopy', disableAutoTotpCopyKey: 'disableAutoTotpCopy',
enableAutoFillOnPageLoadKey: 'enableAutoFillOnPageLoad',
lockOptionKey: 'lockOption', lockOptionKey: 'lockOption',
lastActiveKey: 'lastActive', lastActiveKey: 'lastActive',
encType: { encType: {