mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Big refactor
This commit is contained in:
parent
4212c7eff4
commit
75b3120cb0
22 changed files with 1987 additions and 2308 deletions
|
|
@ -263,6 +263,10 @@
|
|||
"message": "No credentials for the given username found.",
|
||||
"description": "Alert message when no credentials are found for the given username."
|
||||
},
|
||||
"credentialsNoTOTPFound": {
|
||||
"message": "No TOTP found.",
|
||||
"description": "Alert message when no TOTP is found."
|
||||
},
|
||||
"credentialExpired": {
|
||||
"message": "Expired",
|
||||
"description": "If a credential is expired, this is appended to the title label."
|
||||
|
|
|
|||
|
|
@ -2,33 +2,30 @@
|
|||
|
||||
const browserAction = {};
|
||||
|
||||
browserAction.show = function(tab) {
|
||||
let data = {};
|
||||
if (!page.tabs[tab.id] || page.tabs[tab.id].stack.length === 0) {
|
||||
browserAction.showDefault(tab);
|
||||
return;
|
||||
} else {
|
||||
data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1];
|
||||
browserAction.show = function(tab, popupData) {
|
||||
if (!popupData) {
|
||||
popupData = page.popupData;
|
||||
}
|
||||
|
||||
page.popupData = popupData;
|
||||
|
||||
browser.browserAction.setIcon({
|
||||
tabId: tab.id,
|
||||
path: '/icons/toolbar/' + browserAction.generateIconName(data.iconType, data.icon)
|
||||
path: browserAction.generateIconName(popupData.iconType)
|
||||
});
|
||||
|
||||
if (data.popup) {
|
||||
if (popupData.popup) {
|
||||
browser.browserAction.setPopup({
|
||||
tabId: tab.id,
|
||||
popup: 'popups/' + data.popup
|
||||
popup: `popups/${popupData.popup}.html`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
browserAction.showDefault = async function(tab) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
const popupData = {
|
||||
iconType: 'normal',
|
||||
popup: 'popup.html'
|
||||
popup: 'popup'
|
||||
};
|
||||
|
||||
const response = await keepass.isConfigured().catch((err) => {
|
||||
|
|
@ -36,77 +33,41 @@ browserAction.showDefault = async function(tab) {
|
|||
});
|
||||
|
||||
if (!response && !keepass.isKeePassXCAvailable) {
|
||||
stackData.iconType = 'cross';
|
||||
popupData.iconType = 'cross';
|
||||
} else if (keepass.isKeePassXCAvailable && keepass.isDatabaseClosed) {
|
||||
stackData.iconType = 'locked';
|
||||
popupData.iconType = 'locked';
|
||||
}
|
||||
|
||||
if (page.tabs[tab.id] && page.tabs[tab.id].loginList.length > 0) {
|
||||
stackData.iconType = 'questionmark';
|
||||
stackData.popup = 'popup_login.html';
|
||||
popupData.iconType = 'questionmark';
|
||||
popupData.popup = 'popup_login';
|
||||
}
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
browserAction.show(tab);
|
||||
browserAction.show(tab, popupData);
|
||||
};
|
||||
|
||||
browserAction.removeLevelFromStack = function(tab, level, type, dontShow) {
|
||||
if (!page.tabs[tab.id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
type = '<=';
|
||||
}
|
||||
|
||||
const newStack = [];
|
||||
for (const i of page.tabs[tab.id].stack) {
|
||||
if ((type === '<' && i.level >= level)
|
||||
|| (type === '<=' && i.level > level)
|
||||
|| (type === '=' && i.level !== level)
|
||||
|| (type === '==' && i.level !== level)
|
||||
|| (type === '!=' && i.level === level)
|
||||
|| (type === '>' && i.level <= level)
|
||||
|| (type === '>=' && i.level < level)) {
|
||||
newStack.push(i);
|
||||
browserAction.updateIcon = async function(tab, iconType) {
|
||||
if (!tab) {
|
||||
const tabs = await browser.tabs.query({ 'active': true, 'currentWindow': true });
|
||||
if (tabs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tab = tabs[0];
|
||||
}
|
||||
|
||||
page.tabs[tab.id].stack = newStack;
|
||||
|
||||
if (!dontShow) {
|
||||
browserAction.show(tab);
|
||||
}
|
||||
browser.browserAction.setIcon({
|
||||
tabId: tab.id,
|
||||
path: browserAction.generateIconName(iconType)
|
||||
});
|
||||
};
|
||||
|
||||
browserAction.stackPop = function(tabId) {
|
||||
const id = tabId || page.currentTabId;
|
||||
page.tabs[id].stack.pop();
|
||||
};
|
||||
|
||||
browserAction.stackPush = function(data, tabId) {
|
||||
const id = tabId || page.currentTabId;
|
||||
browserAction.removeLevelFromStack({ 'id': id }, data.level, '<=', true);
|
||||
page.tabs[id].stack.push(data);
|
||||
};
|
||||
|
||||
browserAction.stackUnshift = function(data, tabId) {
|
||||
const id = tabId || page.currentTabId;
|
||||
browserAction.removeLevelFromStack({ 'id': id }, data.level, '<=', true);
|
||||
page.tabs[id].stack.unshift(data);
|
||||
};
|
||||
|
||||
browserAction.generateIconName = function(iconType, icon) {
|
||||
if (icon) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
browserAction.generateIconName = function(iconType) {
|
||||
let name = 'icon_';
|
||||
name += (keepass.keePassXCUpdateAvailable()) ? 'new_' : '';
|
||||
name += (!iconType || iconType === 'normal') ? 'normal' : iconType;
|
||||
name += '.png';
|
||||
|
||||
return name;
|
||||
return `/icons/toolbar/${name}.png`;
|
||||
};
|
||||
|
||||
browserAction.ignoreSite = async function(url) {
|
||||
|
|
|
|||
|
|
@ -13,13 +13,16 @@ kpxcEvent.onMessage = async function(request, sender) {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcEvent.showStatus = async function(tab, configured) {
|
||||
kpxcEvent.showStatus = async function(tab, configured, internalPoll) {
|
||||
let keyId = null;
|
||||
if (configured && keepass.databaseHash !== '') {
|
||||
keyId = keepass.keyRing[keepass.databaseHash].id;
|
||||
}
|
||||
|
||||
browserAction.showDefault(tab);
|
||||
if (!internalPoll) {
|
||||
browserAction.showDefault(tab);
|
||||
}
|
||||
|
||||
const errorMessage = page.tabs[tab.id].errorMessage;
|
||||
return {
|
||||
identifier: keyId,
|
||||
|
|
@ -29,7 +32,7 @@ kpxcEvent.showStatus = async function(tab, configured) {
|
|||
encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized,
|
||||
associated: keepass.isAssociated(),
|
||||
error: errorMessage || null,
|
||||
usernameFieldDetected: page.usernameFieldDetected
|
||||
usernameFieldDetected: page.tabs[tab.id].usernameFieldDetected
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -57,11 +60,9 @@ kpxcEvent.onLoadKeyRing = async function() {
|
|||
return item.keyRing;
|
||||
};
|
||||
|
||||
kpxcEvent.onSaveSettings = async function(tab, args = []) {
|
||||
const [ settings ] = args;
|
||||
kpxcEvent.onSaveSettings = async function(tab, settings) {
|
||||
browser.storage.local.set({ 'settings': settings });
|
||||
kpxcEvent.onLoadSettings(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onGetStatus = async function(tab, args = []) {
|
||||
|
|
@ -76,7 +77,7 @@ kpxcEvent.onGetStatus = async function(tab, args = []) {
|
|||
}
|
||||
|
||||
const configured = await keepass.isConfigured();
|
||||
return kpxcEvent.showStatus(tab, configured);
|
||||
return kpxcEvent.showStatus(tab, configured, internalPoll);
|
||||
} catch (err) {
|
||||
console.log('Error: No status shown: ' + err);
|
||||
return Promise.reject();
|
||||
|
|
@ -107,18 +108,12 @@ kpxcEvent.lockDatabase = async function(tab) {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcEvent.onPopStack = function(tab) {
|
||||
browserAction.stackPop(tab.id);
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onGetTabInformation = async function(tab) {
|
||||
const id = tab.id || page.currentTabId;
|
||||
return page.tabs[id];
|
||||
};
|
||||
|
||||
kpxcEvent.onGetConnectedDatabase = function() {
|
||||
kpxcEvent.onGetConnectedDatabase = async function() {
|
||||
return Promise.resolve({
|
||||
count: Object.keys(keepass.keyRing).length,
|
||||
identifier: (keepass.keyRing[keepass.associated.hash]) ? keepass.keyRing[keepass.associated.hash].id : null
|
||||
|
|
@ -143,102 +138,46 @@ kpxcEvent.onUpdateAvailableKeePassXC = async function() {
|
|||
return (page.settings.checkUpdateKeePassXC > 0) ? keepass.keePassXCUpdateAvailable() : false;
|
||||
};
|
||||
|
||||
kpxcEvent.onRemoveCredentialsFromTabInformation = function(tab) {
|
||||
kpxcEvent.onRemoveCredentialsFromTabInformation = async function(tab) {
|
||||
const id = tab.id || page.currentTabId;
|
||||
page.clearCredentials(id);
|
||||
page.clearSubmittedCredentials();
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onLoginPopup = function(tab, logins) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
kpxcEvent.onLoginPopup = async function(tab, logins) {
|
||||
const popupData = {
|
||||
iconType: 'questionmark',
|
||||
popup: 'popup_login.html'
|
||||
popup: 'popup_login'
|
||||
};
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
page.tabs[tab.id].loginList = logins;
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
browserAction.show(tab, popupData);
|
||||
};
|
||||
|
||||
kpxcEvent.initHttpAuth = function() {
|
||||
kpxcEvent.initHttpAuth = async function() {
|
||||
httpAuth.init();
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onHTTPAuthPopup = function(tab, data) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
kpxcEvent.onHTTPAuthPopup = async function(tab, data) {
|
||||
const popupData = {
|
||||
iconType: 'questionmark',
|
||||
popup: 'popup_httpauth.html'
|
||||
popup: 'popup_httpauth'
|
||||
};
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
page.tabs[tab.id].loginList = data;
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
browserAction.show(tab, popupData);
|
||||
};
|
||||
|
||||
kpxcEvent.onMultipleFieldsPopup = function(tab) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
iconType: 'normal',
|
||||
popup: 'popup_multiple-fields.html'
|
||||
};
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageClearLogins = function(tab, alreadyCalled) {
|
||||
if (!alreadyCalled) {
|
||||
page.clearLogins(tab.id);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageGetLoginId = async function() {
|
||||
return page.loginId;
|
||||
};
|
||||
|
||||
kpxcEvent.pageSetLoginId = function(tab, loginId) {
|
||||
page.loginId = loginId;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageClearSubmitted = function() {
|
||||
page.clearSubmittedCredentials();
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageGetSubmitted = async function(tab) {
|
||||
// Do not return any credentials if the tab ID does not match.
|
||||
if (tab.id !== page.submittedCredentials.tabId) {
|
||||
return {};
|
||||
}
|
||||
return page.submittedCredentials;
|
||||
};
|
||||
|
||||
kpxcEvent.pageSetSubmitted = function(tab, args = []) {
|
||||
const [ submitted, username, password, url, oldCredentials ] = args;
|
||||
page.setSubmittedCredentials(submitted, username, password, url, oldCredentials, tab.id);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onUsernameFieldDetected = function(tab, detected) {
|
||||
page.usernameFieldDetected = detected;
|
||||
kpxcEvent.onUsernameFieldDetected = async function(tab, detected) {
|
||||
page.tabs[tab.id].usernameFieldDetected = detected;
|
||||
};
|
||||
|
||||
kpxcEvent.passwordGetFilled = async function() {
|
||||
return page.passwordFilled;
|
||||
};
|
||||
|
||||
kpxcEvent.passwordSetFilled = function(tab, state) {
|
||||
kpxcEvent.passwordSetFilled = async function(tab, state) {
|
||||
page.passwordFilled = state;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.getColorTheme = async function(tab) {
|
||||
|
|
@ -249,6 +188,12 @@ kpxcEvent.pageGetRedirectCount = async function() {
|
|||
return page.redirectCount;
|
||||
};
|
||||
|
||||
kpxcEvent.pageClearLogins = async function(tab, alreadyCalled) {
|
||||
if (!alreadyCalled) {
|
||||
page.clearLogins(tab.id);
|
||||
}
|
||||
};
|
||||
|
||||
// All methods named in this object have to be declared BEFORE this!
|
||||
kpxcEvent.messageHandlers = {
|
||||
'add_credentials': keepass.addCredentials,
|
||||
|
|
@ -266,26 +211,27 @@ kpxcEvent.messageHandlers = {
|
|||
'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions,
|
||||
'get_status': kpxcEvent.onGetStatus,
|
||||
'get_tab_information': kpxcEvent.onGetTabInformation,
|
||||
'get_totp': keepass.getTotp,
|
||||
'init_http_auth': kpxcEvent.initHttpAuth,
|
||||
'is_connected': keepass.getIsKeePassXCAvailable,
|
||||
'load_keyring': kpxcEvent.onLoadKeyRing,
|
||||
'load_settings': kpxcEvent.onLoadSettings,
|
||||
'lock-database': kpxcEvent.lockDatabase,
|
||||
'lock_database': kpxcEvent.lockDatabase,
|
||||
'page_clear_logins': kpxcEvent.pageClearLogins,
|
||||
'page_clear_submitted': kpxcEvent.pageClearSubmitted,
|
||||
'page_get_login_id': kpxcEvent.pageGetLoginId,
|
||||
'page_clear_submitted': page.clearSubmittedCredentials,
|
||||
'page_get_login_id': page.getLoginId,
|
||||
'page_get_manual_fill': page.getManualFill,
|
||||
'page_get_redirect_count': kpxcEvent.pageGetRedirectCount,
|
||||
'page_get_submitted': kpxcEvent.pageGetSubmitted,
|
||||
'page_set_login_id': kpxcEvent.pageSetLoginId,
|
||||
'page_set_submitted': kpxcEvent.pageSetSubmitted,
|
||||
'page_get_submitted': page.getSubmitted,
|
||||
'page_set_login_id': page.setLoginId,
|
||||
'page_set_manual_fill': page.setManualFill,
|
||||
'page_set_submitted': page.setSubmitted,
|
||||
'password_get_filled': kpxcEvent.passwordGetFilled,
|
||||
'password_set_filled': kpxcEvent.passwordSetFilled,
|
||||
'pop_stack': kpxcEvent.onPopStack,
|
||||
'popup_login': kpxcEvent.onLoginPopup,
|
||||
'popup_multiple-fields': kpxcEvent.onMultipleFieldsPopup,
|
||||
'reconnect': kpxcEvent.onReconnect,
|
||||
'remove_credentials_from_tab_information': kpxcEvent.onRemoveCredentialsFromTabInformation,
|
||||
'retrieve_credentials': keepass.retrieveCredentials,
|
||||
'retrieve_credentials': page.retrieveCredentials,
|
||||
'show_default_browseraction': browserAction.showDefault,
|
||||
'update_credentials': keepass.updateCredentials,
|
||||
'username_field_detected': kpxcEvent.onUsernameFieldDetected,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
await httpAuth.init();
|
||||
await keepass.reconnect(null, 5000); // 5 second timeout for the first connect
|
||||
await keepass.enableAutomaticReconnect();
|
||||
await keepass.updateDatabase();
|
||||
} catch (e) {
|
||||
console.log('init.js failed');
|
||||
}
|
||||
|
|
@ -48,9 +49,6 @@ browser.tabs.onRemoved.addListener((tabId, removeInfo) => {
|
|||
* @param {object} activeInfo
|
||||
*/
|
||||
browser.tabs.onActivated.addListener(async function(activeInfo) {
|
||||
// Remove possible credentials from old tab information
|
||||
page.clearCredentials(page.currentTabId, true);
|
||||
|
||||
try {
|
||||
const info = await browser.tabs.get(activeInfo.tabId);
|
||||
if (info && info.id) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ const kpActions = {
|
|||
DATABASE_LOCKED: 'database-locked',
|
||||
DATABASE_UNLOCKED: 'database-unlocked',
|
||||
GET_DATABASE_GROUPS: 'get-database-groups',
|
||||
CREATE_NEW_GROUP: 'create-new-group'
|
||||
CREATE_NEW_GROUP: 'create-new-group',
|
||||
GET_TOTP: 'get-totp'
|
||||
};
|
||||
|
||||
const kpErrors = {
|
||||
|
|
@ -86,6 +87,27 @@ browser.storage.local.get({ 'latestKeePassXC': { 'version': '', 'lastChecked': n
|
|||
keepass.keyRing = item.keyRing;
|
||||
});
|
||||
|
||||
const messageBuffer = {
|
||||
buffer: [],
|
||||
|
||||
addMessage(msg) {
|
||||
if (!this.buffer.includes(msg)) {
|
||||
this.buffer.push(msg);
|
||||
}
|
||||
},
|
||||
|
||||
matchAndRemove(msg) {
|
||||
for (let i = 0; i < this.buffer.length; ++i) {
|
||||
if (msg.nonce && msg.nonce === keepass.incrementedNonce(this.buffer[i].nonce)) {
|
||||
this.buffer.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
keepass.sendNativeMessage = function(request, enableTimeout = false, timeoutValue) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timeout;
|
||||
|
|
@ -95,11 +117,16 @@ keepass.sendNativeMessage = function(request, enableTimeout = false, timeoutValu
|
|||
const listener = ((port, action) => {
|
||||
const handler = (msg) => {
|
||||
if (msg && msg.action === action) {
|
||||
port.removeListener(handler);
|
||||
if (enableTimeout) {
|
||||
clearTimeout(timeout);
|
||||
// Only resolve a matching response or a notification (without nonce)
|
||||
if (!msg.nonce || messageBuffer.matchAndRemove(msg)) {
|
||||
port.removeListener(handler);
|
||||
if (enableTimeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
resolve(msg);
|
||||
return;
|
||||
}
|
||||
resolve(msg);
|
||||
}
|
||||
};
|
||||
return handler;
|
||||
|
|
@ -122,6 +149,9 @@ keepass.sendNativeMessage = function(request, enableTimeout = false, timeoutValu
|
|||
}, messageTimeout);
|
||||
}
|
||||
|
||||
// Store the request to the buffer
|
||||
messageBuffer.addMessage(request);
|
||||
|
||||
// Send the request
|
||||
if (keepass.nativePort) {
|
||||
keepass.nativePort.postMessage(request);
|
||||
|
|
@ -777,6 +807,51 @@ keepass.createNewGroup = async function(tab, args = []) {
|
|||
}
|
||||
};
|
||||
|
||||
keepass.getTotp = async function(tab, args = []) {
|
||||
const [ uuid, oldTotp ] = args;
|
||||
if (!keepass.compareVersion('2.6.1', keepass.currentKeePassXC, true)) {
|
||||
return oldTotp;
|
||||
}
|
||||
|
||||
const taResponse = await keepass.testAssociation(tab, [ false ]);
|
||||
if (!taResponse || !keepass.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const kpAction = kpActions.GET_TOTP;
|
||||
const [ nonce, incrementedNonce ] = keepass.getNonces();
|
||||
|
||||
const messageData = {
|
||||
action: kpAction,
|
||||
uuid: uuid
|
||||
};
|
||||
|
||||
try {
|
||||
const request = keepass.buildRequest(kpAction, keepass.encrypt(messageData, nonce), nonce, keepass.clientID);
|
||||
const response = await keepass.sendNativeMessage(request);
|
||||
if (response.message && response.nonce) {
|
||||
const res = keepass.decrypt(response.message, response.nonce);
|
||||
if (!res) {
|
||||
keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = nacl.util.encodeUTF8(res);
|
||||
const parsed = JSON.parse(message);
|
||||
if (keepass.verifyResponse(parsed, incrementedNonce) && parsed.totp) {
|
||||
keepass.updateLastUsed(keepass.databaseHash);
|
||||
return parsed.totp;
|
||||
}
|
||||
} else if (response.error && response.errorCode) {
|
||||
keepass.handleError(tab, response.errorCode, response.error);
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (err) {
|
||||
console.log('getTotp failed: ', err);
|
||||
}
|
||||
};
|
||||
|
||||
keepass.generateNewKeyPair = function() {
|
||||
keepass.keyPair = nacl.box.keyPair();
|
||||
//console.log(nacl.util.encodeBase64(keepass.keyPair.publicKey) + ' ' + nacl.util.encodeBase64(keepass.keyPair.secretKey));
|
||||
|
|
@ -1159,9 +1234,7 @@ keepass.reconnect = async function(tab, connectionTimeout) {
|
|||
|
||||
keepass.updatePopup = function(iconType) {
|
||||
if (page && page.tabs.length > 0) {
|
||||
const data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1];
|
||||
data.iconType = iconType;
|
||||
browserAction.show({ 'id': page.currentTabId });
|
||||
browserAction.updateIcon(undefined, iconType);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1169,7 +1242,8 @@ keepass.updatePopup = function(iconType) {
|
|||
keepass.updateDatabase = async function() {
|
||||
keepass.associated.value = false;
|
||||
keepass.associated.hash = null;
|
||||
await keepass.testAssociation(null);
|
||||
page.clearAllLogins();
|
||||
await keepass.testAssociation(null, [ true ]);
|
||||
const configured = await keepass.isConfigured();
|
||||
keepass.updatePopup(configured ? 'normal' : 'locked');
|
||||
keepass.updateDatabaseHashToContent();
|
||||
|
|
|
|||
|
|
@ -24,14 +24,20 @@ const defaultSettings = {
|
|||
|
||||
var page = {};
|
||||
page.blockedTabs = [];
|
||||
page.currentRequest = {};
|
||||
page.currentTabId = -1;
|
||||
page.loginId = -1;
|
||||
page.manualFill = ManualFill.NONE;
|
||||
page.passwordFilled = false;
|
||||
page.redirectCount = 0;
|
||||
page.submitted = false;
|
||||
page.submittedCredentials = {};
|
||||
page.tabs = [];
|
||||
page.usernameFieldDetected = false;
|
||||
|
||||
page.popupData = {
|
||||
iconType: 'normal',
|
||||
popup: 'popup'
|
||||
};
|
||||
|
||||
page.initSettings = async function() {
|
||||
try {
|
||||
|
|
@ -136,11 +142,11 @@ page.initOpenedTabs = async function() {
|
|||
// Set initial tab-ID
|
||||
const currentTabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (currentTabs.length === 0) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
page.currentTabId = currentTabs[0].id;
|
||||
browserAction.show(currentTabs[0]);
|
||||
return Promise.resolve();
|
||||
browserAction.showDefault(currentTabs[0]);
|
||||
} catch (err) {
|
||||
console.log('page.initOpenedTabs error: ' + err);
|
||||
return Promise.reject();
|
||||
|
|
@ -149,18 +155,18 @@ page.initOpenedTabs = async function() {
|
|||
|
||||
page.switchTab = function(tab) {
|
||||
browserAction.showDefault(tab);
|
||||
browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => {});
|
||||
browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => {
|
||||
console.log('Cannot send activated_tab message: ', e);
|
||||
});
|
||||
};
|
||||
|
||||
page.clearCredentials = function(tabId, complete) {
|
||||
page.clearCredentials = async function(tabId, complete) {
|
||||
if (!page.tabs[tabId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
page.usernameFieldDetected = false;
|
||||
page.passwordFilled = false;
|
||||
page.tabs[tabId].credentials = [];
|
||||
delete page.tabs[tabId].credentials;
|
||||
|
||||
if (complete) {
|
||||
page.clearLogins(tabId);
|
||||
|
|
@ -176,10 +182,19 @@ page.clearLogins = function(tabId) {
|
|||
return;
|
||||
}
|
||||
|
||||
page.tabs[tabId].credentials = [];
|
||||
page.tabs[tabId].loginList = [];
|
||||
page.currentRequest = {};
|
||||
page.passwordFilled = false;
|
||||
};
|
||||
|
||||
// Clear all logins from all pages and update the content scripts
|
||||
page.clearAllLogins = function() {
|
||||
for (const tabId of Object.keys(page.tabs)) {
|
||||
page.clearCredentials(Number(tabId), true);
|
||||
}
|
||||
};
|
||||
|
||||
page.setSubmittedCredentials = function(submitted, username, password, url, oldCredentials, tabId) {
|
||||
page.submittedCredentials.submitted = submitted;
|
||||
page.submittedCredentials.username = username;
|
||||
|
|
@ -189,17 +204,18 @@ page.setSubmittedCredentials = function(submitted, username, password, url, oldC
|
|||
page.submittedCredentials.tabId = tabId;
|
||||
};
|
||||
|
||||
page.clearSubmittedCredentials = function() {
|
||||
page.clearSubmittedCredentials = async function() {
|
||||
page.submitted = false;
|
||||
page.submittedCredentials = {};
|
||||
};
|
||||
|
||||
page.createTabEntry = function(tabId) {
|
||||
page.tabs[tabId] = {
|
||||
'stack': [],
|
||||
'errorMessage': null,
|
||||
'loginList': []
|
||||
credentials: [],
|
||||
errorMessage: null,
|
||||
loginList: []
|
||||
};
|
||||
|
||||
page.clearSubmittedCredentials();
|
||||
};
|
||||
|
||||
|
|
@ -221,3 +237,61 @@ page.removePageInformationFromNotExistingTabs = async function() {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieves the credentials. Returns cached values when found.
|
||||
// Page reload or tab switch clears the cache.
|
||||
page.retrieveCredentials = async function(tab, args = []) {
|
||||
const [ url, submitUrl ] = args;
|
||||
if (page.tabs[tab.id] && page.tabs[tab.id].credentials.length > 0) {
|
||||
return page.tabs[tab.id].credentials;
|
||||
}
|
||||
|
||||
// Ignore duplicate requests
|
||||
if (page.currentRequest.url === url && page.currentRequest.submitUrl === submitUrl) {
|
||||
return [];
|
||||
} else {
|
||||
page.currentRequest.url = url;
|
||||
page.currentRequest.submitUrl = submitUrl;
|
||||
}
|
||||
|
||||
const credentials = await keepass.retrieveCredentials(tab, args);
|
||||
page.tabs[tab.id].credentials = credentials;
|
||||
return credentials;
|
||||
};
|
||||
|
||||
page.getLoginId = async function(tab) {
|
||||
// If there's only one credential available and loginId is not set
|
||||
if (page.loginId < 0
|
||||
&& page.tabs[tab.id]
|
||||
&& page.tabs[tab.id].credentials.length === 1) {
|
||||
return 0; // Index to the first credential
|
||||
}
|
||||
|
||||
return page.loginId;
|
||||
};
|
||||
|
||||
page.setLoginId = async function(tab, loginId) {
|
||||
page.loginId = loginId;
|
||||
};
|
||||
|
||||
page.getManualFill = async function(tab) {
|
||||
return page.manualFill;
|
||||
};
|
||||
|
||||
page.setManualFill = async function(tab, manualFill) {
|
||||
page.manualFill = manualFill;
|
||||
};
|
||||
|
||||
page.getSubmitted = async function(tab) {
|
||||
// Do not return any credentials if the tab ID does not match.
|
||||
if (tab.id !== page.submittedCredentials.tabId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return page.submittedCredentials;
|
||||
};
|
||||
|
||||
page.setSubmitted = async function(tab, args = []) {
|
||||
const [ submitted, username, password, url, oldCredentials ] = args;
|
||||
page.setSubmittedCredentials(submitted, username, password, url, oldCredentials, tab.id);
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -9,7 +9,7 @@ kpxcAutocomplete.input = undefined;
|
|||
kpxcAutocomplete.shadowRoot = undefined;
|
||||
kpxcAutocomplete.wrapper = undefined;
|
||||
|
||||
kpxcAutocomplete.create = function(input, showListInstantly = false, autoSubmit = false) {
|
||||
kpxcAutocomplete.create = async function(input, showListInstantly = false, autoSubmit = false) {
|
||||
if (input.readOnly) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -63,22 +63,20 @@ kpxcAutocomplete.showList = function(inputField) {
|
|||
item.textContent += c.label;
|
||||
const itemInput = kpxcUI.createElement('input', '', { 'type': 'hidden', 'value': c.value });
|
||||
item.append(itemInput);
|
||||
item.addEventListener('click', function(e) {
|
||||
|
||||
item.addEventListener('click', async function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save index for combination.loginId
|
||||
const index = Array.prototype.indexOf.call(e.currentTarget.parentElement.childNodes, e.currentTarget);
|
||||
browser.runtime.sendMessage({
|
||||
action: 'page_set_login_id', args: index
|
||||
});
|
||||
await sendMessage('page_set_login_id', index);
|
||||
|
||||
inputField.value = this.getElementsByTagName('input')[0].value;
|
||||
kpxcAutocomplete.fillPassword(inputField.value, index);
|
||||
const usernameValue = this.getElementsByTagName('input')[0].value;
|
||||
await kpxcAutocomplete.fillPassword(usernameValue, index, c.uuid);
|
||||
kpxcAutocomplete.closeList();
|
||||
inputField.focus();
|
||||
document.body.removeChild(wrapper);
|
||||
});
|
||||
|
||||
// These events prevent the double hover effect if both keyboard and mouse are used
|
||||
|
|
@ -87,6 +85,7 @@ kpxcAutocomplete.showList = function(inputField) {
|
|||
item.classList.add('kpxcAutocomplete-active');
|
||||
kpxcAutocomplete.index = Array.from(div.childNodes).indexOf(item);
|
||||
});
|
||||
|
||||
item.addEventListener('mouseout', function(e) {
|
||||
item.classList.remove('kpxcAutocomplete-active');
|
||||
});
|
||||
|
|
@ -153,6 +152,7 @@ kpxcAutocomplete.getAllItems = function() {
|
|||
if (!list) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return list.getElementsByTagName('div');
|
||||
};
|
||||
|
||||
|
|
@ -189,7 +189,7 @@ kpxcAutocomplete.keyPress = function(e) {
|
|||
|
||||
if (kpxcAutocomplete.index >= 0 && items && items[kpxcAutocomplete.index] !== undefined) {
|
||||
e.preventDefault();
|
||||
kpxcAutocomplete.input.value = e.currentTarget.value;
|
||||
kpxcAutocomplete.input.value = kpxcAutocomplete.elements[kpxcAutocomplete.index].value;
|
||||
kpxcAutocomplete.fillPassword(kpxcAutocomplete.input.value, kpxcAutocomplete.index);
|
||||
kpxcAutocomplete.closeList();
|
||||
}
|
||||
|
|
@ -212,15 +212,12 @@ kpxcAutocomplete.keyPress = function(e) {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcAutocomplete.fillPassword = async function(value, index) {
|
||||
const fieldId = kpxcAutocomplete.input.getAttribute('data-kpxc-id');
|
||||
kpxcFields.prepareId(fieldId);
|
||||
|
||||
const givenType = kpxcAutocomplete.input.type === 'password' ? 'password' : 'username';
|
||||
const combination = await kpxcFields.getCombination(givenType, fieldId);
|
||||
kpxcAutocomplete.fillPassword = async function(value, index, uuid) {
|
||||
const combination = await kpxcFields.getCombination(kpxcAutocomplete.input);
|
||||
combination.loginId = index;
|
||||
|
||||
kpxc.fillInCredentials(combination, givenType === 'password', false);
|
||||
const manualFill = await sendMessage('page_get_manual_fill');
|
||||
await kpxc.fillInCredentials(combination, value, uuid, manualFill === ManualFill.PASSWORD);
|
||||
kpxcAutocomplete.input.setAttribute('fetched', true);
|
||||
};
|
||||
|
||||
|
|
@ -232,12 +229,10 @@ kpxcAutocomplete.updatePosition = function(inputField, elem) {
|
|||
|
||||
const rect = inputField.getBoundingClientRect();
|
||||
div.style.minWidth = Pixels(inputField.offsetWidth);
|
||||
const bodyRect = document.body.getBoundingClientRect();
|
||||
const bodyStyle = getComputedStyle(document.body);
|
||||
|
||||
if (bodyStyle.position.toLowerCase() === 'relative') {
|
||||
div.style.top = Pixels(rect.top - bodyRect.top + document.scrollingElement.scrollTop + inputField.offsetHeight);
|
||||
div.style.left = Pixels(rect.left - bodyRect.left + document.scrollingElement.scrollLeft);
|
||||
if (kpxcUI.bodyStyle.position.toLowerCase() === 'relative') {
|
||||
div.style.top = Pixels(rect.top - kpxcUI.bodyRect.top + document.scrollingElement.scrollTop + inputField.offsetHeight);
|
||||
div.style.left = Pixels(rect.left - kpxcUI.bodyRect.left + document.scrollingElement.scrollLeft);
|
||||
} else {
|
||||
div.style.top = Pixels(rect.top + document.scrollingElement.scrollTop + inputField.offsetHeight);
|
||||
div.style.left = Pixels(rect.left + document.scrollingElement.scrollLeft);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ kpxcBanner.created = false;
|
|||
kpxcBanner.credentials = {};
|
||||
kpxcBanner.wrapper = undefined;
|
||||
|
||||
kpxcBanner.destroy = function() {
|
||||
kpxcBanner.destroy = async function() {
|
||||
kpxcBanner.created = false;
|
||||
kpxcBanner.credentials = {};
|
||||
|
||||
|
|
@ -17,9 +17,7 @@ kpxcBanner.destroy = function() {
|
|||
kpxcBanner.banner.removeChild(dialog);
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'remove_credentials_from_tab_information'
|
||||
});
|
||||
await sendMessage('remove_credentials_from_tab_information');
|
||||
|
||||
if (kpxcBanner.wrapper && window.parent.document.body.contains(kpxcBanner.wrapper)) {
|
||||
window.parent.document.body.removeChild(kpxcBanner.wrapper);
|
||||
|
|
@ -29,23 +27,20 @@ kpxcBanner.destroy = function() {
|
|||
};
|
||||
|
||||
kpxcBanner.create = async function(credentials = {}) {
|
||||
const connectedDatabase = await browser.runtime.sendMessage({
|
||||
action: 'get_connected_database'
|
||||
});
|
||||
|
||||
const connectedDatabase = await sendMessage('get_connected_database');
|
||||
if (!kpxc.settings.showLoginNotifications || kpxcBanner.created || connectedDatabase.identifier === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if database is closed
|
||||
const state = await browser.runtime.sendMessage({ action: 'check_database_hash' });
|
||||
const state = await sendMessage('check_database_hash');
|
||||
if (state === '') {
|
||||
//kpxcUI.createNotification('error', tr('rememberErrorDatabaseClosed'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show anything if the site is in the ignore
|
||||
if (kpxc.siteIgnored(IGNORE_NORMAL)) {
|
||||
if (await kpxc.siteIgnored(IGNORE_NORMAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -141,64 +136,48 @@ kpxcBanner.create = async function(credentials = {}) {
|
|||
};
|
||||
|
||||
kpxcBanner.saveNewCredentials = async function(credentials = {}) {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
action: 'get_database_groups'
|
||||
});
|
||||
const result = await sendMessage('get_database_groups');
|
||||
if (!result || !result.groups) {
|
||||
console.log('Error: Empty result from get_database_groups');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.defaultGroupAlwaysAsk) {
|
||||
if (result.defaultGroup === '' || result.defaultGroup === DEFAULT_BROWSER_GROUP) {
|
||||
// Default group is used
|
||||
const args = [ credentials.username, credentials.password, credentials.url ];
|
||||
const res = await sendMessage('add_credentials', args);
|
||||
kpxcBanner.verifyResult(res);
|
||||
return;
|
||||
} else {
|
||||
// A specified group is used
|
||||
let gname = '';
|
||||
let guuid = '';
|
||||
|
||||
if (!result.defaultGroupAlwaysAsk && (result.defaultGroup !== '' && result.defaultGroup !== DEFAULT_BROWSER_GROUP)) {
|
||||
// Another group name has been specified
|
||||
const [ gname, guuid ] = kpxcBanner.getDefaultGroup(result.groups[0].children, result.defaultGroup);
|
||||
if (gname === '' && guuid === '') {
|
||||
// Root group is used -> use the root path
|
||||
if (result.defaultGroup.toLowerCase() === 'root') {
|
||||
result.defaultGroup = '/';
|
||||
}
|
||||
|
||||
// Create a new group
|
||||
const newGroup = await browser.runtime.sendMessage({
|
||||
action: 'create_new_group',
|
||||
args: [ result.defaultGroup ]
|
||||
});
|
||||
|
||||
if (newGroup.name && newGroup.uuid) {
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url, newGroup.name, newGroup.uuid ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
gname = result.groups[0].name;
|
||||
guuid = result.groups[0].uuid;
|
||||
} else {
|
||||
kpxcUI.createNotification('error', tr('rememberErrorCreatingNewGroup'));
|
||||
[ gname, guuid ] = kpxcBanner.getDefaultGroup(result.groups[0].children, result.defaultGroup);
|
||||
if (gname === '' && guuid === '') {
|
||||
// Create a new group
|
||||
const newGroup = await sendMessage('create_new_group', [ result.defaultGroup ]);
|
||||
if (newGroup.name && newGroup.uuid) {
|
||||
const res = await sendMessage('add_credentials', [ credentials.username, credentials.password, credentials.url, newGroup.name, newGroup.uuid ]);
|
||||
kpxcBanner.verifyResult(res);
|
||||
} else {
|
||||
kpxcUI.createNotification('error', tr('rememberErrorCreatingNewGroup'));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const res = await sendMessage('add_credentials', [ credentials.username, credentials.password, credentials.url, gname, guuid ]);
|
||||
kpxcBanner.verifyResult(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url, gname, guuid ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
return;
|
||||
} else if ((result.groups === undefined || (result.groups.length > 0 && result.groups[0].children.length === 0))
|
||||
|| (!result.defaultGroupAlwaysAsk && (result.defaultGroup === '' || result.defaultGroup === DEFAULT_BROWSER_GROUP))) {
|
||||
// Only the Root group and no KeePassXC-Browser passwords -> save to default
|
||||
// Or when default group is not set and defaultGroupAskAlways is disabled -> save to default
|
||||
const args = [ credentials.username, credentials.password, credentials.url ];
|
||||
|
||||
// If root group is defined by the user, and there's no default browser group, save the credentials to the root group
|
||||
if (result.groups !== undefined
|
||||
&& result.groups.length > 0
|
||||
&& result.groups[0].children.length === 0
|
||||
&& (result.defaultGroup.toLowerCase() === 'root'
|
||||
|| result.defaultGroup === '/')) {
|
||||
args.push(result.groups[0].name, result.groups[0].uuid);
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: args
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const addChildren = function(group, parentElement, depth) {
|
||||
|
|
@ -227,11 +206,7 @@ kpxcBanner.saveNewCredentials = async function(credentials = {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url, group, groupUuid ]
|
||||
});
|
||||
|
||||
const res = await sendMessage('add_credentials', [ credentials.username, credentials.password, credentials.url, group, groupUuid ]);
|
||||
kpxcBanner.verifyResult(res);
|
||||
});
|
||||
|
||||
|
|
@ -264,10 +239,7 @@ kpxcBanner.updateCredentials = async function(credentials = {}) {
|
|||
credentials.username = credentials.list[0].login;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'update_credentials',
|
||||
args: [ credentials.list[0].uuid, credentials.username, credentials.password, credentials.url ]
|
||||
});
|
||||
const res = await sendMessage('update_credentials', [ credentials.list[0].uuid, credentials.username, credentials.password, credentials.url ]);
|
||||
kpxcBanner.verifyResult(res);
|
||||
} else {
|
||||
await kpxcBanner.createCredentialDialog();
|
||||
|
|
@ -310,10 +282,7 @@ kpxcBanner.updateCredentials = async function(credentials = {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'update_credentials',
|
||||
args: [ credentials.list[entryId].uuid, credentials.username, credentials.password, credentials.url ]
|
||||
});
|
||||
const res = await sendMessage('update_credentials', [ credentials.list[entryId].uuid, credentials.username, credentials.password, credentials.url ]);
|
||||
kpxcBanner.verifyResult(res);
|
||||
});
|
||||
});
|
||||
|
|
@ -369,9 +338,7 @@ kpxcBanner.createCredentialDialog = async function() {
|
|||
kpxcBanner.shadowSelector('#kpxc-banner-btn-update').hidden = true;
|
||||
kpxcBanner.shadowSelector('.kpxc-checkbox').disabled = true;
|
||||
|
||||
const connectedDatabase = await browser.runtime.sendMessage({
|
||||
action: 'get_connected_database'
|
||||
});
|
||||
const connectedDatabase = await sendMessage('get_connected_database');
|
||||
const databaseName = connectedDatabase.count > 0 ? connectedDatabase.identifier : '';
|
||||
|
||||
const dialog = kpxcUI.createElement('div', 'kpxc-banner-dialog');
|
||||
|
|
|
|||
|
|
@ -8,15 +8,18 @@ kpxcDefine.selection = {
|
|||
totp: null,
|
||||
fields: []
|
||||
};
|
||||
kpxcDefine.eventFieldClick = null;
|
||||
|
||||
kpxcDefine.dialog = null;
|
||||
kpxcDefine.startPosX = 0;
|
||||
kpxcDefine.startPosY = 0;
|
||||
kpxcDefine.diffX = 0;
|
||||
kpxcDefine.diffY = 0;
|
||||
kpxcDefine.eventFieldClick = null;
|
||||
kpxcDefine.inputQueryPattern = 'input[type=\'text\'], input[type=\'email\'], input[type=\'password\'], input[type=\'tel\'], input[type=\'number\'], input[type=\'username\'], input:not([type])';
|
||||
kpxcDefine.markedFields= [];
|
||||
kpxcDefine.keyDown = null;
|
||||
kpxcDefine.startPosX = 0;
|
||||
kpxcDefine.startPosY = 0;
|
||||
|
||||
kpxcDefine.init = function() {
|
||||
kpxcDefine.init = async function() {
|
||||
const backdrop = kpxcUI.createElement('div', 'kpxcDefine-modal-backdrop', { 'id': 'kpxcDefine-backdrop' });
|
||||
const chooser = kpxcUI.createElement('div', '', { 'id': 'kpxcDefine-fields' });
|
||||
const description = kpxcUI.createElement('div', '', { 'id': 'kpxcDefine-description' });
|
||||
|
|
@ -25,9 +28,6 @@ kpxcDefine.init = function() {
|
|||
document.body.append(backdrop);
|
||||
document.body.append(chooser);
|
||||
|
||||
kpxcFields.getAllFields();
|
||||
kpxcFields.prepareVisibleFieldsWithID('select');
|
||||
|
||||
kpxcDefine.initDescription();
|
||||
kpxcDefine.resetSelection();
|
||||
kpxcDefine.prepareStep1();
|
||||
|
|
@ -117,21 +117,24 @@ kpxcDefine.resetSelection = function() {
|
|||
fields: []
|
||||
};
|
||||
|
||||
kpxcDefine.markedFields = [];
|
||||
|
||||
const fields = $('#kpxcDefine-fields');
|
||||
if (fields) {
|
||||
fields.textContent = '';
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.isFieldSelected = function(kpxcId) {
|
||||
if (kpxcId) {
|
||||
kpxcDefine.isFieldSelected = function(field) {
|
||||
if (kpxcDefine.markedFields.some(f => f === field)) {
|
||||
return (
|
||||
kpxcId === kpxcDefine.selection.username
|
||||
|| kpxcId === kpxcDefine.selection.password
|
||||
|| kpxcId === kpxcDefine.selection.totp
|
||||
|| kpxcId in kpxcDefine.selection.fields
|
||||
(kpxcDefine.selection.username && kpxcDefine.selection.username.originalElement === field)
|
||||
|| (kpxcDefine.selection.password && kpxcDefine.selection.password.originalElement === field)
|
||||
|| (kpxcDefine.selection.totp && kpxcDefine.selection.totp.originalElement === field)
|
||||
|| kpxcDefine.selection.fields.includes(field)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
|
@ -142,35 +145,37 @@ kpxcDefine.markAllUsernameFields = function(chooser) {
|
|||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
kpxcDefine.selection.username = field.getAttribute('data-kpxc-id');
|
||||
field.classList.add('kpxcDefine-fixed-username-field');
|
||||
field.textContent = tr('username');
|
||||
field.onclick = null;
|
||||
kpxcDefine.selection.username = field;
|
||||
kpxcDefine.markedFields.push(field.originalElement);
|
||||
|
||||
kpxcDefine.prepareStep2();
|
||||
kpxcDefine.markAllPasswordFields('#kpxcDefine-fields');
|
||||
};
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern);
|
||||
|
||||
kpxcDefine.markFields(chooser, kpxcDefine.inputQueryPattern);
|
||||
};
|
||||
|
||||
kpxcDefine.markAllPasswordFields = function(chooser, more = false) {
|
||||
kpxcDefine.markAllPasswordFields = function(chooser) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
kpxcDefine.selection.password = field.getAttribute('data-kpxc-id');
|
||||
field.classList.add('kpxcDefine-fixed-password-field');
|
||||
field.textContent = tr('password');
|
||||
field.onclick = null;
|
||||
kpxcDefine.selection.password = field;
|
||||
kpxcDefine.markedFields.push(field.originalElement);
|
||||
|
||||
kpxcDefine.prepareStep3();
|
||||
kpxcDefine.markAllTOTPFields('#kpxcDefine-fields');
|
||||
};
|
||||
if (more) {
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern);
|
||||
} else {
|
||||
kpxcDefine.markFields(chooser, 'input[type=\'password\']');
|
||||
}
|
||||
|
||||
kpxcDefine.markFields(chooser, 'input[type=\'password\']');
|
||||
};
|
||||
|
||||
kpxcDefine.markAllStringFields = function(chooser) {
|
||||
|
|
@ -180,15 +185,19 @@ kpxcDefine.markAllStringFields = function(chooser) {
|
|||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
const value = field.getAttribute('data-kpxc-id');
|
||||
kpxcDefine.selection.fields[value] = true;
|
||||
if (kpxcDefine.isFieldSelected(field.originalElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
kpxcDefine.selection.fields.push(field.originalElement);
|
||||
kpxcDefine.markedFields.push(field.originalElement);
|
||||
|
||||
const count = Object.keys(kpxcDefine.selection.fields).length;
|
||||
field.classList.add('kpxcDefine-fixed-string-field');
|
||||
field.textContent = tr('defineStringField') + String(count);
|
||||
field.textContent = tr('defineStringField') + String(kpxcDefine.selection.fields.length);
|
||||
field.onclick = null;
|
||||
};
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern + ', select');
|
||||
|
||||
kpxcDefine.markFields(chooser, kpxcDefine.inputQueryPattern + ', select');
|
||||
};
|
||||
|
||||
kpxcDefine.markAllTOTPFields = function(chooser) {
|
||||
|
|
@ -198,14 +207,17 @@ kpxcDefine.markAllTOTPFields = function(chooser) {
|
|||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
kpxcDefine.selection.totp = field.getAttribute('data-kpxc-id');
|
||||
field.classList.add('kpxcDefine-fixed-totp-field');
|
||||
field.textContent = 'TOTP';
|
||||
field.onclick = null;
|
||||
kpxcDefine.selection.totp = field;
|
||||
kpxcDefine.markedFields.push(field.originalElement);
|
||||
|
||||
kpxcDefine.prepareStep4();
|
||||
kpxcDefine.markAllStringFields('#kpxcDefine-fields');
|
||||
};
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern);
|
||||
|
||||
kpxcDefine.markFields(chooser, kpxcDefine.inputQueryPattern);
|
||||
};
|
||||
|
||||
kpxcDefine.markFields = function(chooser, pattern) {
|
||||
|
|
@ -214,39 +226,49 @@ kpxcDefine.markFields = function(chooser, pattern) {
|
|||
const inputs = document.querySelectorAll(pattern);
|
||||
|
||||
for (const i of inputs) {
|
||||
if (kpxcDefine.isFieldSelected(i.getAttribute('data-kpxc-id'))) {
|
||||
if (kpxcDefine.isFieldSelected(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kpxcFields.isVisible(i)) {
|
||||
const field = kpxcUI.createElement('div', 'kpxcDefine-fixed-field', { 'data-kpxc-id': i.getAttribute('data-kpxc-id') });
|
||||
const rect = i.getBoundingClientRect();
|
||||
field.style.top = Pixels(rect.top);
|
||||
field.style.left = Pixels(rect.left);
|
||||
field.style.width = Pixels(rect.width);
|
||||
field.style.height = Pixels(rect.height);
|
||||
field.textContent = String(index);
|
||||
field.addEventListener('click', function(e) {
|
||||
kpxcDefine.eventFieldClick(e);
|
||||
});
|
||||
field.addEventListener('mouseenter', function() {
|
||||
field.classList.add('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
field.addEventListener('mouseleave', function() {
|
||||
field.classList.remove('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
i.addEventListener('focus', function() {
|
||||
field.classList.add('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
i.addEventListener('blur', function() {
|
||||
field.classList.remove('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
const elem = $(chooser);
|
||||
if (elem) {
|
||||
elem.append(field);
|
||||
firstInput = field;
|
||||
++index;
|
||||
}
|
||||
if (!kpxcFields.isVisible(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const field = kpxcUI.createElement('div', 'kpxcDefine-fixed-field');
|
||||
field.originalElement = i;
|
||||
|
||||
const rect = i.getBoundingClientRect();
|
||||
field.style.top = Pixels(rect.top);
|
||||
field.style.left = Pixels(rect.left);
|
||||
field.style.width = Pixels(rect.width);
|
||||
field.style.height = Pixels(rect.height);
|
||||
field.textContent = String(index);
|
||||
|
||||
field.addEventListener('click', function(e) {
|
||||
kpxcDefine.eventFieldClick(e);
|
||||
});
|
||||
|
||||
field.addEventListener('mouseenter', function() {
|
||||
field.classList.add('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
|
||||
field.addEventListener('mouseleave', function() {
|
||||
field.classList.remove('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
|
||||
i.addEventListener('focus', function() {
|
||||
field.classList.add('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
|
||||
i.addEventListener('blur', function() {
|
||||
field.classList.remove('kpxcDefine-fixed-hover-field');
|
||||
});
|
||||
|
||||
const elem = $(chooser);
|
||||
if (elem) {
|
||||
elem.append(field);
|
||||
firstInput = field;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,7 +320,7 @@ kpxcDefine.prepareStep4 = function() {
|
|||
$('#kpxcDefine-help').style.marginBottom = '10px';
|
||||
$('#kpxcDefine-help').textContent = tr('defineHelpText');
|
||||
|
||||
removeContent('div.kpxcDefine-fixed-field:not(.kpxcDefine-fixed-username-field):not(.kpxcDefine-fixed-password-field):not(.kpxcDefine-fixed-totp-field)');
|
||||
removeContent('div.kpxcDefine-fixed-field:not(.kpxcDefine-fixed-username-field):not(.kpxcDefine-fixed-password-field):not(.kpxcDefine-fixed-totp-field):not(.kpxcDefine-fixed-string-field)');
|
||||
$('#kpxcDefine-chooser-headline').textContent = tr('defineConfirmSelection');
|
||||
kpxcDefine.dataStep = 4;
|
||||
$('#kpxcDefine-btn-skip').style.display = 'none';
|
||||
|
|
@ -330,10 +352,17 @@ kpxcDefine.again = function() {
|
|||
};
|
||||
|
||||
kpxcDefine.more = function() {
|
||||
if (kpxcDefine.dataStep === 2) {
|
||||
if (kpxcDefine.dataStep === 1) {
|
||||
kpxcDefine.prepareStep1();
|
||||
} else if (kpxcDefine.dataStep === 2) {
|
||||
kpxcDefine.prepareStep2();
|
||||
kpxcDefine.markAllPasswordFields('#kpxcDefine-fields', true);
|
||||
} else if (kpxcDefine.dataStep === 3) {
|
||||
kpxcDefine.prepareStep3();
|
||||
} else if (kpxcDefine.dataStep === 4) {
|
||||
kpxcDefine.prepareStep4();
|
||||
}
|
||||
|
||||
kpxcDefine.markFields('#kpxcDefine-fields', kpxcDefine.inputQueryPattern + ', select');
|
||||
};
|
||||
|
||||
kpxcDefine.confirm = async function() {
|
||||
|
|
@ -346,21 +375,20 @@ kpxcDefine.confirm = async function() {
|
|||
}
|
||||
|
||||
if (kpxcDefine.selection.username) {
|
||||
kpxcDefine.selection.username = kpxcFields.prepareId(kpxcDefine.selection.username);
|
||||
kpxcDefine.selection.username = kpxcFields.getId(kpxcDefine.selection.username.originalElement);
|
||||
}
|
||||
|
||||
if (kpxcDefine.selection.password) {
|
||||
kpxcDefine.selection.password = kpxcFields.prepareId(kpxcDefine.selection.password);
|
||||
kpxcDefine.selection.password = kpxcFields.getId(kpxcDefine.selection.password.originalElement);
|
||||
}
|
||||
|
||||
if (kpxcDefine.selection.totp) {
|
||||
kpxcDefine.selection.totp = kpxcFields.prepareId(kpxcDefine.selection.totp);
|
||||
kpxcDefine.selection.totp = kpxcFields.getId(kpxcDefine.selection.totp.originalElement);
|
||||
}
|
||||
|
||||
const fieldIds = [];
|
||||
const fieldKeys = Object.keys(kpxcDefine.selection.fields);
|
||||
for (const i of fieldKeys) {
|
||||
fieldIds.push(kpxcFields.prepareId(i));
|
||||
for (const i of kpxcDefine.selection.fields) {
|
||||
fieldIds.push(kpxcFields.getId(i));
|
||||
}
|
||||
|
||||
const location = kpxc.getDocumentLocation();
|
||||
|
|
@ -371,11 +399,7 @@ kpxcDefine.confirm = async function() {
|
|||
fields: fieldIds
|
||||
};
|
||||
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'save_settings',
|
||||
args: [ kpxc.settings ]
|
||||
});
|
||||
|
||||
await sendMessage('save_settings', kpxc.settings);
|
||||
kpxcDefine.close();
|
||||
};
|
||||
|
||||
|
|
@ -387,19 +411,13 @@ kpxcDefine.discard = async function() {
|
|||
const location = kpxc.getDocumentLocation();
|
||||
delete kpxc.settings['defined-custom-fields'][location];
|
||||
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'save_settings',
|
||||
args: [ kpxc.settings ]
|
||||
});
|
||||
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'load_settings'
|
||||
});
|
||||
await sendMessage('save_settings', kpxc.settings);
|
||||
await sendMessage('load_settings');
|
||||
|
||||
$('div.alreadySelected').remove();
|
||||
};
|
||||
|
||||
// Handle the keyboard events
|
||||
// Handle keyboard events
|
||||
kpxcDefine.keyDown = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
|
|
@ -413,7 +431,7 @@ kpxcDefine.keyDown = function(e) {
|
|||
// Select input field by number
|
||||
e.preventDefault();
|
||||
const index = e.keyCode - 48;
|
||||
const inputFields = document.querySelectorAll('div.kpxcDefine-fixed-field:not(.kpxcDefine-fixed-username-field):not(.kpxcDefine-fixed-password-field)');
|
||||
const inputFields = document.querySelectorAll('div.kpxcDefine-fixed-field:not(.kpxcDefine-fixed-username-field):not(.kpxcDefine-fixed-password-field):not(.kpxcDefine-fixed-totp-field)');
|
||||
|
||||
if (inputFields.length >= index) {
|
||||
kpxcDefine.eventFieldClick(e, inputFields[index - 1]);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,8 +3,8 @@
|
|||
const kpxcPasswordIcons = {};
|
||||
kpxcPasswordIcons.icons = [];
|
||||
|
||||
kpxcPasswordIcons.newIcon = function(useIcons, field, inputs, pos, databaseState = DatabaseState.DISCONNECTED) {
|
||||
kpxcPasswordIcons.icons.push(new PasswordIcon(useIcons, field, inputs, pos, databaseState));
|
||||
kpxcPasswordIcons.newIcon = function(field, databaseState = DatabaseState.DISCONNECTED) {
|
||||
kpxcPasswordIcons.icons.push(new PasswordIcon(field, databaseState));
|
||||
};
|
||||
|
||||
kpxcPasswordIcons.switchIcon = function(state) {
|
||||
|
|
@ -15,57 +15,38 @@ kpxcPasswordIcons.deleteHiddenIcons = function() {
|
|||
kpxcUI.deleteHiddenIcons(kpxcPasswordIcons.icons, 'kpxc-password-field');
|
||||
};
|
||||
|
||||
kpxcPasswordIcons.isValid = function(field) {
|
||||
if (!field
|
||||
|| field.readOnly
|
||||
|| field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH
|
||||
|| kpxcIcons.hasIcon(field)
|
||||
|| !kpxcFields.isVisible(field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
class PasswordIcon extends Icon {
|
||||
constructor(useIcons, field, inputs, pos, databaseState) {
|
||||
constructor(field, databaseState = DatabaseState.DISCONNECTED) {
|
||||
super();
|
||||
this.useIcons = useIcons;
|
||||
this.databaseState = databaseState;
|
||||
this.nextFieldExists = false;
|
||||
|
||||
if (this.initField(field, inputs, pos)) {
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
this.initField(field);
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
}
|
||||
|
||||
PasswordIcon.prototype.initField = function(field, inputs, pos) {
|
||||
if (!field
|
||||
|| field.readOnly
|
||||
|| field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (field.getAttribute('kpxc-password-field')
|
||||
|| (field.hasAttribute('kpxc-defined') && field.getAttribute('kpxc-defined') !== 'password')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-password-field', true);
|
||||
|
||||
if (this.useIcons) {
|
||||
// Observer the visibility
|
||||
if (this.observer) {
|
||||
this.observer.observe(field);
|
||||
}
|
||||
this.createIcon(field);
|
||||
PasswordIcon.prototype.initField = function(field) {
|
||||
// Observer the visibility
|
||||
if (this.observer) {
|
||||
this.observer.observe(field);
|
||||
}
|
||||
|
||||
this.createIcon(field);
|
||||
this.inputField = field;
|
||||
|
||||
let found = false;
|
||||
if (inputs) {
|
||||
for (let i = pos + 1; i < inputs.length; i++) {
|
||||
if (inputs[i] && inputs[i].getLowerCaseAttribute('type') === 'password') {
|
||||
field.setAttribute('kpxc-pwgen-next-field-id', inputs[i].getAttribute('data-kpxc-id'));
|
||||
field.setAttribute('kpxc-pwgen-next-is-password-field', (i === 0));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-pwgen-next-field-exists', found);
|
||||
return true;
|
||||
};
|
||||
|
||||
PasswordIcon.prototype.createIcon = function(field) {
|
||||
|
|
@ -81,6 +62,7 @@ PasswordIcon.prototype.createIcon = function(field) {
|
|||
'offset': offset,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
|
||||
});
|
||||
|
||||
icon.style.zIndex = '10000000';
|
||||
icon.style.width = Pixels(size);
|
||||
icon.style.height = Pixels(size);
|
||||
|
|
@ -116,6 +98,8 @@ PasswordIcon.prototype.createIcon = function(field) {
|
|||
const kpxcPasswordDialog = {};
|
||||
kpxcPasswordDialog.created = false;
|
||||
kpxcPasswordDialog.icon = null;
|
||||
kpxcPasswordDialog.input = null;
|
||||
kpxcPasswordDialog.nextField = null;
|
||||
kpxcPasswordDialog.selected = null;
|
||||
kpxcPasswordDialog.startPosX = 0;
|
||||
kpxcPasswordDialog.startPosY = 0;
|
||||
|
|
@ -124,17 +108,6 @@ kpxcPasswordDialog.diffY = 0;
|
|||
kpxcPasswordDialog.dialog = null;
|
||||
kpxcPasswordDialog.titleBar = null;
|
||||
|
||||
kpxcPasswordDialog.removeIcon = function(field) {
|
||||
if (field.getAttribute('kpxc-password-field')) {
|
||||
const pwgenIcons = document.querySelectorAll('.kpxc-pwgen-icon');
|
||||
for (const i of pwgenIcons) {
|
||||
if (i.getAttribute('kpxc-pwgen-field-id') === field.getAttribute('data-kpxc-id')) {
|
||||
document.body.removeChild(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPasswordDialog.createDialog = function() {
|
||||
if (kpxcPasswordDialog.created) {
|
||||
// If database is open again, generate a new password right away
|
||||
|
|
@ -245,6 +218,15 @@ kpxcPasswordDialog.showDialog = function(field, icon) {
|
|||
return;
|
||||
}
|
||||
|
||||
kpxcPasswordDialog.input = field;
|
||||
|
||||
// Save next password field if found
|
||||
if (kpxc.inputs.length > 0) {
|
||||
const index = kpxc.inputs.indexOf(field);
|
||||
const nextField = kpxc.inputs[index+1];
|
||||
kpxcPasswordDialog.nextField = (nextField && nextField.getLowerCaseAttribute('type') === 'password') ? nextField : undefined;
|
||||
}
|
||||
|
||||
kpxcPasswordDialog.createDialog();
|
||||
initColorTheme(kpxcPasswordDialog.dialog);
|
||||
kpxcPasswordDialog.openDialog();
|
||||
|
|
@ -259,10 +241,6 @@ kpxcPasswordDialog.showDialog = function(field, icon) {
|
|||
kpxcPasswordDialog.dialog.style.top = Pixels(rect.top + rect.height);
|
||||
kpxcPasswordDialog.dialog.style.left = Pixels(rect.left);
|
||||
}
|
||||
|
||||
kpxcPasswordDialog.dialog.setAttribute('kpxc-pwgen-field-id', field.getAttribute('data-kpxc-id'));
|
||||
kpxcPasswordDialog.dialog.setAttribute('kpxc-pwgen-next-field-id', field.getAttribute('kpxc-pwgen-next-field-id'));
|
||||
kpxcPasswordDialog.dialog.setAttribute('kpxc-pwgen-next-is-password-field', field.getAttribute('kpxc-pwgen-next-is-password-field'));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -275,9 +253,7 @@ kpxcPasswordDialog.generate = async function(e) {
|
|||
e.preventDefault();
|
||||
}
|
||||
|
||||
callbackGeneratedPassword(await browser.runtime.sendMessage({
|
||||
action: 'generate_password'
|
||||
}));
|
||||
callbackGeneratedPassword(await sendMessage('generate_password'));
|
||||
};
|
||||
|
||||
kpxcPasswordDialog.copy = function(e) {
|
||||
|
|
@ -290,35 +266,27 @@ kpxcPasswordDialog.copy = function(e) {
|
|||
};
|
||||
|
||||
kpxcPasswordDialog.fill = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
if (!e.isTrusted || !kpxcPasswordDialog.input) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Use the active input field
|
||||
const field = _f(kpxcPasswordDialog.dialog.getAttribute('kpxc-pwgen-field-id'));
|
||||
if (field) {
|
||||
const password = kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input');
|
||||
if (field.getAttribute('maxlength')) {
|
||||
if (password.value.length > field.getAttribute('maxlength')) {
|
||||
const message = tr('passwordGeneratorErrorTooLong') + '\r\n'
|
||||
+ tr('passwordGeneratorErrorTooLongCut') + '\r\n' + tr('passwordGeneratorErrorTooLongRemember');
|
||||
message.style.whiteSpace = 'pre';
|
||||
browser.runtime.sendMessage({
|
||||
action: 'show_notification',
|
||||
args: [ message ]
|
||||
});
|
||||
return;
|
||||
}
|
||||
const password = kpxcPasswordDialog.shadowSelector('.kpxc-pwgen-input');
|
||||
if (kpxcPasswordDialog.input.getAttribute('maxlength')) {
|
||||
if (password.value.length > kpxcPasswordDialog.input.getAttribute('maxlength')) {
|
||||
const message = tr('passwordGeneratorErrorTooLong') + '\r\n'
|
||||
+ tr('passwordGeneratorErrorTooLongCut') + '\r\n' + tr('passwordGeneratorErrorTooLongRemember');
|
||||
message.style.whiteSpace = 'pre';
|
||||
sendMessage('show_notification' [ message ]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
field.value = password.value;
|
||||
const nextFieldId = field.getAttribute('kpxc-pwgen-next-field-id');
|
||||
const nextField = $('input[data-kpxc-id=\'' + nextFieldId + '\']');
|
||||
if (nextField) {
|
||||
nextField.value = password.value;
|
||||
}
|
||||
kpxcPasswordDialog.input.value = password.value;
|
||||
|
||||
if (kpxcPasswordDialog.nextField) {
|
||||
kpxcPasswordDialog.nextField.value = password.value;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,17 @@
|
|||
const ignoreRegex = /(zip|postal).*code/i;
|
||||
const ignoredTypes = [ 'email', 'password', 'username' ];
|
||||
|
||||
const acceptedOTPFields = [
|
||||
'2fa',
|
||||
'auth',
|
||||
'challenge',
|
||||
'code',
|
||||
'mfa',
|
||||
'otp',
|
||||
'token',
|
||||
'twofactor'
|
||||
];
|
||||
|
||||
var kpxcTOTPIcons = {};
|
||||
kpxcTOTPIcons.icons = [];
|
||||
|
||||
|
|
@ -18,23 +29,22 @@ kpxcTOTPIcons.deleteHiddenIcons = function() {
|
|||
kpxcUI.deleteHiddenIcons(kpxcTOTPIcons.icons, 'kpxc-totp-field');
|
||||
};
|
||||
|
||||
// Quick check for a valid TOTP field
|
||||
kpxcTOTPIcons.isAcceptedTOTPField = function(field) {
|
||||
const id = field.getLowerCaseAttribute('id');
|
||||
const name = field.getLowerCaseAttribute('name');
|
||||
const autocomplete = field.getLowerCaseAttribute('autocomplete');
|
||||
|
||||
class TOTPFieldIcon extends Icon {
|
||||
constructor(field, databaseState = DatabaseState.DISCONNECTED, forced = false) {
|
||||
super();
|
||||
this.icon = null;
|
||||
this.inputField = null;
|
||||
this.databaseState = databaseState;
|
||||
|
||||
if (this.initField(field, forced)) {
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
if (autocomplete === 'one-time-code' || acceptedOTPFields.some(f => (id && id.includes(f)) || (name && name.includes(f)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
TOTPFieldIcon.prototype.initField = function(field, forced) {
|
||||
if (!field) {
|
||||
return;
|
||||
return false;
|
||||
};
|
||||
|
||||
kpxcTOTPIcons.isValid = function(field, forced) {
|
||||
if (!field || !kpxcTOTPIcons.isAcceptedTOTPField(field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!forced) {
|
||||
|
|
@ -56,8 +66,22 @@ TOTPFieldIcon.prototype.initField = function(field, forced) {
|
|||
}
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-totp-field', 'true');
|
||||
return true;
|
||||
};
|
||||
|
||||
class TOTPFieldIcon extends Icon {
|
||||
constructor(field, databaseState = DatabaseState.DISCONNECTED, forced = false) {
|
||||
super();
|
||||
this.icon = null;
|
||||
this.inputField = null;
|
||||
this.databaseState = databaseState;
|
||||
|
||||
this.initField(field, forced);
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
}
|
||||
|
||||
TOTPFieldIcon.prototype.initField = function(field, forced) {
|
||||
// Observer the visibility
|
||||
if (this.observer) {
|
||||
this.observer.observe(field);
|
||||
|
|
@ -65,7 +89,6 @@ TOTPFieldIcon.prototype.initField = function(field, forced) {
|
|||
|
||||
this.createIcon(field);
|
||||
this.inputField = field;
|
||||
return false;
|
||||
};
|
||||
|
||||
TOTPFieldIcon.prototype.createIcon = function(field) {
|
||||
|
|
@ -97,7 +120,7 @@ TOTPFieldIcon.prototype.createIcon = function(field) {
|
|||
|
||||
e.preventDefault();
|
||||
await kpxc.receiveCredentialsIfNecessary();
|
||||
kpxc.fillInFromActiveElementTOTPOnly(field);
|
||||
kpxc.fillFromTOTP(field);
|
||||
});
|
||||
|
||||
kpxcUI.setIconPosition(icon, field);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
const MINIMUM_INPUT_FIELD_WIDTH = 60;
|
||||
|
||||
const DatabaseState = {
|
||||
DISCONNECTED: 0,
|
||||
LOCKED: 1,
|
||||
UNLOCKED: 2
|
||||
};
|
||||
|
||||
// jQuery style wrapper for querySelector()
|
||||
const $ = function(elem) {
|
||||
return document.querySelector(elem);
|
||||
|
|
@ -44,6 +50,8 @@ class Icon {
|
|||
}
|
||||
|
||||
const kpxcUI = {};
|
||||
kpxcUI.bodyRect = document.body.getBoundingClientRect();
|
||||
kpxcUI.bodyStyle = getComputedStyle(document.body);
|
||||
kpxcUI.mouseDown = false;
|
||||
|
||||
// Wrapper for creating elements
|
||||
|
|
@ -95,14 +103,12 @@ kpxcUI.calculateIconOffset = function(field, size) {
|
|||
|
||||
kpxcUI.setIconPosition = function(icon, field) {
|
||||
const rect = field.getBoundingClientRect();
|
||||
const bodyRect = document.body.getBoundingClientRect();
|
||||
const bodyStyle = getComputedStyle(document.body);
|
||||
const size = (document.dir !== 'rtl') ? Number(icon.getAttribute('size')) : 0;
|
||||
const offset = kpxcUI.calculateIconOffset(field, size);
|
||||
|
||||
if (bodyStyle.position.toLowerCase() === 'relative') {
|
||||
icon.style.top = Pixels(rect.top - bodyRect.top + document.scrollingElement.scrollTop + offset + 1);
|
||||
icon.style.left = Pixels(rect.left - bodyRect.left + document.scrollingElement.scrollLeft + field.offsetWidth - size - offset);
|
||||
if (kpxcUI.bodyStyle.position.toLowerCase() === 'relative') {
|
||||
icon.style.top = Pixels(rect.top - kpxcUI.bodyRect.top + document.scrollingElement.scrollTop + offset + 1);
|
||||
icon.style.left = Pixels(rect.left - kpxcUI.bodyRect.left + document.scrollingElement.scrollLeft + field.offsetWidth - size - offset);
|
||||
} else {
|
||||
icon.style.top = Pixels(rect.top + document.scrollingElement.scrollTop + offset + 1);
|
||||
icon.style.left = Pixels(rect.left + document.scrollingElement.scrollLeft + field.offsetWidth - size - offset);
|
||||
|
|
@ -110,11 +116,21 @@ kpxcUI.setIconPosition = function(icon, field) {
|
|||
};
|
||||
|
||||
kpxcUI.deleteHiddenIcons = function(iconList, attr) {
|
||||
const deletedIcons = [];
|
||||
for (const icon of iconList) {
|
||||
if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) {
|
||||
const index = iconList.indexOf(icon);
|
||||
icon.removeIcon(attr);
|
||||
iconList.splice(index, 1);
|
||||
deletedIcons.push(icon.inputField);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the same icons from kpxcIcons.icons array
|
||||
for (const input of deletedIcons) {
|
||||
const index = kpxcIcons.icons.findIndex(e => e.field === input);
|
||||
if (index >= 0) {
|
||||
kpxcIcons.icons.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -139,7 +155,7 @@ kpxcUI.updateFromIntersectionObserver = function(iconClass, entries) {
|
|||
// Wait for possible DOM animations
|
||||
setTimeout(() => {
|
||||
kpxcUI.setIconPosition(iconClass.icon, entry.target);
|
||||
}, 500);
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
const kpxcUsernameIcons = {};
|
||||
kpxcUsernameIcons.icons = [];
|
||||
kpxcUsernameIcons.detectedFields = [];
|
||||
|
||||
kpxcUsernameIcons.newIcon = function(field, databaseState = DatabaseState.DISCONNECTED) {
|
||||
kpxcUsernameIcons.icons.push(new UsernameFieldIcon(field, databaseState));
|
||||
|
|
@ -15,6 +16,18 @@ kpxcUsernameIcons.deleteHiddenIcons = function() {
|
|||
kpxcUI.deleteHiddenIcons(kpxcUsernameIcons.icons, 'kpxc-username-field');
|
||||
};
|
||||
|
||||
kpxcUsernameIcons.isValid = function(field) {
|
||||
if (!field
|
||||
|| field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH
|
||||
|| field.readOnly
|
||||
|| kpxcIcons.hasIcon(field)
|
||||
|| !kpxcFields.isVisible(field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
class UsernameFieldIcon extends Icon {
|
||||
constructor(field, databaseState = DatabaseState.DISCONNECTED) {
|
||||
|
|
@ -23,14 +36,15 @@ class UsernameFieldIcon extends Icon {
|
|||
this.icon = null;
|
||||
this.inputField = null;
|
||||
|
||||
if (this.initField(field)) {
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
this.initField(field);
|
||||
kpxcUI.monitorIconPosition(this);
|
||||
}
|
||||
|
||||
switchIcon(state) {
|
||||
if (!this.icon) {
|
||||
return;
|
||||
} else {
|
||||
this.observer.disconnect();
|
||||
}
|
||||
|
||||
this.icon.classList.remove('lock', 'lock-moz', 'unlock', 'unlock-moz', 'disconnected', 'disconnected-moz');
|
||||
|
|
@ -40,18 +54,6 @@ class UsernameFieldIcon extends Icon {
|
|||
}
|
||||
|
||||
UsernameFieldIcon.prototype.initField = function(field) {
|
||||
if (!field
|
||||
|| field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH
|
||||
|| field.readOnly
|
||||
|| field.getAttribute('kpxc-username-field') === 'true'
|
||||
|| field.getAttribute('kpxc-totp-field') === 'true'
|
||||
|| (field.hasAttribute('kpxc-defined') && field.getAttribute('kpxc-defined') !== 'username')
|
||||
|| !kpxcFields.isVisible(field)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-username-field', 'true');
|
||||
|
||||
// Observer the visibility
|
||||
if (this.observer) {
|
||||
this.observer.observe(field);
|
||||
|
|
@ -59,15 +61,9 @@ UsernameFieldIcon.prototype.initField = function(field) {
|
|||
|
||||
this.createIcon(field);
|
||||
this.inputField = field;
|
||||
return true;
|
||||
};
|
||||
|
||||
UsernameFieldIcon.prototype.createIcon = function(target) {
|
||||
// Remove any existing password generator icons from the input field
|
||||
if (target.getAttribute('kpxc-password-field')) {
|
||||
kpxcPasswordDialog.removeIcon(target);
|
||||
}
|
||||
|
||||
const field = target;
|
||||
const className = getIconClassName(this.databaseState);
|
||||
|
||||
|
|
@ -124,20 +120,18 @@ const iconClicked = async function(field, icon) {
|
|||
return;
|
||||
}
|
||||
|
||||
const connected = await browser.runtime.sendMessage({ action: 'is_connected' });
|
||||
const connected = await sendMessage('is_connected');
|
||||
if (!connected) {
|
||||
kpxcUI.createNotification('error', tr('errorNotConnected'));
|
||||
return;
|
||||
}
|
||||
|
||||
const databaseHash = await browser.runtime.sendMessage({ action: 'check_database_hash' });
|
||||
const databaseHash = await sendMessage('check_database_hash');
|
||||
if (databaseHash === '') {
|
||||
// Triggers database unlock
|
||||
_called.manualFillRequested = ManualFill.BOTH;
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'get_database_hash',
|
||||
args: [ false, true ] // Set triggerUnlock to true
|
||||
});
|
||||
await sendMessage('page_set_manual_fill', ManualFill.BOTH);
|
||||
await sendMessage('get_database_hash', [ false, true ]); // Set triggerUnlock to true
|
||||
field.focus();
|
||||
}
|
||||
|
||||
if (icon.className.includes('unlock')) {
|
||||
|
|
@ -165,11 +159,6 @@ const getIconText = function(state) {
|
|||
};
|
||||
|
||||
const fillCredentials = async function(field) {
|
||||
const fieldId = field.getAttribute('data-kpxc-id');
|
||||
kpxcFields.prepareId(fieldId);
|
||||
|
||||
const givenType = field.type === 'password' ? 'password' : 'username';
|
||||
const combination = await kpxcFields.getCombination(givenType, fieldId);
|
||||
|
||||
kpxc.fillInCredentials(combination, givenType === 'password', false);
|
||||
const combination = await kpxcFields.getCombination(field);
|
||||
kpxc.fillFromUsernameIcon(combination);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ const AssociatedAction = {
|
|||
CANCELED: 3
|
||||
};
|
||||
|
||||
const ManualFill = {
|
||||
NONE: 0,
|
||||
PASSWORD: 1,
|
||||
BOTH: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms a valid match pattern into a regular expression
|
||||
* which matches all URLs included by that pattern.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "KeePassXC-Browser",
|
||||
"version": "1.6.6",
|
||||
"version_name": "1.6.6",
|
||||
"version": "1.7.0",
|
||||
"version_name": "1.7.0",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"author": "KeePassXC Team",
|
||||
"icons": {
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ options.saveSetting = async function(name) {
|
|||
await browser.runtime.sendMessage({
|
||||
action: 'load_settings'
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
options.saveSettings = async function() {
|
||||
|
|
@ -66,8 +64,6 @@ options.saveKeyRing = async function() {
|
|||
await browser.runtime.sendMessage({
|
||||
action: 'load_keyring'
|
||||
});
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
options.initGeneralSettings = function() {
|
||||
|
|
@ -175,10 +171,8 @@ options.initGeneralSettings = function() {
|
|||
|
||||
$('#defaultGroupButton').click(async function() {
|
||||
const value = $('#defaultGroup').val();
|
||||
if (value.length > 0) {
|
||||
options.settings['defaultGroup'] = value;
|
||||
await options.saveSettings();
|
||||
}
|
||||
options.settings['defaultGroup'] = (value.length > 0 ? value : '');
|
||||
await options.saveSettings();
|
||||
});
|
||||
|
||||
$('#defaultGroupButtonReset').click(async function() {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ $(async () => {
|
|||
|
||||
$('#lock-database-button').click(async () => {
|
||||
statusResponse(await browser.runtime.sendMessage({
|
||||
action: 'lock-database'
|
||||
action: 'lock_database'
|
||||
}));
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ $(async () => {
|
|||
|
||||
$('#lock-database-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'lock-database'
|
||||
action: 'lock_database'
|
||||
}).then(statusResponse);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,18 @@ $(async () => {
|
|||
if (tabs.length === 0) {
|
||||
return; // For example: only the background devtools or a popup are opened
|
||||
}
|
||||
const tab = tabs[0];
|
||||
|
||||
const tab = tabs[0];
|
||||
const logins = global.page.tabs[tab.id].loginList;
|
||||
const ll = document.getElementById('login-list');
|
||||
|
||||
for (let i = 0; i < logins.length; i++) {
|
||||
const uuid = logins[i].uuid;
|
||||
const a = document.createElement('a');
|
||||
a.textContent = logins[i];
|
||||
a.textContent = logins[i].text;
|
||||
a.setAttribute('class', 'list-group-item');
|
||||
a.setAttribute('id', '' + i);
|
||||
|
||||
a.addEventListener('click', (e) => {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
|
|
@ -25,10 +28,13 @@ $(async () => {
|
|||
const id = e.target.id;
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'fill_user_pass_with_specific_login',
|
||||
id: Number(id)
|
||||
id: Number(id),
|
||||
uuid: uuid
|
||||
});
|
||||
|
||||
close();
|
||||
});
|
||||
|
||||
ll.appendChild(a);
|
||||
}
|
||||
|
||||
|
|
@ -50,12 +56,13 @@ $(async () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
filter.focus();
|
||||
}
|
||||
|
||||
$('#lock-database-button').click((e) => {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'lock-database'
|
||||
action: 'lock_database'
|
||||
});
|
||||
$('#credentialsList').hide();
|
||||
$('#database-not-opened').show();
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../css/colors.css" />
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="../fonts/fork-awesome.min.css" />
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../bootstrap/jquery-3.4.1.min.js"></script>
|
||||
<script src="../bootstrap/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="settings" class="settings">
|
||||
<button id="btn-options" class="btn btn-sm btn-success"><i class="fa fa-cog" aria-hidden="true"></i> <span data-i18n="popupSettingsText"></span></button>
|
||||
<button id="btn-choose-credential-fields" class="btn btn-sm btn-warning"><i class="fa fa-list-alt" aria-hidden="true"></i> <span data-i18n="popupChooseCredentialsText"></span></button>
|
||||
<button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase"><i class="fa fa-lock" aria-hidden="true"></i></button>
|
||||
|
||||
<div id="update-available" class="alert alert-danger">
|
||||
<span data-i18n="popupUpdateAvailable"></span>
|
||||
<br />
|
||||
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<span data-i18n="popupMultiplePasswordFields"></span>
|
||||
"<code><span data-i18n="contextMenuFillUsernameAndPassword"></span></code>", "<code><span data-i18n="contextMenuFillPassword"></span></code>"
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue