mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
commit
c852ba226d
35 changed files with 3060 additions and 2715 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,3 +9,4 @@
|
|||
.bz
|
||||
.bz2
|
||||
.xz
|
||||
node_modules/*
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
"description": "Checkbox text below the password generator input field."
|
||||
},
|
||||
"passwordGeneratorBits": {
|
||||
"message": "??? Bits",
|
||||
"message": "$1 Bits",
|
||||
"description": "Bits of the generated password."
|
||||
},
|
||||
"passwordGeneratorTitle": {
|
||||
|
|
@ -123,6 +123,10 @@
|
|||
"message": "Generate",
|
||||
"description": "Generate button text in password generator."
|
||||
},
|
||||
"passwordGeneratorTryAgain": {
|
||||
"message": "Try again",
|
||||
"description": "Generate button text in password generator when not connected."
|
||||
},
|
||||
"passwordGeneratorCopy": {
|
||||
"message": "Copy",
|
||||
"description": "Copy button text in password generator."
|
||||
|
|
@ -132,7 +136,15 @@
|
|||
"description": "Fill and copy button text in password generator."
|
||||
},
|
||||
"passwordGeneratorErrorTooLong": {
|
||||
"message": "Error:\nThe generated password is longer than the allowed length!\nIt has been cut to fit the length.\n\nPlease remember the new password!",
|
||||
"message": "Error: The generated password is longer than the allowed length!",
|
||||
"description": "A warning text shown in the password generator."
|
||||
},
|
||||
"passwordGeneratorErrorTooLongCut": {
|
||||
"message": "It has been cut to fit the length.",
|
||||
"description": "A warning text shown in the password generator."
|
||||
},
|
||||
"passwordGeneratorErrorTooLongRemember": {
|
||||
"message": "Please remember the new password!",
|
||||
"description": "A warning text shown in the password generator."
|
||||
},
|
||||
"passwordGeneratorGenerateText": {
|
||||
|
|
@ -140,7 +152,11 @@
|
|||
"description": "Password icon title text."
|
||||
},
|
||||
"passwordGeneratorError": {
|
||||
"message": "Cannot receive generated password.<br />Is KeePassXC running?",
|
||||
"message": "Cannot receive generated password.",
|
||||
"description": "Password generator error text when KeePassXC is closed."
|
||||
},
|
||||
"passwordGeneratorErrorIsRunning": {
|
||||
"message": "Is KeePassXC running and connected?",
|
||||
"description": "Password generator error text when KeePassXC is closed."
|
||||
},
|
||||
"defineDismiss": {
|
||||
|
|
@ -208,27 +224,31 @@
|
|||
"description": "Shown when no username is set in the credentials."
|
||||
},
|
||||
"credentialsNoLoginsFound": {
|
||||
"message": "Error:\nNo logins found.",
|
||||
"message": "Error: No logins found.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Error:\nMore than one login was found in KeePassXC!\nPress the KeePassXC-Browser icon for more options.",
|
||||
"message": "Error: More than one login was found in KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
},
|
||||
"credentialsMultipleFoundSecondLine": {
|
||||
"message": "Press the KeePassXC-Browser icon for more options.",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
},
|
||||
"credentialsNoUsernameFound": {
|
||||
"message": "Error:\nNo credentials for the given username found.",
|
||||
"message": "Error: No credentials for the given username found.",
|
||||
"description": "Alert message when no credentials are found for the given username."
|
||||
},
|
||||
"fieldsFill": {
|
||||
"message": "Error:\nCannot find fields to fill in.",
|
||||
"message": "Error: Cannot find fields to fill in.",
|
||||
"description": "Alert message when no fields are found to fill in."
|
||||
},
|
||||
"fieldsNoPasswordField": {
|
||||
"message": "Error:\nUnable to find a password field.",
|
||||
"message": "Error: Unable to find a password field.",
|
||||
"description": "Message shown when no password fields are found."
|
||||
},
|
||||
"rememberNothingChanged": {
|
||||
"message": "Error:\nCould not detect changed credentials.",
|
||||
"message": "Error: Could not detect changed credentials.",
|
||||
"description": "Message shown when trying to save credentials that haven't changed."
|
||||
},
|
||||
"popupTitle": {
|
||||
|
|
@ -323,12 +343,12 @@
|
|||
"message": "Please choose the group you want to add the new credentials.",
|
||||
"description": "A popup message shown choosing what group user wants to use for new credentials."
|
||||
},
|
||||
"popupRememberErrorDefaultGroupNotFound": {
|
||||
"message": "Error: Specified default group not found. Creating all necessary groups.",
|
||||
"description": "Error message shown when default group set cannot be found."
|
||||
"popupRememberInfoDefaultGroupNotFound": {
|
||||
"message": "Info: Default group created in connected database.",
|
||||
"description": "Info message shown when default group set cannot be found."
|
||||
},
|
||||
"popupRememberErrorCreatingNewGroup": {
|
||||
"message": "New group(s) cannot be created.",
|
||||
"message": "Error: New group(s) cannot be created.",
|
||||
"description": "Error message shown when new groups cannot be created."
|
||||
},
|
||||
"popupRememberErrorPasswordNotChanged": {
|
||||
|
|
@ -503,6 +523,10 @@
|
|||
"message": "Automatically retrieve credentials.",
|
||||
"description": "Automatically retrieve credentials checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutomaticReconnect": {
|
||||
"message": "Automatically reconnect to KeePassXC.",
|
||||
"description": "Auto-reconnect checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillSingleEntry": {
|
||||
"message": "Automatically fill in single-credential entries.",
|
||||
"description": "Automatically fill-in single credential entry checkbox text."
|
||||
|
|
@ -579,6 +603,10 @@
|
|||
"message": "KeePassXC-Browser will immediately retrieve credentials when a tab is activated.",
|
||||
"description": "Auto-Retrive Credentials option help text."
|
||||
},
|
||||
"optionsAutomaticReconnectHelpText": {
|
||||
"message": "Reconnects automatically to KeePassXC when it is detected.",
|
||||
"description": "Auto-reconnect option help text."
|
||||
},
|
||||
"optionsAutoFillSingleEntryHelpText": {
|
||||
"message": "Let KeePassXC-Browser automatically fill in credentials if it receives only a single entry.",
|
||||
"description": "Auto-Fill Single Entry option help text."
|
||||
|
|
@ -731,6 +759,10 @@
|
|||
"message": "Go to the page and click on the KeePassXC-Browser icon, then select ",
|
||||
"description": "Saved custom login fields info text, second part."
|
||||
},
|
||||
"optionsCustomFieldsTabHelpTextThird": {
|
||||
"message": "On this page you can manage saved custom login fields.",
|
||||
"description": "Saved custom login fields info text, third part."
|
||||
},
|
||||
"optionsCustomFieldsRemove": {
|
||||
"message": "Remove saved custom login fields?",
|
||||
"description": "Confirmation text when removing saved custom login fields."
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ browserAction.removeRememberPopup = function(callback, tab, removeImmediately) {
|
|||
const currentMS = Date.now();
|
||||
if (removeImmediately || (data.visibleForPageUpdates <= 0 && data.redirectOffset > 0)) {
|
||||
browserAction.stackPop(tab.id);
|
||||
browserAction.show(null, { 'id': tab.id });
|
||||
browserAction.show(null, { 'id': tab.id } );
|
||||
page.clearCredentials(tab.id);
|
||||
} else if (!isNaN(data.visibleForPageUpdates) && data.redirectOffset > 0 && currentMS >= data.redirectOffset) {
|
||||
data.visibleForPageUpdates -= 1;
|
||||
|
|
@ -205,7 +205,7 @@ browserAction.setRememberPopup = function(tabId, username, password, url, userna
|
|||
browser.storage.local.get({ 'settings': {} }).then(function(item) {
|
||||
const settings = item.settings;
|
||||
|
||||
// Don't show anything if the site is in the ignore
|
||||
// Don't show anything if the site is in the ignore
|
||||
if (settings.sitePreferences !== undefined) {
|
||||
for (const site of settings.sitePreferences) {
|
||||
if (site.ignore === IGNORE_NORMAL && (site.url === url || siteMatch(site.url, url))) {
|
||||
|
|
@ -233,7 +233,7 @@ browserAction.setRememberPopup = function(tabId, username, password, url, userna
|
|||
index: 0,
|
||||
counter: 0,
|
||||
max: 2,
|
||||
icons: [ 'icon_remember_red_background_19x19.png', 'icon_remember_red_lock_19x19.png' ]
|
||||
icons: ['icon_remember_red_background_19x19.png', 'icon_remember_red_lock_19x19.png']
|
||||
},
|
||||
icon: 'icon_remember_red_background_19x19.png',
|
||||
popup: 'popup_remember.html'
|
||||
|
|
@ -271,8 +271,8 @@ browserAction.setRememberPopup = function(tabId, username, password, url, userna
|
|||
'buttons': buttons
|
||||
});
|
||||
|
||||
browser.notifications.onButtonClicked.addListener((id, index) => {
|
||||
browser.notifications.clear(id);
|
||||
browser.notifications.onButtonClicked.addListener((notificationId, index) => {
|
||||
browser.notifications.clear(notificationId);
|
||||
if (index === 1) {
|
||||
browserAction.ignoreSite(url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ kpxcEvent.showStatus = function(configured, tab, callback) {
|
|||
keePassXCAvailable: keepass.isKeePassXCAvailable,
|
||||
encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized,
|
||||
associated: keepass.isAssociated(),
|
||||
error: errorMessage ? errorMessage : null
|
||||
error: errorMessage || null
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -192,16 +192,16 @@ kpxcEvent.onGetConnectedDatabase = function(callback, tab) {
|
|||
kpxcEvent.onGetKeePassXCVersions = function(callback, tab) {
|
||||
if (keepass.currentKeePassXC === '') {
|
||||
keepass.getDatabaseHash((res) => {
|
||||
callback({'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version});
|
||||
callback({ 'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version });
|
||||
}, tab);
|
||||
} else {
|
||||
callback({'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version});
|
||||
callback({ 'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version });
|
||||
}
|
||||
};
|
||||
|
||||
kpxcEvent.onCheckUpdateKeePassXC = function(callback, tab) {
|
||||
keepass.checkForNewKeePassXCVersion();
|
||||
callback({current: keepass.currentKeePassXC.version, latest: keepass.latestKeePassXC.version});
|
||||
callback({ current: keepass.currentKeePassXC.version, latest: keepass.latestKeePassXC.version });
|
||||
};
|
||||
|
||||
kpxcEvent.onUpdateAvailableKeePassXC = function(callback, tab) {
|
||||
|
|
@ -285,6 +285,8 @@ kpxcEvent.messageHandlers = {
|
|||
'associate': keepass.associate,
|
||||
'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC,
|
||||
'create_new_group': keepass.createNewGroup,
|
||||
'enable_automatic_reconnect': keepass.enableAutomaticReconnect,
|
||||
'disable_automatic_reconnect': keepass.disableAutomaticReconnect,
|
||||
'generate_password': keepass.generatePassword,
|
||||
'get_connected_database': kpxcEvent.onGetConnectedDatabase,
|
||||
'get_database_groups': keepass.getDatabaseGroups,
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ httpAuth.init = function() {
|
|||
browser.webRequest.onErrorOccurred.removeListener(httpAuth.requestCompleted);
|
||||
}
|
||||
|
||||
// only intercept http auth requests if the option is turned on.
|
||||
// Only intercept http auth requests if the option is turned on.
|
||||
if (page.settings.autoFillAndSend) {
|
||||
const opts = { urls: ['<all_urls>'] };
|
||||
const opts = { urls: [ '<all_urls>' ] };
|
||||
|
||||
browser.webRequest.onAuthRequired.addListener(handleReq, opts, [reqType]);
|
||||
browser.webRequest.onAuthRequired.addListener(handleReq, opts, [ reqType ]);
|
||||
browser.webRequest.onCompleted.addListener(httpAuth.requestCompleted, opts);
|
||||
browser.webRequest.onErrorOccurred.addListener(httpAuth.requestCompleted, opts);
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ httpAuth.processPendingCallbacks = async function(details, resolve, reject) {
|
|||
|
||||
httpAuth.loginOrShowCredentials = function(logins, details, resolve, reject) {
|
||||
// At least one login found --> use first to login
|
||||
if (logins.length > 0 && page.settings.autoFillAndSend) {
|
||||
if (logins.length > 0 && page.settings.autoFillAndSend) {
|
||||
if (logins.length === 1) {
|
||||
resolve({
|
||||
authCredentials: {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ keepass.migrateKeyRing().then(() => {
|
|||
keepass.connectToNative();
|
||||
keepass.generateNewKeyPair();
|
||||
keepass.changePublicKeys(null).then((pkRes) => {
|
||||
keepass.enableAutomaticReconnect();
|
||||
keepass.getDatabaseHash((gdRes) => {}, null);
|
||||
});
|
||||
});
|
||||
|
|
@ -85,11 +86,11 @@ const contextMenuItems = [
|
|||
{ title: tr('contextMenuFillPassword'), action: 'fill_password' },
|
||||
{ title: tr('contextMenuFillTOTP'), action: 'fill_totp' },
|
||||
{ title: tr('contextMenuShowPasswordGeneratorIcons'), action: 'activate_password_generator' },
|
||||
{ title: tr('contextMenuSaveCredentials'), action: 'remember_credentials' },
|
||||
{ title: tr('contextMenuShowPasswordGenerator'), action: 'show_password_generator' }
|
||||
{ title: tr('contextMenuShowPasswordGenerator'), action: 'show_password_generator' },
|
||||
{ title: tr('contextMenuSaveCredentials'), action: 'remember_credentials' }
|
||||
];
|
||||
|
||||
const menuContexts = ['editable'];
|
||||
const menuContexts = [ 'editable' ];
|
||||
|
||||
if (isFirefox()) {
|
||||
menuContexts.push('password');
|
||||
|
|
@ -103,8 +104,8 @@ for (const item of contextMenuItems) {
|
|||
onclick: (info, tab) => {
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: item.action
|
||||
}).catch((e) => {
|
||||
console.log(e);
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
const keepass = {};
|
||||
keepass.associated = {'value': false, 'hash': null};
|
||||
keepass.keyPair = {publicKey: null, secretKey: null};
|
||||
keepass.associated = { 'value': false, 'hash': null };
|
||||
keepass.keyPair = { publicKey: null, secretKey: null };
|
||||
keepass.serverPublicKey = '';
|
||||
keepass.clientID = '';
|
||||
keepass.isConnected = false;
|
||||
|
|
@ -22,6 +22,7 @@ keepass.keyId = 'keepassxc-browser-cryptokey-name';
|
|||
keepass.keyBody = 'keepassxc-browser-key';
|
||||
keepass.messageTimeout = 500; // Milliseconds
|
||||
keepass.nonce = nacl.util.encodeBase64(nacl.randomBytes(keepass.keySize));
|
||||
keepass.reconnectLoop = null;
|
||||
|
||||
const kpActions = {
|
||||
SET_LOGIN: 'set-login',
|
||||
|
|
@ -80,11 +81,9 @@ const kpErrors = {
|
|||
}
|
||||
};
|
||||
|
||||
browser.storage.local.get({
|
||||
'latestKeePassXC': { 'version': '', 'lastChecked': null },
|
||||
'keyRing': {}}).then((item) => {
|
||||
keepass.latestKeePassXC = item.latestKeePassXC;
|
||||
keepass.keyRing = item.keyRing;
|
||||
browser.storage.local.get({ 'latestKeePassXC': { 'version': '', 'lastChecked': null }, 'keyRing': {} }).then((item) => {
|
||||
keepass.latestKeePassXC = item.latestKeePassXC;
|
||||
keepass.keyRing = item.keyRing;
|
||||
});
|
||||
|
||||
keepass.sendNativeMessage = function(request, enableTimeout = false) {
|
||||
|
|
@ -134,7 +133,6 @@ keepass.addCredentials = function(callback, tab, username, password, url, group,
|
|||
};
|
||||
|
||||
keepass.updateCredentials = function(callback, tab, entryId, username, password, url, group, groupUuid) {
|
||||
page.debug('keepass.updateCredentials(callback, {1}, {2}, {3}, [password], {4})', tab.id, entryId, username, url);
|
||||
if (tab && page.tabs[tab.id]) {
|
||||
page.tabs[tab.id].errorMessage = null;
|
||||
}
|
||||
|
|
@ -199,8 +197,6 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
|
|||
};
|
||||
|
||||
keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallback, triggerUnlock = false, httpAuth = false) {
|
||||
page.debug('keepass.retrieveCredentials(callback, {1}, {2}, {3}, {4})', tab.id, url, submiturl, forceCallback);
|
||||
|
||||
keepass.testAssociation((response) => {
|
||||
if (!response) {
|
||||
browserAction.showDefault(null, tab);
|
||||
|
|
@ -220,12 +216,12 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
|
|||
}
|
||||
|
||||
let entries = [];
|
||||
let keys = [];
|
||||
const keys = [];
|
||||
const kpAction = kpActions.GET_LOGINS;
|
||||
const nonce = keepass.getNonce();
|
||||
const incrementedNonce = keepass.incrementedNonce(nonce);
|
||||
const { dbid } = keepass.getCryptoKey();
|
||||
|
||||
|
||||
for (const keyHash in keepass.keyRing) {
|
||||
keys.push({
|
||||
id: keepass.keyRing[keyHash].id,
|
||||
|
|
@ -279,7 +275,6 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
|
|||
} else {
|
||||
console.log('RetrieveCredentials for ' + url + ' rejected');
|
||||
}
|
||||
page.debug('keepass.retrieveCredentials() => entries.length = {1}', entries.length);
|
||||
} else if (response.error && response.errorCode) {
|
||||
keepass.handleError(tab, response.errorCode, response.error);
|
||||
callback([]);
|
||||
|
|
@ -291,7 +286,7 @@ keepass.retrieveCredentials = function(callback, tab, url, submiturl, forceCallb
|
|||
}, tab, false, triggerUnlock);
|
||||
};
|
||||
|
||||
keepass.generatePassword = function(callback, tab, forceCallback) {
|
||||
keepass.generatePassword = function(callback, tab) {
|
||||
if (!keepass.isConnected) {
|
||||
callback([]);
|
||||
return;
|
||||
|
|
@ -300,9 +295,7 @@ keepass.generatePassword = function(callback, tab, forceCallback) {
|
|||
keepass.testAssociation((taresponse) => {
|
||||
if (!taresponse) {
|
||||
browserAction.showDefault(null, tab);
|
||||
if (forceCallback) {
|
||||
callback([]);
|
||||
}
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +352,7 @@ keepass.associate = function(callback, tab) {
|
|||
return;
|
||||
}
|
||||
|
||||
keepass.getDatabaseHash((res) => {
|
||||
keepass.getDatabaseHash((hash) => {
|
||||
if (keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) {
|
||||
callback([]);
|
||||
return;
|
||||
|
|
@ -421,11 +414,22 @@ keepass.associate = function(callback, tab) {
|
|||
}, tab);
|
||||
};
|
||||
|
||||
keepass.testAssociation = function(callback, tab, enableTimeout = false, triggerUnlock = false) {
|
||||
keepass.testAssociation = async function(callback, tab, enableTimeout = false, triggerUnlock = false) {
|
||||
if (tab && page.tabs[tab.id]) {
|
||||
page.tabs[tab.id].errorMessage = null;
|
||||
}
|
||||
|
||||
if (!keepass.isKeePassXCAvailable && !page.settings.automaticReconnect) {
|
||||
try {
|
||||
keepass.connectToNative();
|
||||
await keepass.reconnect();
|
||||
} catch (err) {
|
||||
keepass.handleError(tab, kpErrors.PUBLIC_KEY_NOT_FOUND);
|
||||
callback(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
keepass.getDatabaseHash((dbHash) => {
|
||||
if (!dbHash) {
|
||||
callback(false);
|
||||
|
|
@ -622,8 +626,8 @@ keepass.changePublicKeys = function(tab, enableTimeout = false) {
|
|||
if (!keepass.verifyKeyResponse(response, key, incrementedNonce)) {
|
||||
if (tab && page.tabs[tab.id]) {
|
||||
keepass.handleError(tab, kpErrors.KEY_CHANGE_FAILED);
|
||||
reject(false);
|
||||
}
|
||||
reject(false);
|
||||
} else {
|
||||
keepass.isKeePassXCAvailable = true;
|
||||
console.log('Server public key: ' + nacl.util.encodeBase64(keepass.serverPublicKey));
|
||||
|
|
@ -846,10 +850,10 @@ keepass.migrateKeyRing = function() {
|
|||
// Change dates to numbers, for compatibilty with Chromium based browsers
|
||||
if (keyring) {
|
||||
let num = 0;
|
||||
for (let keyHash in keyring) {
|
||||
let key = keyring[keyHash];
|
||||
['created', 'lastUsed'].forEach((fld) => {
|
||||
let v = key[fld];
|
||||
for (const keyHash in keyring) {
|
||||
const key = keyring[keyHash];
|
||||
[ 'created', 'lastUsed' ].forEach((fld) => {
|
||||
const v = key[fld];
|
||||
if (v instanceof Date && v.valueOf() >= 0) {
|
||||
key[fld] = v.valueOf();
|
||||
num++;
|
||||
|
|
@ -1127,25 +1131,32 @@ keepass.decrypt = function(input, nonce) {
|
|||
};
|
||||
|
||||
keepass.enableAutomaticReconnect = function() {
|
||||
setInterval(() => {
|
||||
if (!keepass.isKeePassXCAvailable) {
|
||||
keepass.connectToNative();
|
||||
keepass.reconnect();
|
||||
}
|
||||
}, 1000);
|
||||
// Disable for Windows if KeePassXC is older than 2.3.4
|
||||
if (!page.settings.automaticReconnect ||
|
||||
(navigator.platform.toLowerCase().includes('win') && !keepass.compareVersion('2.3.4', keepass.currentKeePassXC))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (keepass.reconnectLoop === null) {
|
||||
keepass.reconnectLoop = setInterval(() => {
|
||||
if (!keepass.isKeePassXCAvailable) {
|
||||
keepass.connectToNative();
|
||||
keepass.reconnect().catch((e) => {});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
keepass.disableAutomaticReconnect = function() {
|
||||
clearInterval(keepass.reconnectLoop);
|
||||
keepass.reconnectLoop = null;
|
||||
};
|
||||
|
||||
keepass.reconnect = function(callback, tab) {
|
||||
return new Promise((resolve, reject) => {
|
||||
keepass.generateNewKeyPair();
|
||||
keepass.changePublicKeys(tab).then((pkRes) => {
|
||||
// Database hash should be received if the reconnection succeeded
|
||||
keepass.changePublicKeys(tab, true).then((pkRes) => {
|
||||
keepass.getDatabaseHash((gdRes) => {
|
||||
if (!gdRes) {
|
||||
reject(false);
|
||||
return;
|
||||
}
|
||||
|
||||
keepass.testAssociation((response) => {
|
||||
keepass.isConfigured().then((configured) => {
|
||||
resolve(configured);
|
||||
|
|
@ -1155,6 +1166,8 @@ keepass.reconnect = function(callback, tab) {
|
|||
});
|
||||
}, tab);
|
||||
}, null);
|
||||
}).catch((e) => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -1178,7 +1191,7 @@ keepass.updateDatabase = function() {
|
|||
if (tabs.length) {
|
||||
browser.tabs.sendMessage(tabs[0].id, {
|
||||
action: 'check_database_hash',
|
||||
hash: {old: keepass.previousDatabaseHash, new: keepass.databaseHash}
|
||||
hash: { old: keepass.previousDatabaseHash, new: keepass.databaseHash }
|
||||
});
|
||||
keepass.previousDatabaseHash = keepass.databaseHash;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const defaultSettings = {
|
|||
showNotifications: true,
|
||||
showLoginNotifications: true,
|
||||
saveDomainOnly: true,
|
||||
automaticReconnect: true,
|
||||
defaultGroup: '',
|
||||
defaultGroupAlwaysAsk: false
|
||||
};
|
||||
|
|
@ -51,6 +52,9 @@ page.initSettings = function() {
|
|||
if (!('saveDomainOnly' in page.settings)) {
|
||||
page.settings.saveDomainOnly = defaultSettings.saveDomainOnly;
|
||||
}
|
||||
if (!('automaticReconnect' in page.settings)) {
|
||||
page.settings.automaticReconnect = defaultSettings.automaticReconnect;
|
||||
}
|
||||
if (!('defaultGroup' in page.settings)) {
|
||||
page.settings.defaultGroup = defaultSettings.defaultGroup;
|
||||
}
|
||||
|
|
@ -71,13 +75,13 @@ page.initOpenedTabs = function() {
|
|||
}
|
||||
|
||||
// Set initial tab-ID
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((t) => {
|
||||
if (t.length === 0) {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((currentTabs) => {
|
||||
if (currentTabs.length === 0) {
|
||||
resolve();
|
||||
return; // For example: only the background devtools or a popup are opened
|
||||
}
|
||||
page.currentTabId = t[0].id;
|
||||
browserAction.show(null, t[0]);
|
||||
page.currentTabId = currentTabs[0].id;
|
||||
browserAction.show(null, currentTabs[0]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
|
@ -147,31 +151,3 @@ page.removePageInformationFromNotExistingTabs = function() {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
page.debugConsole = function() {
|
||||
if (arguments.length > 1) {
|
||||
console.log(page.sprintf(arguments[0], arguments));
|
||||
} else {
|
||||
console.log(arguments[0]);
|
||||
}
|
||||
};
|
||||
|
||||
page.sprintf = function(input, args) {
|
||||
return input.replace(/{(\d+)}/g, (match, number) => {
|
||||
return typeof args[number] !== 'undefined' ? (typeof args[number] === 'object' ? JSON.stringify(args[number]) : args[number]) : match;
|
||||
});
|
||||
};
|
||||
|
||||
page.debugDummy = function() {};
|
||||
|
||||
page.debug = page.debugDummy;
|
||||
|
||||
page.setDebug = function(bool) {
|
||||
if (bool) {
|
||||
page.debug = page.debugConsole;
|
||||
return 'Debug mode enabled';
|
||||
} else {
|
||||
page.debug = page.debugDummy;
|
||||
return 'Debug mode disabled';
|
||||
}
|
||||
};
|
||||
|
|
|
|||
188
keepassxc-browser/content/autocomplete.js
Normal file
188
keepassxc-browser/content/autocomplete.js
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
'use strict';
|
||||
|
||||
var kpxcAutocomplete = {};
|
||||
kpxcAutocomplete.elements = [];
|
||||
|
||||
kpxcAutocomplete.create = function(input, showListInstantly = false) {
|
||||
let _index = -1;
|
||||
|
||||
input.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.value !== '') {
|
||||
input.select();
|
||||
}
|
||||
showList(input);
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', keyPress);
|
||||
input.setAttribute('autocomplete', 'off');
|
||||
|
||||
if (showListInstantly) {
|
||||
showList(input);
|
||||
}
|
||||
|
||||
function showList(input) {
|
||||
closeList();
|
||||
const div = kpxcUI.createElement('div', 'kpxcAutocomplete-items', { 'id': 'kpxcAutocomplete-list' });
|
||||
|
||||
// Element position
|
||||
const rect = input.getBoundingClientRect();
|
||||
div.style.top = String((rect.top + document.body.scrollTop) + input.offsetHeight) + 'px';
|
||||
div.style.left = String((rect.left + document.body.scrollLeft)) + 'px';
|
||||
div.style.minWidth = String(input.offsetWidth) + 'px';
|
||||
div.style.zIndex = '2147483646';
|
||||
document.body.append(div);
|
||||
|
||||
for (const c of kpxcAutocomplete.elements) {
|
||||
const item = document.createElement('div');
|
||||
item.textContent += c.label;
|
||||
const itemInput = kpxcUI.createElement('input', '', { 'type': 'hidden', 'value': c.value });
|
||||
item.append(itemInput);
|
||||
item.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save index for combination.loginId
|
||||
const index = Array.prototype.indexOf.call(e.currentTarget.parentElement.childNodes, e.currentTarget);
|
||||
input.value = this.getElementsByTagName('input')[0].value;
|
||||
fillPassword(input.value, index);
|
||||
closeList();
|
||||
input.focus();
|
||||
});
|
||||
|
||||
// These events prevent the double hover effect if both keyboard and mouse are used
|
||||
item.addEventListener('mouseover', function(e) {
|
||||
removeItem(getAllItems());
|
||||
item.classList.add('kpxcAutocomplete-active');
|
||||
_index = Array.from(div.childNodes).indexOf(item);
|
||||
});
|
||||
item.addEventListener('mouseout', function(e) {
|
||||
item.classList.remove('kpxcAutocomplete-active');
|
||||
});
|
||||
|
||||
div.appendChild(item);
|
||||
}
|
||||
|
||||
// Activate the first item automatically
|
||||
const items = getAllItems();
|
||||
_index = 0;
|
||||
activateItem(items);
|
||||
};
|
||||
|
||||
function activateItem(item) {
|
||||
if (!item || item.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeItem(item);
|
||||
if (_index >= item.length) {
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
if (_index < 0) {
|
||||
_index = item.length - 1;
|
||||
}
|
||||
|
||||
if (item[_index] !== undefined) {
|
||||
item[_index].classList.add('kpxcAutocomplete-active');
|
||||
}
|
||||
};
|
||||
|
||||
function removeItem(items) {
|
||||
for (const item of items) {
|
||||
item.classList.remove('kpxcAutocomplete-active');
|
||||
}
|
||||
};
|
||||
|
||||
function closeList(elem) {
|
||||
const items = document.getElementsByClassName('kpxcAutocomplete-items');
|
||||
for (const item of items) {
|
||||
if (elem !== item && input) {
|
||||
item.parentNode.removeChild(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getAllItems() {
|
||||
const list = document.getElementById('kpxcAutocomplete-list');
|
||||
if (!list) {
|
||||
return [];
|
||||
}
|
||||
return list.getElementsByTagName('div');
|
||||
};
|
||||
|
||||
/**
|
||||
* Keyboard shortcuts for autocomplete menu:
|
||||
* - ArrowDown shows the list or selects item below, or the first item (last is active)
|
||||
* - ArrowUp selects item above, or the last item (first is active)
|
||||
* - Enter or Tab selects the item
|
||||
* - Backspace and Delete shows the list if input field is empty. First item is activated
|
||||
*/
|
||||
function keyPress(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item = getAllItems();
|
||||
if (e.key === 'ArrowDown') {
|
||||
// If the list is now shown, show it
|
||||
if (item.length === 0) {
|
||||
_index = -1;
|
||||
showList(input);
|
||||
} else {
|
||||
// Activate next item
|
||||
++_index;
|
||||
activateItem(item);
|
||||
}
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
--_index;
|
||||
activateItem(item);
|
||||
} else if (e.key === 'Enter' || e.key === 'Tab') {
|
||||
if (input.value === '' && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (_index >= 0 && item && item[_index] !== undefined) {
|
||||
input.value = e.currentTarget.value;
|
||||
fillPassword(input.value, _index);
|
||||
closeList();
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
closeList();
|
||||
} else if ((e.key === 'Backspace' || e.key === 'Delete') && input.value === '') {
|
||||
// Show menu when input field has no value and backspace is pressed
|
||||
_index = -1;
|
||||
showList(input);
|
||||
}
|
||||
};
|
||||
|
||||
function fillPassword(value, index) {
|
||||
const fieldId = input.getAttribute('data-kpxc-id');
|
||||
kpxcFields.prepareId(fieldId);
|
||||
const combination = kpxcFields.getCombination('username', fieldId);
|
||||
combination.loginId = index;
|
||||
|
||||
kpxc.fillInCredentials(combination, false, false);
|
||||
input.setAttribute('fetched', true);
|
||||
};
|
||||
|
||||
// Detect click outside autocomplete
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const list = document.getElementById('kpxcAutocomplete-list');
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.target !== input && (e.target.nodeName !== input.nodeName)) {
|
||||
closeList(e.target);
|
||||
}
|
||||
});
|
||||
};
|
||||
382
keepassxc-browser/content/define.js
Normal file
382
keepassxc-browser/content/define.js
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
'use strict';
|
||||
|
||||
var kpxcDefine = {};
|
||||
|
||||
kpxcDefine.selection = {
|
||||
username: null,
|
||||
password: null,
|
||||
fields: []
|
||||
};
|
||||
kpxcDefine.eventFieldClick = null;
|
||||
kpxcDefine.dialog = null;
|
||||
kpxcDefine.startPosX = 0;
|
||||
kpxcDefine.startPosY = 0;
|
||||
kpxcDefine.diffX = 0;
|
||||
kpxcDefine.diffY = 0;
|
||||
kpxcDefine.keyDown = null;
|
||||
|
||||
kpxcDefine.init = 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' });
|
||||
|
||||
backdrop.append(description);
|
||||
document.body.append(backdrop);
|
||||
document.body.append(chooser);
|
||||
|
||||
kpxcFields.getAllFields();
|
||||
kpxcFields.prepareVisibleFieldsWithID('select');
|
||||
|
||||
kpxcDefine.initDescription();
|
||||
kpxcDefine.resetSelection();
|
||||
kpxcDefine.prepareStep1();
|
||||
kpxcDefine.markAllUsernameFields('#kpxcDefine-fields');
|
||||
|
||||
kpxcDefine.dialog = $('#kpxcDefine-description');;
|
||||
kpxcDefine.dialog.onmousedown = function(e) {
|
||||
kpxcDefine.mouseDown(e);
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', kpxcDefine.keyDown);
|
||||
};
|
||||
|
||||
kpxcDefine.close = function() {
|
||||
$('#kpxcDefine-backdrop').remove();
|
||||
$('#kpxcDefine-fields').remove();
|
||||
document.removeEventListener('keydown', kpxcDefine.keyDown);
|
||||
};
|
||||
|
||||
kpxcDefine.mouseDown = function(e) {
|
||||
kpxcDefine.selected = kpxcDefine.dialog;
|
||||
kpxcDefine.startPosX = e.clientX;
|
||||
kpxcDefine.startPosY = e.clientY;
|
||||
kpxcDefine.diffX = kpxcDefine.startPosX - kpxcDefine.dialog.offsetLeft;
|
||||
kpxcDefine.diffY = kpxcDefine.startPosY - kpxcDefine.dialog.offsetTop;
|
||||
return false;
|
||||
};
|
||||
|
||||
kpxcDefine.initDescription = function() {
|
||||
const description = $('#kpxcDefine-description');
|
||||
const h1 = kpxcUI.createElement('div', '', { 'id': 'kpxcDefine-chooser-headline' });
|
||||
const help = kpxcUI.createElement('div', 'kpxcDefine-chooser-help', { 'id': 'kpxcDefine-help' });
|
||||
|
||||
// Show keyboard shortcuts help text
|
||||
const keyboardHelp = kpxcUI.createElement('div', 'kpxcDefine-keyboardHelp', {}, `${tr('optionsKeyboardShortcutsHeader')}:`);
|
||||
keyboardHelp.style.marginBottom = '5px';
|
||||
keyboardHelp.appendMultiple(document.createElement('br'), kpxcUI.createElement('kbd', '', {}, 'Escape'), ' ' + tr('defineDismiss'));
|
||||
keyboardHelp.appendMultiple(document.createElement('br'), kpxcUI.createElement('kbd', '', {}, 'S'), ' ' + tr('defineSkip'));
|
||||
keyboardHelp.appendMultiple(document.createElement('br'), kpxcUI.createElement('kbd', '', {}, 'A'), ' ' + tr('defineAgain'));
|
||||
keyboardHelp.appendMultiple(document.createElement('br'), kpxcUI.createElement('kbd', '', {}, 'C'), ' ' + tr('defineConfirm'));
|
||||
keyboardHelp.appendMultiple(document.createElement('br'), kpxcUI.createElement('kbd', '', {}, 'M'), ' ' + tr('defineMore'));
|
||||
keyboardHelp.appendMultiple(document.createElement('br'), kpxcUI.createElement('kbd', '', {}, 'D'), ' ' + tr('defineDiscard'));
|
||||
|
||||
description.appendMultiple(h1, help, keyboardHelp);
|
||||
|
||||
const buttonDismiss = kpxcUI.createElement('button', 'kpxc-button kpxc-red-button', { 'id': 'kpxcDefine-btn-dismiss' }, tr('defineDismiss'));
|
||||
buttonDismiss.addEventListener('click', kpxcDefine.close);
|
||||
|
||||
const buttonSkip = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxcDefine-btn-skip' }, tr('defineSkip'));
|
||||
buttonSkip.style.marginRight = '5px';
|
||||
buttonSkip.addEventListener('click', kpxcDefine.skip);
|
||||
|
||||
const buttonMore = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxcDefine-btn-more' }, tr('defineMore'));
|
||||
buttonMore.style.marginRight = '5px';
|
||||
buttonMore.style.marginLeft = '5px';
|
||||
buttonMore.addEventListener('click', kpxcDefine.more);
|
||||
|
||||
const buttonAgain = kpxcUI.createElement('button', 'kpxc-button kpxc-blue-button', { 'id': 'kpxcDefine-btn-again' }, tr('defineAgain'));
|
||||
buttonAgain.style.marginRight = '5px';
|
||||
buttonAgain.addEventListener('click', kpxcDefine.again);
|
||||
|
||||
const buttonConfirm = kpxcUI.createElement('button', 'kpxc-button kpxc-green-button', { 'id': 'kpxcDefine-btn-confirm' }, tr('defineConfirm'));
|
||||
buttonConfirm.style.marginRight = '15px';
|
||||
buttonConfirm.style.display = 'none';
|
||||
buttonConfirm.addEventListener('click', kpxcDefine.confirm);
|
||||
|
||||
description.appendMultiple(buttonConfirm, buttonSkip, buttonMore, buttonAgain, buttonDismiss);
|
||||
|
||||
const location = kpxc.getDocumentLocation();
|
||||
if (kpxc.settings['defined-custom-fields'] && kpxc.settings['defined-custom-fields'][location]) {
|
||||
const div = kpxcUI.createElement('div', 'alreadySelected', {});
|
||||
const defineDiscard = kpxcUI.createElement('p', '', {}, tr('defineAlreadySelected'));
|
||||
const buttonDiscard = kpxcUI.createElement('button', 'kpxc-button kpxc-red-button', { 'id': 'kpxcDefine-btn-discard' }, tr('defineDiscard'));
|
||||
buttonDiscard.style.marginTop = '5px';
|
||||
buttonDiscard.addEventListener('click', kpxcDefine.discard);
|
||||
|
||||
div.appendMultiple(defineDiscard, buttonDiscard);
|
||||
description.append(div);
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.resetSelection = function() {
|
||||
kpxcDefine.selection = {
|
||||
username: null,
|
||||
password: null,
|
||||
fields: []
|
||||
};
|
||||
|
||||
const fields = $('#kpxcDefine-fields');
|
||||
if (fields) {
|
||||
fields.textContent = '';
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.isFieldSelected = function(kpxcId) {
|
||||
if (kpxcId) {
|
||||
return (
|
||||
kpxcId === kpxcDefine.selection.username ||
|
||||
kpxcId === kpxcDefine.selection.password ||
|
||||
kpxcId in kpxcDefine.selection.fields
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
kpxcDefine.markAllUsernameFields = function(chooser) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
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.prepareStep2();
|
||||
kpxcDefine.markAllPasswordFields('#kpxcDefine-fields');
|
||||
};
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern);
|
||||
};
|
||||
|
||||
kpxcDefine.markAllPasswordFields = function(chooser, more) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
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.prepareStep3();
|
||||
kpxcDefine.markAllStringFields('#kpxcDefine-fields');
|
||||
};
|
||||
if (more) {
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern);
|
||||
} else {
|
||||
kpxcDefine.markFields(chooser, 'input[type=\'password\']');
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.markAllStringFields = function(chooser) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
const field = elem || e.currentTarget;
|
||||
const value = field.getAttribute('data-kpxc-id');
|
||||
kpxcDefine.selection.fields[value] = true;
|
||||
|
||||
const count = Object.keys(kpxcDefine.selection.fields).length;
|
||||
field.classList.add('kpxcDefine-fixed-string-field');
|
||||
field.textContent = tr('defineStringField') + String(count);
|
||||
field.onclick = null;
|
||||
};
|
||||
kpxcDefine.markFields(chooser, kpxcFields.inputQueryPattern + ', select');
|
||||
};
|
||||
|
||||
kpxcDefine.markFields = function(chooser, pattern) {
|
||||
let index = 1;
|
||||
let firstInput = null;
|
||||
const inputs = document.querySelectorAll(pattern);
|
||||
|
||||
for (const i of inputs) {
|
||||
if (kpxcDefine.isFieldSelected(i.getAttribute('data-kpxc-id'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = rect.top + 'px';
|
||||
field.style.left = rect.left + 'px';
|
||||
field.style.width = rect.width + 'px';
|
||||
field.style.height = rect.height + 'px';
|
||||
field.textContent = String(index);
|
||||
field.onclick = function(e) {
|
||||
kpxcDefine.eventFieldClick(e);
|
||||
};
|
||||
field.onmouseenter = function() {
|
||||
field.classList.add('kpxcDefine-fixed-hover-field');
|
||||
};
|
||||
field.onmouseleave = function() {
|
||||
field.classList.remove('kpxcDefine-fixed-hover-field');
|
||||
};
|
||||
i.onfocus = function() {
|
||||
field.classList.add('kpxcDefine-fixed-hover-field');
|
||||
}
|
||||
i.onblur = function() {
|
||||
field.classList.remove('kpxcDefine-fixed-hover-field');
|
||||
};
|
||||
const elem = $(chooser);
|
||||
if (elem) {
|
||||
elem.append(field);
|
||||
firstInput = field;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstInput) {
|
||||
firstInput.focus();
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.prepareStep1 = function() {
|
||||
const help = $('#kpxcDefine-help');
|
||||
help.style.marginBottom = '10px';
|
||||
help.textContent = tr('defineKeyboardText');
|
||||
|
||||
removeContent('div#kpxcDefine-fixed-field');
|
||||
$('#kpxcDefine-chooser-headline').textContent = tr('defineChooseUsername');
|
||||
kpxcDefine.dataStep = 1;
|
||||
$('#kpxcDefine-btn-skip').style.display = 'inline-block';
|
||||
$('#kpxcDefine-btn-confirm').style.display = 'none';
|
||||
$('#kpxcDefine-btn-again').style.display = 'none';
|
||||
$('#kpxcDefine-btn-more').style.display = 'none';
|
||||
};
|
||||
|
||||
kpxcDefine.prepareStep2 = function() {
|
||||
const help = $('#kpxcDefine-help');
|
||||
help.style.marginBottom = '10px';
|
||||
help.textContent = tr('defineKeyboardText');
|
||||
|
||||
removeContent('div.kpxcDefine-fixed-field:not(.kpxcDefine-fixed-username-field)');
|
||||
$('#kpxcDefine-chooser-headline').textContent = tr('defineChoosePassword');
|
||||
kpxcDefine.dataStep = 2;
|
||||
$('#kpxcDefine-btn-again').style.display = 'inline-block';
|
||||
$('#kpxcDefine-btn-more').style.display = 'inline-block';
|
||||
};
|
||||
|
||||
kpxcDefine.prepareStep3 = 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)');
|
||||
$('#kpxcDefine-chooser-headline').textContent = tr('defineConfirmSelection');
|
||||
kpxcDefine.dataStep = 3;
|
||||
$('#kpxcDefine-btn-skip').style.display = 'none';
|
||||
$('#kpxcDefine-btn-more').style.display = 'none';
|
||||
$('#kpxcDefine-btn-again').style.display = 'inline-block';
|
||||
$('#kpxcDefine-btn-confirm').style.display = 'inline-block';
|
||||
};
|
||||
|
||||
kpxcDefine.skip = function() {
|
||||
if (kpxcDefine.dataStep === 1) {
|
||||
kpxcDefine.selection.username = null;
|
||||
kpxcDefine.prepareStep2();
|
||||
kpxcDefine.markAllPasswordFields('#kpxcDefine-fields');
|
||||
} else if (kpxcDefine.dataStep === 2) {
|
||||
kpxcDefine.selection.password = null;
|
||||
kpxcDefine.prepareStep3();
|
||||
kpxcDefine.markAllStringFields('#kpxcDefine-fields');
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.again = function() {
|
||||
kpxcDefine.resetSelection();
|
||||
kpxcDefine.prepareStep1();
|
||||
kpxcDefine.markAllUsernameFields('#kpxcDefine-fields');
|
||||
};
|
||||
|
||||
kpxcDefine.more = function() {
|
||||
if (kpxcDefine.dataStep === 2) {
|
||||
kpxcDefine.prepareStep2();
|
||||
kpxcDefine.markAllPasswordFields('#kpxcDefine-fields', true);
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.confirm = function() {
|
||||
if (kpxcDefine.dataStep !== 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!kpxc.settings['defined-custom-fields']) {
|
||||
kpxc.settings['defined-custom-fields'] = {};
|
||||
}
|
||||
|
||||
if (kpxcDefine.selection.username) {
|
||||
kpxcDefine.selection.username = kpxcFields.prepareId(kpxcDefine.selection.username);
|
||||
}
|
||||
|
||||
if (kpxcDefine.selection.password) {
|
||||
kpxcDefine.selection.password = kpxcFields.prepareId(kpxcDefine.selection.password);
|
||||
}
|
||||
|
||||
const fieldIds = [];
|
||||
const fieldKeys = Object.keys(kpxcDefine.selection.fields);
|
||||
for (const i of fieldKeys) {
|
||||
fieldIds.push(kpxcFields.prepareId(i));
|
||||
}
|
||||
|
||||
const location = kpxc.getDocumentLocation();
|
||||
kpxc.settings['defined-custom-fields'][location] = {
|
||||
username: kpxcDefine.selection.username,
|
||||
password: kpxcDefine.selection.password,
|
||||
fields: fieldIds
|
||||
};
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'save_settings',
|
||||
args: [ kpxc.settings ]
|
||||
});
|
||||
|
||||
kpxcDefine.close();
|
||||
};
|
||||
|
||||
kpxcDefine.discard = function() {
|
||||
const location = kpxc.getDocumentLocation();
|
||||
delete kpxc.settings['defined-custom-fields'][location];
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'save_settings',
|
||||
args: [ kpxc.settings ]
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'load_settings'
|
||||
});
|
||||
|
||||
$('div.alreadySelected').remove();
|
||||
};
|
||||
|
||||
// Handle the keyboard events
|
||||
kpxcDefine.keyDown = function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
kpxcDefine.close();
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode >= 49 && e.keyCode <= 57) {
|
||||
// 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)');
|
||||
|
||||
if (inputFields.length >= index) {
|
||||
kpxcDefine.eventFieldClick(e, inputFields[index - 1]);
|
||||
}
|
||||
} else if (e.key === 's') {
|
||||
e.preventDefault();
|
||||
kpxcDefine.skip();
|
||||
} else if (e.key === 'a') {
|
||||
e.preventDefault();
|
||||
kpxcDefine.again();
|
||||
} else if (e.key === 'c') {
|
||||
e.preventDefault();
|
||||
kpxcDefine.confirm();
|
||||
} else if (e.key === 'm') {
|
||||
e.preventDefault();
|
||||
kpxcDefine.more();
|
||||
} else if (e.key === 'd') {
|
||||
e.preventDefault();
|
||||
kpxcDefine.discard();
|
||||
}
|
||||
};
|
||||
|
||||
const removeContent = function(pattern) {
|
||||
const elems = document.querySelectorAll(pattern);
|
||||
for (const e of elems) {
|
||||
e.remove();
|
||||
}
|
||||
};
|
||||
1496
keepassxc-browser/content/keepassxc-browser.js
Executable file
1496
keepassxc-browser/content/keepassxc-browser.js
Executable file
File diff suppressed because it is too large
Load diff
407
keepassxc-browser/content/pwgen.js
Normal file
407
keepassxc-browser/content/pwgen.js
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
'use strict';
|
||||
|
||||
var kpxcPassword = {};
|
||||
kpxcPassword.created = false;
|
||||
kpxcPassword.icon = null;
|
||||
kpxcPassword.inputField = null;
|
||||
kpxcPassword.selected = null;
|
||||
kpxcPassword.startPosX = 0;
|
||||
kpxcPassword.startPosY = 0;
|
||||
kpxcPassword.diffX = 0;
|
||||
kpxcPassword.diffY = 0;
|
||||
kpxcPassword.dialog = null;
|
||||
kpxcPassword.titleBar = null;
|
||||
|
||||
const DOMRectToArray = function(domRect) {
|
||||
return [ domRect.bottom, domRect.height, domRect.left, domRect.right, domRect.top, domRect.width, domRect.x, domRect.y ];
|
||||
};
|
||||
|
||||
/**
|
||||
* Detects if the input field appears or disappears -> show/hide the icon
|
||||
* - boundingClientRect with slightly (< -10) negative values -> hidden
|
||||
* - intersectionRatio === 0 -> hidden
|
||||
* - isIntersecting === false -> hidden
|
||||
* - intersectionRatio > 0 -> shown
|
||||
* - isIntersecting === true -> shown
|
||||
*/
|
||||
kpxcPassword.observer = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const rect = DOMRectToArray(entry.boundingClientRect);
|
||||
|
||||
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
|
||||
kpxcPassword.icon.style.display = 'none';
|
||||
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
|
||||
kpxcPassword.icon.style.display = 'block';
|
||||
|
||||
// Wait for possible DOM animations
|
||||
setTimeout(() => {
|
||||
kpxcPassword.setIconPosition(kpxcPassword.icon, entry.target)
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
kpxcPassword.init = function() {
|
||||
if ('initPasswordGenerator' in _called) {
|
||||
return;
|
||||
}
|
||||
|
||||
_called.initPasswordGenerator = true;
|
||||
};
|
||||
|
||||
kpxcPassword.initField = function(field, inputs, pos) {
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Observer the visibility
|
||||
kpxcPassword.observer.observe(field);
|
||||
|
||||
if (field.getAttribute('kpxc-password-generator')) {
|
||||
return;
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-password-generator', true);
|
||||
|
||||
kpxcPassword.createIcon(field);
|
||||
kpxcPassword.inputField = field;
|
||||
|
||||
let found = false;
|
||||
if (inputs) {
|
||||
for (let i = pos + 1; i < inputs.length; i++) {
|
||||
if (inputs[i] && inputs[i].getAttribute('type') && inputs[i].getAttribute('type').toLowerCase() === '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);
|
||||
};
|
||||
|
||||
kpxcPassword.createIcon = function(field) {
|
||||
const className = (isFirefox() ? 'key-moz' : 'key');
|
||||
const size = (field.offsetHeight > 28) ? 24 : 16;
|
||||
let offset = Math.floor((field.offsetHeight - size) / 3);
|
||||
offset = (offset < 0) ? 0 : offset;
|
||||
|
||||
const icon = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-icon ' + className,
|
||||
{
|
||||
'title': tr('passwordGeneratorGenerateText'),
|
||||
'alt': tr('passwordGeneratorIcon'),
|
||||
'size': size,
|
||||
'offset': offset,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
|
||||
});
|
||||
icon.style.zIndex = '9999';
|
||||
icon.style.width = String(size) + 'px';
|
||||
icon.style.height = String(size) + 'px';
|
||||
|
||||
icon.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
kpxcPassword.showDialog(field, icon);
|
||||
});
|
||||
|
||||
kpxcPassword.setIconPosition(icon, field);
|
||||
kpxcPassword.icon = icon;
|
||||
document.body.appendChild(icon);
|
||||
};
|
||||
|
||||
kpxcPassword.setIconPosition = function(icon, field) {
|
||||
const rect = field.getBoundingClientRect();
|
||||
const offset = Number(icon.getAttribute('offset'));
|
||||
const size = Number(icon.getAttribute('size'));
|
||||
|
||||
icon.style.top = String((rect.top + document.scrollingElement.scrollTop) + offset + 1) + 'px';
|
||||
icon.style.left = String((rect.left + document.scrollingElement.scrollLeft) + field.offsetWidth - size - offset) + 'px';
|
||||
};
|
||||
|
||||
kpxcPassword.createDialog = function() {
|
||||
if (kpxcPassword.created) {
|
||||
// If database is open again, generate a new password right away
|
||||
const input = $('.kpxc-pwgen-input');
|
||||
if (input.style.display === 'none') {
|
||||
kpxcPassword.generate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
kpxcPassword.created = true;
|
||||
|
||||
const wrapper = kpxcUI.createElement('div', 'kpxc');
|
||||
const dialog = kpxcUI.createElement('div', 'kpxc kpxc-pwgen-dialog');
|
||||
const titleBar = kpxcUI.createElement('div', 'kpxc-pwgen-titlebar', {}, tr('passwordGeneratorTitle'));
|
||||
const closeButton = kpxcUI.createElement('div', 'kpxc-pwgen-close', {}, '×');
|
||||
closeButton.onclick = function(e) {
|
||||
kpxcPassword.openDialog();
|
||||
};
|
||||
titleBar.append(closeButton);
|
||||
|
||||
const passwordRow = kpxcUI.createElement('div', 'kpxc-pwgen-password-row');
|
||||
const input = kpxcUI.createElement('input', 'kpxc-pwgen-input', { 'placeholder': tr('passwordGeneratorPlaceholder'), 'type': 'text', 'tabindex': '-1' });
|
||||
const inputLabel = kpxcUI.createElement('label', 'kpxc-pwgen-bits', {}, tr('passwordGeneratorBits', '???'));
|
||||
passwordRow.appendMultiple(input, inputLabel);
|
||||
|
||||
const nextFillRow = kpxcUI.createElement('div', 'kpxc-pwgen-nextfill-row');
|
||||
const checkbox = kpxcUI.createElement('input', 'kpxc-pwgen-checkbox', { 'id': 'kpxc-pwgen-checkbox', 'type': 'checkbox' });
|
||||
const checkboxLabel = kpxcUI.createElement('label', 'kpxc-pwgen-checkbox-label', { 'for': 'kpxc-pwgen-checkbox' }, tr('passwordGeneratorLabel'));
|
||||
nextFillRow.appendMultiple(checkbox, checkboxLabel);
|
||||
|
||||
// Buttons
|
||||
const buttonsRow = kpxcUI.createElement('div', 'kpxc-pwgen-buttons');
|
||||
const generateButton = kpxcUI.createElement('button', 'kpxc-button kpxc-white-button', { 'id': 'kpxc-pwgen-btn-generate' }, tr('passwordGeneratorGenerate'));
|
||||
const copyButton = kpxcUI.createElement('button', 'kpxc-button', { 'id': 'kpxc-pwgen-btn-copy' }, tr('passwordGeneratorCopy'));
|
||||
const fillButton = kpxcUI.createElement('button', 'kpxc-button', { 'id': 'kpxc-pwgen-btn-fill' }, tr('passwordGeneratorFillAndCopy'));
|
||||
|
||||
generateButton.onclick = function(e) {
|
||||
kpxcPassword.generate(e);
|
||||
};
|
||||
|
||||
copyButton.onclick = function(e) {
|
||||
kpxcPassword.copy(e);
|
||||
};
|
||||
|
||||
fillButton.onclick = function(e) {
|
||||
kpxcPassword.fill(e);
|
||||
};
|
||||
|
||||
buttonsRow.appendMultiple(generateButton, copyButton, fillButton);
|
||||
dialog.appendMultiple(titleBar, passwordRow, nextFillRow, buttonsRow);
|
||||
wrapper.append(dialog);
|
||||
|
||||
const icon = $('.kpxc-pwgen-icon');
|
||||
dialog.style.top = String(icon.offsetTop + icon.offsetHeight) + 'px';
|
||||
dialog.style.left = icon.style.left;
|
||||
|
||||
document.body.append(wrapper);
|
||||
|
||||
kpxcPassword.dialog = dialog;
|
||||
kpxcPassword.titleBar = titleBar;
|
||||
kpxcPassword.titleBar.onmousedown = function(e) {
|
||||
kpxcPassword.mouseDown(e);
|
||||
};
|
||||
|
||||
kpxcPassword.generate();
|
||||
};
|
||||
|
||||
kpxcPassword.mouseDown = function(e) {
|
||||
kpxcPassword.selected = kpxcPassword.titleBar;
|
||||
kpxcPassword.startPosX = e.clientX;
|
||||
kpxcPassword.startPosY = e.clientY;
|
||||
kpxcPassword.diffX = kpxcPassword.startPosX - kpxcPassword.dialog.offsetLeft;
|
||||
kpxcPassword.diffY = kpxcPassword.startPosY - kpxcPassword.dialog.offsetTop;
|
||||
return false;
|
||||
};
|
||||
|
||||
kpxcPassword.openDialog = function() {
|
||||
if (kpxcPassword.dialog.style.display === '' || kpxcPassword.dialog.style.display === 'none') {
|
||||
kpxcPassword.dialog.style.display = 'block';
|
||||
} else {
|
||||
kpxcPassword.dialog.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.trigger = function() {
|
||||
kpxcPassword.showDialog(kpxcPassword.inputField, kpxcPassword.icon);
|
||||
};
|
||||
|
||||
kpxcPassword.showDialog = function(field, icon) {
|
||||
if (!kpxcFields.isVisible(field)) {
|
||||
document.body.removeChild(icon);
|
||||
field.removeAttribute('kpxc-password-generator');
|
||||
return;
|
||||
}
|
||||
|
||||
kpxcPassword.createDialog();
|
||||
kpxcPassword.openDialog();
|
||||
|
||||
// Adjust the dialog location
|
||||
if (kpxcPassword.dialog) {
|
||||
kpxcPassword.dialog.style.top = String(icon.offsetTop + icon.offsetHeight) + 'px';
|
||||
kpxcPassword.dialog.style.left = icon.style.left;
|
||||
|
||||
kpxcPassword.dialog.setAttribute('kpxc-pwgen-field-id', field.getAttribute('data-kpxc-id'));
|
||||
kpxcPassword.dialog.setAttribute('kpxc-pwgen-next-field-id', field.getAttribute('kpxc-pwgen-next-field-id'));
|
||||
kpxcPassword.dialog.setAttribute('kpxc-pwgen-next-is-password-field', field.getAttribute('kpxc-pwgen-next-is-password-field'));
|
||||
|
||||
const fieldExists = Boolean(field.getAttribute('kpxc-pwgen-next-field-exists'));
|
||||
const checkbox = $('.kpxc-pwgen-checkbox');
|
||||
if (checkbox) {
|
||||
checkbox.setAttribute('checked', fieldExists);
|
||||
if (fieldExists) {
|
||||
checkbox.removeAttribute('disabled');
|
||||
} else {
|
||||
checkbox.setAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.generate = function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'generate_password'
|
||||
}).then(kpxcPassword.callbackGeneratedPassword).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
kpxcPassword.copy = function(e) {
|
||||
e.preventDefault();
|
||||
if (kpxcPassword.copyPasswordToClipboard()) {
|
||||
kpxcPassword.greenButton('#kpxc-pwgen-btn-copy');
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-fill');
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.fill = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let field = null;
|
||||
const inputs = document.querySelectorAll('input[type=\'password\']');
|
||||
for (const i of inputs) {
|
||||
if (i.getAttribute('data-kpxc-id')) {
|
||||
field = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (field) {
|
||||
const password = $('.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;
|
||||
}
|
||||
}
|
||||
|
||||
field.value = password.value;
|
||||
if ($('.kpxc-pwgen-checkbox').checked) {
|
||||
if (field.getAttribute('kpxc-pwgen-next-field-id')) {
|
||||
const nextFieldId = field.getAttribute('kpxc-pwgen-next-field-id');
|
||||
const nextField = $('input[data-kpxc-id=\'' + nextFieldId + '\']');
|
||||
if (nextField) {
|
||||
nextField.value = password.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kpxcPassword.copyPasswordToClipboard()) {
|
||||
kpxcPassword.greenButton('#kpxc-pwgen-btn-fill');
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-copy');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.copyPasswordToClipboard = function() {
|
||||
$('.kpxc-pwgen-input').select();
|
||||
try {
|
||||
return document.execCommand('copy');
|
||||
}
|
||||
catch (err) {
|
||||
console.log('Could not copy password to clipboard: ' + err);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
kpxcPassword.callbackGeneratedPassword = function(entries) {
|
||||
if (entries && entries.length >= 1) {
|
||||
const errorMessage = $('#kpxc-pwgen-error');
|
||||
if (errorMessage) {
|
||||
kpxcPassword.enableButtons();
|
||||
|
||||
$('.kpxc-pwgen-checkbox').parentElement.style.display = 'block';
|
||||
$('.kpxc-pwgen-bits').style.display = 'block';
|
||||
|
||||
const input = $('.kpxc-pwgen-input');
|
||||
input.style.display = 'block';
|
||||
errorMessage.remove();
|
||||
}
|
||||
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-fill');
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-copy');
|
||||
$('.kpxc-pwgen-input').value = entries[0].password;
|
||||
$('.kpxc-pwgen-bits').textContent = tr('passwordGeneratorBits', (isNaN(entries[0].login) ? '???' : entries[0].login));
|
||||
} else {
|
||||
if (document.querySelectorAll('div#kpxc-pwgen-error').length === 0) {
|
||||
$('.kpxc-pwgen-checkbox').parentElement.style.display = 'none';
|
||||
$('.kpxc-pwgen-bits').style.display = 'none';
|
||||
|
||||
const input = $('.kpxc-pwgen-input');
|
||||
input.style.display = 'none';
|
||||
|
||||
const errorMessage = kpxcUI.createElement('div', '', { 'id': 'kpxc-pwgen-error' },
|
||||
tr('passwordGeneratorError') + '\r\n' + tr('passwordGeneratorErrorIsRunning'));
|
||||
errorMessage.style.whiteSpace = 'pre';
|
||||
input.parentElement.append(errorMessage);
|
||||
|
||||
kpxcPassword.disableButtons();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.onRequestPassword = function() {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'generate_password'
|
||||
}).then(kpxcPassword.callbackGeneratedPassword);
|
||||
};
|
||||
|
||||
kpxcPassword.greenButton = function(button) {
|
||||
$(button).classList.remove('kpxc-white-button');
|
||||
$(button).classList.add('kpxc-green-button');
|
||||
};
|
||||
|
||||
kpxcPassword.whiteButton = function(button) {
|
||||
$(button).classList.remove('kpxc-green-button');
|
||||
$(button).classList.add('kpxc-white-button');
|
||||
};
|
||||
|
||||
kpxcPassword.enableButtons = function() {
|
||||
$('#kpxc-pwgen-btn-generate').textContent = tr('passwordGeneratorGenerate');
|
||||
$('#kpxc-pwgen-btn-copy').style.display = 'inline-block';
|
||||
$('#kpxc-pwgen-btn-fill').style.display = 'inline-block';
|
||||
};
|
||||
|
||||
kpxcPassword.disableButtons = function() {
|
||||
$('#kpxc-pwgen-btn-generate').textContent = tr('passwordGeneratorTryAgain');
|
||||
$('#kpxc-pwgen-btn-copy').style.display = 'none';
|
||||
$('#kpxc-pwgen-btn-fill').style.display = 'none';
|
||||
};
|
||||
|
||||
// Handle icon position on window resize
|
||||
window.addEventListener('resize', function(e) {
|
||||
if (kpxcPassword.inputField && kpxcPassword.icon) {
|
||||
kpxcPassword.setIconPosition(kpxcPassword.icon, kpxcPassword.inputField);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle icon position on scroll
|
||||
window.addEventListener('scroll', function(e) {
|
||||
if (kpxcPassword.inputField && kpxcPassword.icon) {
|
||||
kpxcPassword.setIconPosition(kpxcPassword.icon, kpxcPassword.inputField);
|
||||
}
|
||||
});
|
||||
|
||||
// Closes the dialog when clicked outside of it)
|
||||
document.onclick = function(e) {
|
||||
if (kpxcPassword.dialog && kpxcPassword.dialog.style.display === 'block') {
|
||||
const dialogEndX = kpxcPassword.dialog.offsetLeft + kpxcPassword.dialog.offsetWidth;
|
||||
const dialogEndY = kpxcPassword.dialog.offsetTop + kpxcPassword.dialog.offsetHeight;
|
||||
|
||||
if ((e.clientX < kpxcPassword.dialog.offsetLeft || e.clientX > dialogEndX) ||
|
||||
(e.clientY < kpxcPassword.dialog.offsetTop || e.clientY > dialogEndY) &&
|
||||
!e.target.classList.contains('kpxc-pwgen-icon')) {
|
||||
kpxcPassword.openDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
67
keepassxc-browser/content/ui.js
Normal file
67
keepassxc-browser/content/ui.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
'use strict';
|
||||
|
||||
// jQuery style wrapper for querySelector()
|
||||
var $ = function(elem)
|
||||
{
|
||||
return document.querySelector(elem);
|
||||
};
|
||||
|
||||
var kpxcUI = {};
|
||||
|
||||
// Wrapper for creating elements
|
||||
kpxcUI.createElement = function(type, classes, attributes, textContent) {
|
||||
const element = document.createElement(type);
|
||||
|
||||
if (classes) {
|
||||
const splitted = classes.split(' ');
|
||||
for (const c of splitted) {
|
||||
element.classList.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes !== undefined) {
|
||||
Object.keys(attributes).forEach((key) => {
|
||||
element.setAttribute(key, attributes[key]);
|
||||
});
|
||||
}
|
||||
|
||||
if (textContent !== undefined) {
|
||||
element.textContent = textContent;
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
// Enables dragging
|
||||
document.onmousemove = function(e) {
|
||||
if (kpxcPassword.selected === kpxcPassword.titleBar) {
|
||||
const x_pos = e.clientX - kpxcPassword.diffX;
|
||||
const y_pos = e.clientY - kpxcPassword.diffY;
|
||||
|
||||
if (kpxcPassword.selected !== null) {
|
||||
kpxcPassword.dialog.style.left = x_pos + 'px';
|
||||
kpxcPassword.dialog.style.top = y_pos + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
if (kpxcDefine.selected === kpxcDefine.dialog) {
|
||||
const x_pos = e.clientX - kpxcDefine.diffX;
|
||||
const y_pos = e.clientY - kpxcDefine.diffY;
|
||||
|
||||
if (kpxcDefine.selected !== null) {
|
||||
kpxcDefine.dialog.style.left = x_pos + 'px';
|
||||
kpxcDefine.dialog.style.top = y_pos + 'px';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.onmouseup = function() {
|
||||
kpxcPassword.selected = null;
|
||||
kpxcDefine.selected = null;
|
||||
};
|
||||
|
||||
HTMLDivElement.prototype.appendMultiple = function(...args) {
|
||||
for (const a of args) {
|
||||
this.append(a);
|
||||
}
|
||||
};
|
||||
19
keepassxc-browser/css/autocomplete.css
Normal file
19
keepassxc-browser/css/autocomplete.css
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
.kpxcAutocomplete-items {
|
||||
position: absolute !important;
|
||||
border: 1px solid #d4d4d4;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-items div {
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #d4d4d4;
|
||||
width: auto;
|
||||
color: #000;
|
||||
font-size: .9em !important;
|
||||
}
|
||||
|
||||
.kpxcAutocomplete-active {
|
||||
background-color: #6cac4d !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
48
keepassxc-browser/css/button.css
Normal file
48
keepassxc-browser/css/button.css
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
.kpxc-button {
|
||||
font-size: 12px !important;
|
||||
font-family: 'Lato', sans-serif !important;
|
||||
height: 28px !important;
|
||||
margin: .2em !important;
|
||||
background-color: #fff;
|
||||
border: 1px solid #888 !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.kpxc-white-button {
|
||||
color: #333 !important;
|
||||
background-color: #fff !important;
|
||||
border-color: #ccc !important;
|
||||
}
|
||||
|
||||
.kpxc-red-button {
|
||||
color: #fff !important;
|
||||
background-color: #d9534f !important;
|
||||
border-color: #d43f3a !important;
|
||||
}
|
||||
|
||||
.kpxc-orange-button {
|
||||
color: #fff !important;
|
||||
background-color: #f0ad4e !important;
|
||||
border-color: #eea236 !important;
|
||||
}
|
||||
|
||||
.kpxc-blue-button {
|
||||
color: #fff !important;
|
||||
background-color: #337ab7 !important;
|
||||
border-color: #2e6da4 !important;
|
||||
}
|
||||
|
||||
.kpxc-green-button {
|
||||
color: #fff !important;
|
||||
background-color: #5cb85c !important;
|
||||
border-color: #4cae4c !important;
|
||||
}
|
||||
|
||||
.kpxc-button:hover {
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kpxc-button:active {
|
||||
background-color: #ddd;
|
||||
}
|
||||
110
keepassxc-browser/css/define.css
Normal file
110
keepassxc-browser/css/define.css
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
.kpxcDefine-modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 2147483645;
|
||||
}
|
||||
|
||||
.kpxcDefine-modal-backdrop:after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #000000;
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
|
||||
#kpxcDefine-fields {
|
||||
z-index: 2147483646;
|
||||
}
|
||||
|
||||
#kpxcDefine-description {
|
||||
z-index: 2147483646;
|
||||
color: #efefef;
|
||||
border: 2px solid #555555;
|
||||
padding: 7px 5px;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 150px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
font-size: 15px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#kpxcDefine-description div:first-of-type {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
text-align: left;
|
||||
color: #efefef;
|
||||
padding-bottom: 8px;
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
#kpxcDefine-description p {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
color: #efefef;
|
||||
border-top: 2px solid #666666;
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
#kpxcDefine-help {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.kpxcDefine-keyboardHelp {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.kpxcDefine-keyboardHelp kbd {
|
||||
background-color: #eee;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #b4b4b4;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
padding: 2px 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kpxcDefine-fixed-field {
|
||||
position: absolute;
|
||||
border: 2px solid #efefef;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
z-index: 2147483646;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color: rgba(239,239,239,0.4);
|
||||
}
|
||||
|
||||
.kpxcDefine-fixed-hover-field {
|
||||
border: 2px solid orange;
|
||||
background-color: rgba(255,165,239,0.4);
|
||||
}
|
||||
|
||||
.kpxcDefine-fixed-password-field {
|
||||
border: 2px solid red;
|
||||
color: #efefef;
|
||||
background-color: rgba(255,0,0,0.4);
|
||||
}
|
||||
|
||||
.kpxcDefine-fixed-username-field {
|
||||
border: 2px solid limegreen;
|
||||
color: #efefef;
|
||||
background-color: rgba(50,205,50,0.4);
|
||||
}
|
||||
|
||||
.kpxcDefine-fixed-string-field {
|
||||
border: 2px solid deepskyblue;
|
||||
color: #efefef;
|
||||
background-color: rgba(30,144,255,0.4);
|
||||
}
|
||||
114
keepassxc-browser/css/pwgen.css
Normal file
114
keepassxc-browser/css/pwgen.css
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
.kpxc-pwgen-dialog {
|
||||
font-family: 'Lato', sans-serif !important;
|
||||
width: 350px;
|
||||
min-height: 80px;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2);
|
||||
z-index: 2147483645;
|
||||
padding: .1em .2em .1em .2em;
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-titlebar {
|
||||
font-size: 14px !important;
|
||||
background-color: #6cac4d;
|
||||
color: #fff;
|
||||
padding: .4em .2em .4em .8em;
|
||||
margin: .2em 0 .2em 0;
|
||||
position: relative;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-password-row {
|
||||
color: #fff;
|
||||
padding: .4em 1em;
|
||||
margin: .2em 0 .2em 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-nextfill-row {
|
||||
font-size: 12px !important;
|
||||
color: #000;
|
||||
padding: .4em 1em;
|
||||
margin: .2em 0 .2em 0;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-buttons {
|
||||
color: #fff;
|
||||
padding: .4em 1em;
|
||||
margin: .2em 0 .2em 0;
|
||||
border-top: 1px solid #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-bits {
|
||||
font-size: 12px !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: .4em;
|
||||
border-radius: 4px;
|
||||
min-width: 60px;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-collapse: separate;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-close {
|
||||
color: #fff;
|
||||
float: right;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-close:hover {
|
||||
background-color: #6cac4d !important;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-input {
|
||||
width: 100%;
|
||||
font-size: 12px !important;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-checkbox-label {
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.kpxc #kpxc-pwgen-error {
|
||||
color: #000;
|
||||
font-size: 12px !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-icon {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-icon.key {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-icon.key-moz {
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
|
@ -4,15 +4,12 @@ const IGNORE_NOTHING = 'ignoreNothing';
|
|||
const IGNORE_NORMAL = 'ignoreNormal';
|
||||
const IGNORE_FULL = 'ignoreFull';
|
||||
|
||||
var schemeSegment = '(\\*|http|https|ws|wss|file|ftp)';
|
||||
var hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?';
|
||||
var pathSegment = '(.*)';
|
||||
const schemeSegment = '(\\*|http|https|ws|wss|file|ftp)';
|
||||
const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?';
|
||||
const pathSegment = '(.*)';
|
||||
|
||||
var isFirefox = function() {
|
||||
if (!(/Chrome/.test(navigator.userAgent) && /Google/.test(navigator.vendor))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1;
|
||||
};
|
||||
|
||||
var showNotification = function(message) {
|
||||
|
|
@ -45,12 +42,12 @@ var matchPatternToRegExp = function(pattern) {
|
|||
|
||||
let match = matchPatternRegExp.exec(pattern);
|
||||
if (!match) {
|
||||
throw new TypeError(pattern + ' is not a valid MatchPattern');
|
||||
throw new TypeError('"${pattern}" is not a valid MatchPattern');
|
||||
}
|
||||
|
||||
let [, scheme, host, path] = match;
|
||||
if (!host) {
|
||||
throw new TypeError(pattern + ' does not have a valid host');
|
||||
throw new TypeError('"${pattern}" does not have a valid host');
|
||||
}
|
||||
|
||||
let regex = '^';
|
||||
|
|
@ -92,7 +89,6 @@ var siteMatch = function(site, url) {
|
|||
return url.match(rx);
|
||||
};
|
||||
|
||||
// Checks if URL has only scheme and host without the last / char.
|
||||
var slashNeededForUrl = function(pattern) {
|
||||
const matchPattern = new RegExp(`^${schemeSegment}://${hostSegment}$`);
|
||||
return matchPattern.exec(pattern);
|
||||
|
|
|
|||
7
keepassxc-browser/jquery-ui.min.css
vendored
7
keepassxc-browser/jquery-ui.min.css
vendored
File diff suppressed because one or more lines are too long
13
keepassxc-browser/jquery-ui.min.js
vendored
13
keepassxc-browser/jquery-ui.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,255 +0,0 @@
|
|||
.kpxc .ui-autocomplete li.ui-menu-item {
|
||||
text-align: left !important;
|
||||
font-size: .9em !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-family: 'Lato',sans-serif !important;
|
||||
color: #222222 !important;
|
||||
}
|
||||
|
||||
.kpxc .ui-menu {
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.ui-helper-hidden-accessible {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.kpxc .ui-dialog-titlebar-close {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.kpxc .ui-dialog {
|
||||
font-size: 12px !important;
|
||||
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2);
|
||||
z-index: 2147483645;
|
||||
}
|
||||
|
||||
.kpxc .ui-widget-overlay {
|
||||
opacity: 0.0 !important;
|
||||
}
|
||||
|
||||
.kpxc .dialog-form .ui-dialog-content .ui-widget-content {
|
||||
max-height: 80px !important;
|
||||
}
|
||||
|
||||
.kpxc .ui-dialog-titlebar {
|
||||
font-family: 'Lato',sans-serif;
|
||||
background-color: #6cac4d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.kpxc .ui-dialog .ui-dialog-buttonpane {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.kpxc .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.kpxc .ui-button .ui-button-text .ui-button {
|
||||
font-size: .10em !important;
|
||||
}
|
||||
|
||||
input.genpw-text {
|
||||
font-size: .9em !important;
|
||||
padding: .4em;
|
||||
border-radius: 4px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-collapse: separate;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.genpw-input-group-addon {
|
||||
font-size: .9em !important;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: .2em;
|
||||
border-radius: 4px;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
display: table-cell;
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
.kpxc .genpw-input-group {
|
||||
position: relative;
|
||||
display: table;
|
||||
border-collapse: separate;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cip-genpw-icon {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cip-genpw-icon.key {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.cip-genpw-icon.key-moz {
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc .cip-genpw-checkbox {
|
||||
-webkit-appearance: checkbox !important;
|
||||
-moz-appearance: checkbox !important;
|
||||
appearance: checkbox !important;
|
||||
}
|
||||
|
||||
#cip-genpw-btn-fillin {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#cip-genpw-quality {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.b2c-modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 2147483645;
|
||||
}
|
||||
|
||||
.b2c-modal-backdrop:after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #000000;
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
}
|
||||
|
||||
#b2c-cipDefine-fields {
|
||||
z-index: 2147483646;
|
||||
}
|
||||
|
||||
#b2c-cipDefine-description {
|
||||
z-index: 2147483646;
|
||||
color: #efefef;
|
||||
border: 2px solid #555555;
|
||||
padding: 7px 5px;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 150px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#b2c-cipDefine-description div:first-of-type {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
text-align: left;
|
||||
color: #efefef;
|
||||
padding-bottom: 8px;
|
||||
font-weight: bold;
|
||||
font-size: 160%;
|
||||
}
|
||||
|
||||
#b2c-cipDefine-description p {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
color: #efefef;
|
||||
border-top: 2px solid #666666;
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
#b2c-help {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
#b2c-keyboardHelp {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
#b2c-keyboardHelp kbd {
|
||||
background-color: #eee;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #b4b4b4;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
padding: 2px 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.b2c-fixed-field {
|
||||
position: absolute;
|
||||
border: 2px solid #efefef;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
z-index: 2147483646;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
background-color:rgba(239,239,239,0.4);
|
||||
}
|
||||
|
||||
.b2c-fixed-hover-field {
|
||||
border: 2px solid orange;
|
||||
background-color:rgba(255,165,239,0.4);
|
||||
}
|
||||
|
||||
.b2c-fixed-password-field {
|
||||
border: 2px solid red;
|
||||
color: #efefef;
|
||||
background-color:rgba(255,0,0,0.4);
|
||||
}
|
||||
|
||||
.b2c-fixed-username-field {
|
||||
border: 2px solid limegreen;
|
||||
color: #efefef;
|
||||
background-color:rgba(50,205,50,0.4);
|
||||
}
|
||||
|
||||
.b2c-fixed-string-field {
|
||||
border: 2px solid deepskyblue;
|
||||
color: #efefef;
|
||||
background-color:rgba(30,144,255,0.4);
|
||||
}
|
||||
|
||||
.b2c-input-append {
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.b2c-input-append input,
|
||||
.b2c-input-append select,
|
||||
.b2c-input-append .b2c-uneditable-input,
|
||||
.b2c-input-append .b2c-dropdown-menu,
|
||||
.b2c-input-append .b2c-popover {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.b2c-input-append input,
|
||||
.b2c-input-append select,
|
||||
.b2c-input-append .b2c-uneditable-input {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
*margin-left: 0;
|
||||
vertical-align: top;
|
||||
-webkit-border-radius: 0 4px 4px 0;
|
||||
-moz-border-radius: 0 4px 4px 0;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.b2c-input-append input:focus,
|
||||
.b2c-input-append select:focus,
|
||||
.b2c-input-append .b2c-b2c-uneditable-input:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "KeePassXC-Browser",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"version_name": "1.3.3",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"author": "KeePassXC Team",
|
||||
"icons": {
|
||||
|
|
@ -37,8 +38,8 @@
|
|||
"background/page.js",
|
||||
"background/event.js",
|
||||
"background/init.js"
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
|
|
@ -50,14 +51,18 @@
|
|||
],
|
||||
"js": [
|
||||
"browser-polyfill.min.js",
|
||||
"global.js",
|
||||
"jquery-3.3.1.min.js",
|
||||
"jquery-ui.min.js",
|
||||
"keepassxc-browser.js"
|
||||
"content/autocomplete.js",
|
||||
"content/define.js",
|
||||
"content/keepassxc-browser.js",
|
||||
"content/pwgen.js",
|
||||
"content/ui.js",
|
||||
"global.js"
|
||||
],
|
||||
"css": [
|
||||
"jquery-ui.min.css",
|
||||
"keepassxc-browser.css"
|
||||
"css/autocomplete.css",
|
||||
"css/button.css",
|
||||
"css/define.css",
|
||||
"css/pwgen.css"
|
||||
],
|
||||
"run_at": "document_idle",
|
||||
"all_frames": true
|
||||
|
|
@ -111,10 +116,10 @@
|
|||
"https://api.github.com/"
|
||||
],
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "keepassxc-browser@keepassxc.org",
|
||||
"strict_min_version": "52.0"
|
||||
}
|
||||
},
|
||||
"default_locale": "en"
|
||||
"gecko": {
|
||||
"id": "keepassxc-browser@keepassxc.org",
|
||||
"strict_min_version": "52.0"
|
||||
}
|
||||
},
|
||||
"default_locale": "en"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
<span class="help-inline">
|
||||
<span data-i18n="optionsRedirectAllowanceHelpText"></span>
|
||||
<br />
|
||||
<span data-i18n="optionsDefault"></span>1
|
||||
<span data-i18n="optionsDefault" i18n-placeholder="1"></span>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
|
|
@ -134,6 +134,15 @@
|
|||
</div>
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<div class="checkbox">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="automaticReconnect" value="true" /><span data-i18n="optionsCheckboxAutomaticReconnect"/>
|
||||
</label>
|
||||
<span class="help-block" data-i18n="optionsAutomaticReconnectHelpText"></span>
|
||||
</div>
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<div class="checkbox">
|
||||
<label class="checkbox">
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ options.initGeneralSettings = function() {
|
|||
$('#tab-general-settings input[type=checkbox]').change(function() {
|
||||
const name = $(this).attr('name');
|
||||
options.settings[name] = $(this).is(':checked');
|
||||
options.saveSettingsPromise().then((x) => {
|
||||
options.saveSettingsPromise().then((updated) => {
|
||||
if (name === 'autoFillAndSend') {
|
||||
browser.runtime.sendMessage({ action: 'init_http_auth' });
|
||||
} else if (name === 'defaultGroupAlwaysAsk') {
|
||||
|
|
@ -107,6 +107,9 @@ options.initGeneralSettings = function() {
|
|||
$('#defaultGroupButton').prop('disabled', false);
|
||||
$('#defaultGroupButtonReset').prop('disabled', false);
|
||||
}
|
||||
} else if (name == 'automaticReconnect') {
|
||||
const message = updated.automaticReconnect ? 'enable_automatic_reconnect' : 'disable_automatic_reconnect';
|
||||
browser.runtime.sendMessage({ action: message });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -330,6 +333,10 @@ options.initCustomCredentialFields = function() {
|
|||
};
|
||||
|
||||
options.initSitePreferences = function() {
|
||||
if (!options.settings['sitePreferences']) {
|
||||
options.settings['sitePreferences'] = [];
|
||||
}
|
||||
|
||||
$('#dialogDeleteSite').modal({ keyboard: true, show: false, backdrop: true });
|
||||
$('#tab-site-preferences tr.clone:first button.delete:first').click(function(e) {
|
||||
e.preventDefault();
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ async function updateShortcut(shortcut) {
|
|||
shortcut: document.querySelector('#' + shortcut).value
|
||||
});
|
||||
createBanner('success', shortcut);
|
||||
} catch (e) {
|
||||
console.log('Cannot change shortcut: ' + e);
|
||||
} catch (err) {
|
||||
console.log('Cannot change shortcut: ' + err);
|
||||
createBanner('danger', shortcut);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,18 +40,18 @@ body {
|
|||
font-size: 80%;
|
||||
color: #787878;
|
||||
}
|
||||
.small {
|
||||
margin-top: 3px;
|
||||
margin-left: 12px;
|
||||
font-size: 80%;
|
||||
color: #787878;
|
||||
}
|
||||
#list {
|
||||
padding-left: 0px;
|
||||
}
|
||||
#child {
|
||||
border-top: 0px;
|
||||
}
|
||||
.small {
|
||||
margin-top: 3px;
|
||||
margin-left: 12px;
|
||||
font-size: 80%;
|
||||
color: #787878;
|
||||
}
|
||||
#update-available {
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../options/bootstrap.min.css" />
|
||||
<script type="text/javascript" src="../browser-polyfill.min.js"></script>
|
||||
<script type="text/javascript" src="../global.js"></script>
|
||||
<script type="text/javascript" src="../jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="../options/bootstrap.min.js" /></script>
|
||||
<script type="text/javascript" src="popup_functions.js" /></script>
|
||||
<script type="text/javascript" src="popup.js" /></script>
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../jquery-3.3.1.min.js"></script>
|
||||
<script src="../options/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
|
@ -21,7 +23,7 @@
|
|||
<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"/></a>.
|
||||
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -30,18 +32,18 @@
|
|||
</div>
|
||||
|
||||
<div id="not-configured" style="display: none">
|
||||
<p data-i18n="popupNotConfigured"/>
|
||||
<p data-i18n="popupNotConfigured"></p>
|
||||
<div style="text-align: right">
|
||||
<button id="connect-button" class="btn btn-sm btn-primary"><span class="glyphicon glyphicon-link"></span> <span data-i18n="popupConnectButton"/></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="need-reconfigure" style="display: none">
|
||||
<p data-i18n="popupNeedReconfigure"/>
|
||||
<p data-i18n="popupNeedReconfigure"></p>
|
||||
<p style="margin-left: 1em">
|
||||
<code id="need-reconfigure-message"></code>
|
||||
</p>
|
||||
<p data-i18n="popupNeedReconfigureMessage"/>
|
||||
<p data-i18n="popupNeedReconfigureMessage"></p>
|
||||
<div style="text-align: right">
|
||||
<button id="reconnect-button" class="btn btn-sm btn-primary"><span class="glyphicon glyphicon-refresh"></span> <span data-i18n="popupReconnectButton"/></button>
|
||||
</div>
|
||||
|
|
@ -59,7 +61,7 @@
|
|||
</div>
|
||||
|
||||
<div id="error-encountered" style="display: none">
|
||||
<p data-i18n="popupErrorEncountered"/>
|
||||
<p data-i18n="popupErrorEncountered"></p>
|
||||
<p style="margin-left: 1em">
|
||||
<code id="error-message"></code>
|
||||
</p>
|
||||
|
|
@ -69,7 +71,7 @@
|
|||
</div>
|
||||
|
||||
<div id="database-not-opened" style="display: none">
|
||||
<p data-i18n="popupErrorEncountered"/>
|
||||
<p data-i18n="popupErrorEncountered"></p>
|
||||
<p style="margin-left: 1em">
|
||||
<code id="database-error-message"></code>
|
||||
</p>
|
||||
|
|
@ -78,6 +80,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../translate.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../options/bootstrap.min.css" />
|
||||
<script type="text/javascript" src="../browser-polyfill.min.js"></script>
|
||||
<script type="text/javascript" src="../global.js"></script>
|
||||
<script type="text/javascript" src="../jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="../options/bootstrap.min.js" /></script>
|
||||
<script type="text/javascript" src="popup_functions.js"></script>
|
||||
<script type="text/javascript" src="popup_httpauth.js"></script>
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../jquery-3.3.1.min.js"></script>
|
||||
<script src="../options/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script src="popup_httpauth.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
|
@ -21,7 +23,7 @@
|
|||
<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"/></a>.
|
||||
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -33,6 +35,5 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../translate.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
const getLoginData = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.runtime.getBackgroundPage().then((global) => {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
|
||||
browser.tabs.query({'active': true, 'currentWindow': true}).then((tabs) => {
|
||||
resolve(global.page.tabs[tabs[0].id].loginList);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../options/bootstrap.min.css" />
|
||||
<script type="text/javascript" src="../browser-polyfill.min.js"></script>
|
||||
<script type="text/javascript" src="../global.js"></script>
|
||||
<script type="text/javascript" src="../jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="../options/bootstrap.min.js" /></script>
|
||||
<script type="text/javascript" src="popup_functions.js"></script>
|
||||
<script type="text/javascript" src="popup_login.js"></script>
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../jquery-3.3.1.min.js"></script>
|
||||
<script src="../options/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script src="popup_login.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
|
@ -21,7 +23,7 @@
|
|||
<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"/></a>.
|
||||
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -44,6 +46,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../translate.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../options/bootstrap.min.css" />
|
||||
<script type="text/javascript" src="../browser-polyfill.min.js"></script>
|
||||
<script type="text/javascript" src="../global.js"></script>
|
||||
<script type="text/javascript" src="../jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="../options/bootstrap.min.js" /></script>
|
||||
<script type="text/javascript" src="popup_functions.js"></script>
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../jquery-3.3.1.min.js"></script>
|
||||
<script src="../options/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
|
@ -20,7 +22,7 @@
|
|||
<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"/></a>.
|
||||
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -31,6 +33,5 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../translate.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../options/bootstrap.min.css" />
|
||||
<script type="text/javascript" src="../browser-polyfill.min.js"></script>
|
||||
<script type="text/javascript" src="../global.js"></script>
|
||||
<script type="text/javascript" src="../jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="../options/bootstrap.min.js" /></script>
|
||||
<script type="text/javascript" src="popup_functions.js"></script>
|
||||
<script type="text/javascript" src="popup_remember.js"></script>
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../jquery-3.3.1.min.js"></script>
|
||||
<script src="../options/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script src="popup_remember.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="buttons">
|
||||
|
|
@ -43,6 +45,5 @@
|
|||
<p data-i18n="popupRememberChooseGroup"></p>
|
||||
<ul id="list" class="list-group"></ul>
|
||||
</div>
|
||||
<script type="text/javascript" src="../translate.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ function _initialize(tab) {
|
|||
// Another group name has been specified
|
||||
const [ gname, guuid ] = getDefaultGroup(result.groups[0].children, result.defaultGroup);
|
||||
if (gname === '' && guuid === '') {
|
||||
showNotification(tr('popupRememberErrorDefaultGroupNotFound'));
|
||||
showNotification(tr('popupRememberInfoDefaultGroupNotFound'));
|
||||
|
||||
// Create a new group
|
||||
browser.runtime.sendMessage({
|
||||
|
|
@ -216,7 +216,7 @@ function _initialize(tab) {
|
|||
const currentTab = tabs[0];
|
||||
browser.runtime.getBackgroundPage().then((global) => {
|
||||
browser.tabs.sendMessage(currentTab.id, {
|
||||
action: 'ignore-site',
|
||||
action: 'ignore_site',
|
||||
args: [ _tab.credentials.url ]
|
||||
});
|
||||
_close();
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 673 B |
Loading…
Reference in a new issue