mirror of
https://github.com/bitwarden/desktop.git
synced 2024-09-06 00:28:04 +02:00
refactor autofill. add auto-fill on page load
This commit is contained in:
parent
c40465f292
commit
ad544e5240
@ -729,5 +729,14 @@
|
||||
},
|
||||
"environmentSaved": {
|
||||
"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."
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ var bg_lockService = new LockService(bg_constantsService, bg_cryptoService, bg_f
|
||||
refreshBadgeAndMenu);
|
||||
var bg_syncService = new SyncService(bg_loginService, bg_folderService, bg_userService, bg_apiService, bg_settingsService,
|
||||
bg_cryptoService, logout);
|
||||
var bg_autofillService = new AutofillService();
|
||||
var bg_passwordGenerationService = new PasswordGenerationService();
|
||||
var bg_totpService = new TotpService(bg_constantsService);
|
||||
var bg_autofillService = new AutofillService(bg_utilsService, bg_totpService, bg_tokenService, bg_loginService);
|
||||
|
||||
if (chrome.commands) {
|
||||
chrome.commands.onCommand.addListener(function (command) {
|
||||
@ -63,7 +63,7 @@ chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
|
||||
messageTab(sender.tab.id, 'closeNotificationBar');
|
||||
}
|
||||
else if (msg.command === 'bgCollectPageDetails') {
|
||||
collectPageDetailsForContentScript(sender.tab);
|
||||
collectPageDetailsForContentScript(sender.tab, msg.sender);
|
||||
}
|
||||
else if (msg.command === 'bgAddLogin') {
|
||||
addLogin(msg.login, sender.tab);
|
||||
@ -78,13 +78,18 @@ chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
|
||||
saveNever(sender.tab);
|
||||
}
|
||||
else if (msg.command === 'collectPageDetailsResponse') {
|
||||
if (msg.contentScript) {
|
||||
if (msg.sender === 'notificationBar') {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
} else if (msg.command === 'bgUpdateContextMenu') {
|
||||
@ -396,8 +401,8 @@ function messageTab(tabId, command, data, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function collectPageDetailsForContentScript(tab) {
|
||||
chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tabId: tab.id, contentScript: true }, function () {
|
||||
function collectPageDetailsForContentScript(tab, sender) {
|
||||
chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tab: tab, sender: sender }, function () {
|
||||
});
|
||||
}
|
||||
|
||||
@ -555,74 +560,28 @@ function checkbg_loginsToAdd(tab, callback) {
|
||||
function startAutofillPage(login) {
|
||||
loginToAutoFill = login;
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
|
||||
var tabId = null;
|
||||
var tab = null;
|
||||
if (tabs.length > 0) {
|
||||
tabId = tabs[0].id;
|
||||
tab = tabs[0];
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tabId) {
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.tabs.sendMessage(tabId, { command: 'collectPageDetails', tabId: tabId }, function () {
|
||||
chrome.tabs.sendMessage(tab.id, { command: 'collectPageDetails', tab: tab }, function () {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function autofillPage() {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
|
||||
var tabId = null;
|
||||
if (tabs.length > 0) {
|
||||
tabId = tabs[0].id;
|
||||
}
|
||||
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 = [];
|
||||
});
|
||||
bg_autofillService.doAutoFill(loginToAutoFill, pageDetailsToAutoFill, true);
|
||||
// reset
|
||||
loginToAutoFill = null;
|
||||
pageDetailsToAutoFill = [];
|
||||
}
|
||||
|
||||
function sortLogins(logins) {
|
||||
|
@ -962,9 +962,9 @@
|
||||
var pageDetailsObj = JSON.parse(pageDetails);
|
||||
chrome.runtime.sendMessage({
|
||||
command: 'collectPageDetailsResponse',
|
||||
tabId: msg.tabId,
|
||||
tab: msg.tab,
|
||||
details: pageDetailsObj,
|
||||
contentScript: msg.contentScript ? true : false
|
||||
sender: msg.sender
|
||||
});
|
||||
sendResponse();
|
||||
return true;
|
||||
|
15
src/content/autofiller.js
Normal file
15
src/content/autofiller.js
Normal 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'
|
||||
});
|
||||
}
|
||||
});
|
@ -36,7 +36,8 @@
|
||||
chrome.storage.local.get('disableAddLoginNotification', function (obj) {
|
||||
if (!obj || !obj.disableAddLoginNotification) {
|
||||
chrome.runtime.sendMessage({
|
||||
command: 'bgCollectPageDetails'
|
||||
command: 'bgCollectPageDetails',
|
||||
sender: 'notificationBar'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -27,6 +27,18 @@
|
||||
],
|
||||
"run_at": "document_start"
|
||||
},
|
||||
{
|
||||
"all_frames": true,
|
||||
"js": [
|
||||
"content/autofiller.js"
|
||||
],
|
||||
"matches": [
|
||||
"http://*/*",
|
||||
"https://*/*",
|
||||
"file:///*"
|
||||
],
|
||||
"run_at": "document_start"
|
||||
},
|
||||
{
|
||||
"all_frames": false,
|
||||
"js": [
|
||||
|
@ -6,7 +6,6 @@ angular
|
||||
$scope.i18n = i18nService;
|
||||
|
||||
var pageDetails = [],
|
||||
tabId = null,
|
||||
url = null,
|
||||
domain = null,
|
||||
canAutofill = false;
|
||||
@ -22,7 +21,6 @@ angular
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
|
||||
if (tabs.length > 0) {
|
||||
url = tabs[0].url;
|
||||
tabId = tabs[0].id;
|
||||
}
|
||||
else {
|
||||
$scope.loaded = true;
|
||||
@ -37,9 +35,10 @@ angular
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.tabs.sendMessage(tabId, { command: 'collectPageDetails', tabId: tabId }, function () {
|
||||
canAutofill = true;
|
||||
});
|
||||
chrome.tabs.sendMessage(tabs[0].id,
|
||||
{ command: 'collectPageDetails', tab: tabs[0], sender: 'currentController' }, function () {
|
||||
canAutofill = true;
|
||||
});
|
||||
|
||||
$q.when(loginService.getAllDecryptedForDomain(domain)).then(function (logins) {
|
||||
$scope.loaded = true;
|
||||
@ -68,49 +67,18 @@ angular
|
||||
};
|
||||
|
||||
$scope.fillLogin = function (login) {
|
||||
var didAutofill = false;
|
||||
|
||||
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) {
|
||||
if (!canAutofill) {
|
||||
$analytics.eventTrack('Autofilled Error');
|
||||
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) {
|
||||
|
@ -38,7 +38,7 @@ angular
|
||||
else if (msg.command === 'collectPageDetailsResponse') {
|
||||
$scope.$broadcast('collectPageDetailsResponse', {
|
||||
frameId: sender.frameId,
|
||||
tabId: msg.tabId,
|
||||
tab: msg.tab,
|
||||
details: msg.details
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,14 @@
|
||||
$scope.disableGa = false;
|
||||
$scope.disableAddLoginNotification = 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) {
|
||||
$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');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
@ -6,6 +6,19 @@
|
||||
</div>
|
||||
<div class="content">
|
||||
<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-items">
|
||||
<div class="list-section-item list-section-item-checkbox">
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
@ -130,6 +135,106 @@ function initAutofill() {
|
||||
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) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < pageDetails.fields.length; i++) {
|
||||
|
@ -7,6 +7,7 @@ function ConstantsService(i18nService) {
|
||||
disableAddLoginNotificationKey: 'disableAddLoginNotification',
|
||||
disableContextMenuItemKey: 'disableContextMenuItem',
|
||||
disableAutoTotpCopyKey: 'disableAutoTotpCopy',
|
||||
enableAutoFillOnPageLoadKey: 'enableAutoFillOnPageLoad',
|
||||
lockOptionKey: 'lockOption',
|
||||
lastActiveKey: 'lastActive',
|
||||
encType: {
|
||||
|
Loading…
Reference in New Issue
Block a user