diff --git a/.eslintrc b/.eslintrc index 50fa8f0..cb4c5c4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,12 +3,12 @@ "ignorePatterns": ["**/*.min.js"], "env": { "browser": true, - "es6": true, + "es2022": true, "jquery": true, "webextensions": true }, "parserOptions": { - "ecmaVersion": 2020 + "ecmaVersion": 13 }, "rules": { "array-bracket-spacing": ["error", "always"], @@ -101,6 +101,7 @@ "DatabaseState": true, "debugLogMessage": true, "EXTENSION_NAME": true, + "getCurrentTab": true, "getTopLevelDomainFromUrl": true, "httpAuth": true, "Icon": true, diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index 149673b..d724eaa 100755 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -3,10 +3,7 @@ const browserAction = {}; browserAction.show = function(tab, popupData) { - if (!popupData) { - popupData = page.popupData; - } - + popupData ??= page.popupData; page.popupData = popupData; browser.browserAction.setIcon({ @@ -38,13 +35,9 @@ browserAction.showDefault = async function(tab) { } // Get the current tab if no tab given + tab ??= await getCurrentTab(); if (!tab) { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length === 0) { - return; - } - - tab = tabs[0]; + return; } if (page.tabs[tab.id]?.loginList.length > 0) { @@ -65,10 +58,7 @@ browserAction.generateIconName = function(iconType) { browserAction.ignoreSite = async function(url) { await browser.windows.getCurrent(); - - // Get current active window - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - const tab = tabs[0]; + const tab = await getCurrentTab(); // Send the message to the current tab's content script await browser.runtime.getBackgroundPage(); diff --git a/keepassxc-browser/background/client.js b/keepassxc-browser/background/client.js index bb7f194..fe6800d 100644 --- a/keepassxc-browser/background/client.js +++ b/keepassxc-browser/background/client.js @@ -81,7 +81,7 @@ keepassClient.sendNativeMessage = function(request, enableTimeout = false, timeo const listener = ((port, action) => { const handler = (msg) => { - if (msg && msg.action === action) { + if (msg && msg?.action === action) { // If the request has a separate requestID, check if it matches when there's no nonce (an error message) const isNotificationOrError = !msg.nonce && request.requestID === msg.requestID; diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 459d5c4..ec14232 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -4,7 +4,7 @@ const kpxcEvent = {}; kpxcEvent.onMessage = async function(request, sender) { if (request.action in kpxcEvent.messageHandlers) { - if (!sender.hasOwnProperty('tab') || sender.tab.id < 1) { + if (!Object.hasOwn(sender, 'tab') || sender.tab.id < 1) { sender.tab = {}; sender.tab.id = page.currentTabId; } @@ -56,8 +56,8 @@ kpxcEvent.onLoadKeyRing = async function() { keepass.keyRing = item.keyRing; if (keepass.isAssociated() && !keepass.keyRing[keepass.associated.hash]) { keepass.associated = { - 'value': false, - 'hash': null + value: false, + hash: null }; } @@ -113,7 +113,7 @@ kpxcEvent.lockDatabase = async function(tab) { }; kpxcEvent.onGetTabInformation = async function(tab) { - const id = tab.id || page.currentTabId; + const id = tab?.id || page.currentTabId; return page.tabs[id]; }; @@ -143,7 +143,7 @@ kpxcEvent.onUpdateAvailableKeePassXC = async function() { }; kpxcEvent.onRemoveCredentialsFromTabInformation = async function(tab) { - const id = tab.id || page.currentTabId; + const id = tab?.id || page.currentTabId; page.clearCredentials(id); page.clearSubmittedCredentials(); }; diff --git a/keepassxc-browser/background/init.js b/keepassxc-browser/background/init.js index 6d84218..80cfc8f 100644 --- a/keepassxc-browser/background/init.js +++ b/keepassxc-browser/background/init.js @@ -21,14 +21,14 @@ * @param {object} tab */ browser.tabs.onCreated.addListener((tab) => { - if (tab.id > 0) { - if (tab.selected) { - page.currentTabId = tab.id; - if (!page.tabs[tab.id]) { - page.createTabEntry(tab.id); - } - page.switchTab(tab); + if (tab?.id > 0 && tab?.selected) { + page.currentTabId = tab.id; + + if (!page.tabs[tab.id]) { + page.createTabEntry(tab.id); } + + page.switchTab(tab); } }); @@ -39,12 +39,8 @@ browser.tabs.onCreated.addListener((tab) => { */ browser.tabs.onRemoved.addListener(async function(tabId, removeInfo) { if (page.currentTabId === tabId) { - const activeTabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (activeTabs.length > 0) { - page.currentTabId = activeTabs[0].id; - } else { - page.currentTabId = -1; - } + const currentTab = await getCurrentTab(); + page.currentTabId = currentTab ? currentTab.id : -1; } delete page.tabs[tabId]; }); @@ -97,8 +93,7 @@ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { * @param {object} details */ browser.webNavigation.onCommitted.addListener((details) => { - if ((details.transitionQualifiers.length > 0 && details.transitionQualifiers[0] === 'client_redirect') - || details.transitionType === 'form_submit') { + if (details.transitionQualifiers?.[0] === 'client_redirect' || details.transitionType === 'form_submit') { page.redirectCount += 1; return; } @@ -153,9 +148,9 @@ browser.commands.onCommand.addListener(async (command) => { || command === 'choose_credential_fields' || command === 'retrive_credentials_forced' || command === 'reload_extension') { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length) { - browser.tabs.sendMessage(tabs[0].id, { action: command }); + const tab = await getCurrentTab(); + if (tab) { + browser.tabs.sendMessage(tab.id, { action: command }); } } }); diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 82ad4d1..ee15d32 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -15,8 +15,6 @@ keepass.latestVersionUrl = 'https://api.github.com/repos/keepassxreboot/keepassx keepass.cacheTimeout = 30 * 1000; // Milliseconds keepass.databaseHash = ''; keepass.previousDatabaseHash = ''; -keepass.keyId = 'keepassxc-browser-cryptokey-name'; -keepass.keyBody = 'keepassxc-browser-key'; keepass.reconnectLoop = null; const kpActions = { @@ -644,7 +642,7 @@ keepass.migrateKeyRing = function() { }; keepass.saveKey = function(hash, id, key) { - if (!(hash in keepass.keyRing)) { + if (!Object.hasOwn(keepass.keyRing, hash)) { keepass.keyRing[hash] = { id: id, key: key, @@ -663,7 +661,7 @@ keepass.saveKey = function(hash, id, key) { }; keepass.updateLastUsed = function(hash) { - if ((hash in keepass.keyRing)) { + if (Object.hasOwn(keepass.keyRing, hash)) { keepass.keyRing[hash].lastUsed = new Date().valueOf(); browser.storage.local.set({ 'keyRing': keepass.keyRing }); } @@ -691,6 +689,7 @@ keepass.deleteKey = function(hash) { keepass.getCryptoKey = function() { let dbkey = null; let dbid = null; + if (!(keepass.databaseHash in keepass.keyRing)) { return [ dbid, dbkey ]; } @@ -712,7 +711,7 @@ keepass.setCryptoKey = function(id, key) { // Connection //-------------------------------------------------------------------------- -keepass.enableAutomaticReconnect = function() { +keepass.enableAutomaticReconnect = async function() { // Disable for Windows if KeePassXC is older than 2.3.4 if (!page.settings.autoReconnect || (navigator.platform.toLowerCase().includes('win') @@ -767,7 +766,7 @@ keepass.generateNewKeyPair = function() { keepass.isConfigured = async function() { if (typeof(keepass.databaseHash) === 'undefined') { const hash = keepass.getDatabaseHash(); - return hash in keepass.keyRing; + return Object.hasOwn(keepass.keyRing, hash); } return keepass.databaseHash in keepass.keyRing; @@ -870,10 +869,10 @@ keepass.updateDatabase = async function() { keepass.updateDatabaseHashToContent = async function() { try { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length) { + const tab = await getCurrentTab(); + if (tab) { // Send message to content script - browser.tabs.sendMessage(tabs[0].id, { + browser.tabs.sendMessage(tab.id, { action: 'check_database_hash', hash: { old: keepass.previousDatabaseHash, new: keepass.databaseHash }, connected: keepass.isKeePassXCAvailable diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index fe1aec7..d787818 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -58,116 +58,11 @@ page.initSettings = async function() { page.settings = item.settings; page.settings.autoReconnect = false; - if (!('afterFillSorting' in page.settings)) { - page.settings.afterFillSorting = defaultSettings.afterFillSorting; - } - - if (!('afterFillSortingTotp' in page.settings)) { - page.settings.afterFillSortingTotp = defaultSettings.afterFillSortingTotp; - } - - if (!('autoCompleteUsernames' in page.settings)) { - page.settings.autoCompleteUsernames = defaultSettings.autoCompleteUsernames; - } - - if (!('showGroupNameInAutocomplete' in page.settings)) { - page.settings.showGroupNameInAutocomplete = defaultSettings.showGroupNameInAutocomplete; - } - - if (!('autoFillAndSend' in page.settings)) { - page.settings.autoFillAndSend = defaultSettings.autoFillAndSend; - } - - if (!('autoFillSingleEntry' in page.settings)) { - page.settings.autoFillSingleEntry = defaultSettings.autoFillSingleEntry; - } - - if (!('autoRetrieveCredentials' in page.settings)) { - page.settings.autoRetrieveCredentials = defaultSettings.autoRetrieveCredentials; - } - - if (!('autoSubmit' in page.settings)) { - page.settings.autoSubmit = defaultSettings.autoSubmit; - } - - if (!('checkUpdateKeePassXC' in page.settings)) { - page.settings.checkUpdateKeePassXC = defaultSettings.checkUpdateKeePassXC; - } - - if (!('colorTheme' in page.settings)) { - page.settings.colorTheme = defaultSettings.colorTheme; - } - - if (!('clearCredentialsTimeout' in page.settings)) { - page.settings.clearCredentialsTimeout = defaultSettings.clearCredentialsTimeout; - } - - if (!('credentialSorting' in page.settings)) { - page.settings.credentialSorting = defaultSettings.credentialSorting; - } - - if (!('debugLogging' in page.settings)) { - page.settings.debugLogging = defaultSettings.debugLogging; - } - - if (!('defaultGroup' in page.settings)) { - page.settings.defaultGroup = defaultSettings.defaultGroup; - } - - if (!('defaultGroupAlwaysAsk' in page.settings)) { - page.settings.defaultGroupAlwaysAsk = defaultSettings.defaultGroupAlwaysAsk; - } - - if (!('downloadFaviconAfterSave' in page.settings)) { - page.settings.downloadFaviconAfterSave = defaultSettings.downloadFaviconAfterSave; - } - - if (!('redirectAllowance' in page.settings)) { - page.settings.redirectAllowance = defaultSettings.redirectAllowance; - } - - if (!('saveDomainOnly' in page.settings)) { - page.settings.saveDomainOnly = defaultSettings.saveDomainOnly; - } - - if (!('showGettingStartedGuideAlert' in page.settings)) { - page.settings.showGettingStartedGuideAlert = defaultSettings.showGettingStartedGuideAlert; - } - - if (!('showTroubleshootingGuideAlert' in page.settings)) { - page.settings.showTroubleshootingGuideAlert = defaultSettings.showTroubleshootingGuideAlert; - } - - if (!('showLoginFormIcon' in page.settings)) { - page.settings.showLoginFormIcon = defaultSettings.showLoginFormIcon; - } - - if (!('showLoginNotifications' in page.settings)) { - page.settings.showLoginNotifications = defaultSettings.showLoginNotifications; - } - - if (!('showNotifications' in page.settings)) { - page.settings.showNotifications = defaultSettings.showNotifications; - } - - if (!('showOTPIcon' in page.settings)) { - page.settings.showOTPIcon = defaultSettings.showOTPIcon; - } - - if (!('usePasswordGeneratorIcons' in page.settings)) { - page.settings.usePasswordGeneratorIcons = defaultSettings.usePasswordGeneratorIcons; - } - - if (!('useObserver' in page.settings)) { - page.settings.useObserver = defaultSettings.useObserver; - } - - if (!('usePasswordGeneratorIcons' in page.settings)) { - page.settings.usePasswordGeneratorIcons = defaultSettings.usePasswordGeneratorIcons; - } - - if (!('usePredefinedSites' in page.settings)) { - page.settings.usePredefinedSites = defaultSettings.usePredefinedSites; + // Set default settings if needed + for (const [ key, value ] of Object.entries(defaultSettings)) { + if (!Object.hasOwn(page.settings, key)) { + page.settings[key] = value; + } } await browser.storage.local.set({ 'settings': page.settings }); @@ -186,13 +81,13 @@ page.initOpenedTabs = async function() { } // Set initial tab-ID - const currentTabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (currentTabs.length === 0) { + const currentTab = await getCurrentTab(); + if (!currentTab) { return; } - page.currentTabId = currentTabs[0].id; - browserAction.showDefault(currentTabs[0]); + page.currentTabId = currentTab.id; + browserAction.showDefault(currentTab); } catch (err) { logError('page.initOpenedTabs error: ' + err); return Promise.reject(); @@ -372,11 +267,7 @@ page.setAutoSubmitPerformed = async function(tab) { }; page.getLoginList = async function(tab) { - if (page.tabs[tab.id]) { - return page.tabs[tab.id].loginList; - } - - return []; + return page.tabs[tab.id] ? page.tabs[tab.id].loginList : []; }; page.fillHttpAuth = async function(tab, credentials) { @@ -410,8 +301,8 @@ page.updateContextMenu = async function(tab, credentials) { // Show username inside [] if there are KPH attributes inside multiple credentials const attributeName = Object.keys(attribute)[0].slice(5); const finalName = credentials.length > 1 - ? `[${cred.login}] ${attributeName}` - : attributeName; + ? `[${cred.login}] ${attributeName}` + : attributeName; page.attributeMenuItemIds.push(createContextMenuItem({ action: 'fill_attribute', diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 65a5c4a..fe84b65 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -148,12 +148,17 @@ const logError = function(message) { // Returns file name and line number from error stack const getFileAndLine = function() { const err = new Error().stack.split('\n'); - const line = err[4] ?? err[err.length - 1]; + const line = err[4] ?? err.at(-1); const result = line.substring(line.lastIndexOf('/') + 1, line.lastIndexOf(':')); return result; }; +const getCurrentTab = async function() { + const tabs = await browser.tabs.query({ active: true, currentWindow: true }); + return tabs.length > 0 ? tabs[0] : undefined; +}; + HTMLElement.prototype.show = function() { this.style.display = 'block'; }; diff --git a/keepassxc-browser/common/sites.js b/keepassxc-browser/common/sites.js index a4c4f24..a695037 100644 --- a/keepassxc-browser/common/sites.js +++ b/keepassxc-browser/common/sites.js @@ -172,7 +172,7 @@ kpxcSites.formSubmitButtonExceptionFound = function(form) { 'login.microsoftonline.us', ].some(u => form.action.includes(u))) { const buttons = Array.from(form.querySelectorAll(kpxcForm.formButtonQuery)); - if (buttons && buttons.length > 1) { + if (buttons?.length > 1) { return buttons[1]; } } @@ -186,7 +186,7 @@ kpxcSites.formSubmitButtonExceptionFound = function(form) { * @returns {boolean} True if exception found */ kpxcSites.popupExceptionFound = function(combinations) { - if (combinations.length > 1 && combinations[0].form && combinations[0].form.action.startsWith(googleUrl)) { + if (combinations?.[0].form?.action.startsWith(googleUrl)) { return true; } diff --git a/keepassxc-browser/content/banner.js b/keepassxc-browser/content/banner.js index 2338ea9..ce1e799 100644 --- a/keepassxc-browser/content/banner.js +++ b/keepassxc-browser/content/banner.js @@ -61,9 +61,9 @@ kpxcBanner.create = async function(credentials = {}) { const usernameText = kpxcUI.createElement('span', 'small', {}, tr('popupUsername') + ' '); const usernameSpan = kpxcUI.createElement('span', 'small info information-username', {}, credentials.username); - const newButton = kpxcUI.createElement('button', 'kpxc-button kpxc-green-button', { 'id': 'kpxc-banner-btn-new' }, tr('popupButtonNew')); - const updateButton = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxc-banner-btn-update' }, tr('popupButtonUpdate')); - const dismissButton = kpxcUI.createElement('button', 'kpxc-button kpxc-red-button', { 'id': 'kpxc-banner-btn-dismiss' }, tr('popupButtonDismiss')); + const newButton = kpxcUI.createElement('button', GREEN_BUTTON, { 'id': 'kpxc-banner-btn-new' }, tr('popupButtonNew')); + const updateButton = kpxcUI.createElement('button', ORANGE_BUTTON, { 'id': 'kpxc-banner-btn-update' }, tr('popupButtonUpdate')); + const dismissButton = kpxcUI.createElement('button', RED_BUTTON, { 'id': 'kpxc-banner-btn-dismiss' }, tr('popupButtonDismiss')); const separator = kpxcUI.createElement('div', 'kpxc-separator'); const ignoreCheckbox = kpxcUI.createElement('input', 'kpxc-checkbox', { type: 'checkbox', name: 'ignoreCheckbox', id: 'kpxc-banner-ignoreCheckbox' }); @@ -238,11 +238,9 @@ kpxcBanner.saveNewCredentials = async function(credentials = {}) { kpxcBanner.updateCredentials = async function(credentials = {}) { // Only one entry which could be updated - if (credentials.list.length === 1) { + if (credentials.list?.length === 1) { // Use the current username if it's empty - if (!credentials.username) { - credentials.username = credentials.list[0].login; - } + credentials.username ??= credentials.list[0].login; const res = await sendMessage('update_credentials', [ credentials.list[0].uuid, credentials.username, credentials.password, credentials.url ]); kpxcBanner.verifyResult(res); @@ -259,8 +257,8 @@ kpxcBanner.updateCredentials = async function(credentials = {}) { kpxcBanner.shadowSelector('.kpxc-banner-dialog .username-exists').style.display = 'none'; } - for (let i = 0; i < credentials.list.length; i++) { - const a = kpxcUI.createElement('a', 'list-group-item', { 'href': '#', 'entryId': i }, `${credentials.list[i].login} (${credentials.list[i].name})`); + for (const [ i, cred ] of credentials.list.entries()) { + const a = kpxcUI.createElement('a', 'list-group-item', { 'href': '#', 'entryId': i }, `${cred.login} (${cred.name})`); a.addEventListener('click', function(e) { e.preventDefault(); if (!e.isTrusted) { @@ -271,7 +269,7 @@ kpxcBanner.updateCredentials = async function(credentials = {}) { // Use the current username if it's empty if (!credentials.username) { - credentials.username = credentials.list[entryId].login; + credentials.username = cred.login; } let url = credentials.url; @@ -292,7 +290,7 @@ kpxcBanner.updateCredentials = async function(credentials = {}) { }); }); - if (credentials.usernameExists && credentials.username === credentials.list[i].login) { + if (credentials.usernameExists && credentials.username === cred.login) { a.style.fontWeight = 'bold'; } diff --git a/keepassxc-browser/content/custom-fields-banner.js b/keepassxc-browser/content/custom-fields-banner.js index b06ad8b..f811e26 100644 --- a/keepassxc-browser/content/custom-fields-banner.js +++ b/keepassxc-browser/content/custom-fields-banner.js @@ -6,12 +6,6 @@ const STEP_SELECT_PASSWORD = 2; const STEP_SELECT_TOTP = 3; const STEP_SELECT_STRING_FIELDS = 4; -const BLUE_BUTTON = 'kpxc-button kpxc-blue-button'; -const GREEN_BUTTON = 'kpxc-button kpxc-green-button'; -const ORANGE_BUTTON = 'kpxc-button kpxc-orange-button'; -const RED_BUTTON = 'kpxc-button kpxc-red-button'; -const GRAY_BUTTON_CLASS = 'kpxc-gray-button'; - const DEFINED_CUSTOM_FIELDS = 'defined-custom-fields'; const FIXED_FIELD_CLASS = 'kpxcDefine-fixed-field'; const DARK_FIXED_FIELD_CLASS = 'kpxcDefine-fixed-field-dark'; @@ -309,11 +303,11 @@ kpxcCustomLoginFieldsBanner.confirm = async function() { // If the new selection is already used in some other field, clear it const clearIdenticalField = function(path, location) { const currentSite = kpxc.settings[DEFINED_CUSTOM_FIELDS][location]; - if (currentSite.username && currentSite.username[0] === path[0]) { + if (currentSite.username?.[0] === path[0]) { kpxc.settings[DEFINED_CUSTOM_FIELDS][location].username = undefined; - } else if (currentSite.password && currentSite.password[0] === path[0]) { + } else if (currentSite.password?.[0] === path[0]) { kpxc.settings[DEFINED_CUSTOM_FIELDS][location].password = undefined; - } else if (currentSite.totp && currentSite.totp[0] === path[0]) { + } else if (currentSite.totp?.[0] === path[0]) { kpxc.settings[DEFINED_CUSTOM_FIELDS][location].totp = undefined; } }; diff --git a/keepassxc-browser/content/fields.js b/keepassxc-browser/content/fields.js index f0c5c93..e363643 100644 --- a/keepassxc-browser/content/fields.js +++ b/keepassxc-browser/content/fields.js @@ -111,9 +111,9 @@ kpxcFields.getSegmentedTOTPFields = function(inputs, combinations) { combinations.push(combination); // Create an icon to the right side of the segmented fields - kpxcTOTPIcons.newIcon(totpInputs[totpInputs.length - 1], kpxc.databaseState, true); + kpxcTOTPIcons.newIcon(totpInputs.at(-1), kpxc.databaseState, true); kpxcIcons.icons.push({ - field: totpInputs[totpInputs.length - 1], + field: totpInputs.at(-1), iconType: kpxcIcons.iconTypes.TOTP, segmented: true }); @@ -272,7 +272,7 @@ kpxcFields.getIdFromProperties = function(target) { return `${target.nodeName} ${target.type} ${target.name} ${target.placeholder}`; } - if (target.classList && target.classList.length > 0) { + if (target.classList?.length > 0) { return `${target.nodeName} ${target.type} ${target.classList.value} ${target.placeholder}`; } @@ -285,11 +285,11 @@ kpxcFields.getIdFromProperties = function(target) { // Legacy unique ID generation for converting kpxcFields.getLegacyId = function(target) { - if (target.classList.length > 0) { + if (target.classList?.length > 0) { return `${target.nodeName} ${target.type} ${target.classList.value} ${target.name} ${target.placeholder}`; } - if (target.id && target.id !== '') { + if (target.id && target?.id !== '') { return `${target.nodeName} ${target.type} ${kpxcFields.prepareId(target.id)} ${target.name} ${target.placeholder}`; } @@ -316,14 +316,13 @@ kpxcFields.isCustomLoginFieldsUsed = function() { kpxcFields.isSearchForm = function(form) { // Check form action const formAction = form.getLowerCaseAttribute('action'); - if (formAction && (formAction.includes('search') && !formAction.includes('research'))) { + if (formAction?.includes('search') && !formAction?.includes('research')) { return true; } // Ignore form with search classes const formId = form.getLowerCaseAttribute('id'); - if (form.className && (form.className.includes('search') - || (formId && formId.includes('search') && !formId.includes('research')))) { + if (form.className?.includes('search') || (formId?.includes('search') && !formId?.includes('research'))) { return true; } diff --git a/keepassxc-browser/content/fill.js b/keepassxc-browser/content/fill.js index 5353a7c..8f7cb02 100644 --- a/keepassxc-browser/content/fill.js +++ b/keepassxc-browser/content/fill.js @@ -114,12 +114,12 @@ kpxcFill.fillFromTOTP = async function(target) { const el = target || document.activeElement; const credentialList = await kpxc.updateTOTPList(); - if (credentialList && credentialList.length === 0) { + if (credentialList?.length === 0) { kpxcUI.createNotification('warning', tr('credentialsNoTOTPFound')); return; } - if (credentialList && credentialList.length === 1) { + if (credentialList?.length === 1) { kpxcFill.fillTOTPFromUuid(el, credentialList[0].uuid); return; } @@ -140,7 +140,7 @@ kpxcFill.fillTOTPFromUuid = async function(el, uuid) { return; } - if (user.totp && user.totp.length > 0) { + if (user.totp?.length > 0) { // Retrieve a new TOTP value const totp = await sendMessage('get_totp', [ user.uuid, user.totp ]); if (!totp) { @@ -149,7 +149,7 @@ kpxcFill.fillTOTPFromUuid = async function(el, uuid) { } kpxcFill.setTOTPValue(el, totp); - } else if (user.stringFields && user.stringFields.length > 0) { + } else if (user.stringFields?.length > 0) { const stringFields = user.stringFields; for (const s of stringFields) { const val = s['KPH: {TOTP}']; @@ -168,7 +168,7 @@ kpxcFill.setTOTPValue = function(elem, val) { } for (const comb of kpxc.combinations) { - if (comb.totpInputs && comb.totpInputs.length > 0) { + if (comb.totpInputs?.length > 0) { kpxcFill.fillSegmentedTotp(elem, val, comb.totpInputs); return; } @@ -223,10 +223,9 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui // Use predefined username as default let usernameValue = predefinedUsername; - if (!usernameValue) { - // With single password field the combination.password is used instead - usernameValue = combination.username ? combination.username.value : combination.password.value; - } + + // With single password field the combination.password is used instead + usernameValue ??= combination.username ? combination.username.value : combination.password.value; // Find the correct credentials const selectedCredentials = kpxc.credentials.find(c => c.uuid === uuid); @@ -262,7 +261,7 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui } // Fill StringFields - if (selectedCredentials.stringFields && selectedCredentials.stringFields.length > 0) { + if (selectedCredentials.stringFields?.length > 0) { kpxcFill.fillInStringFields(combination.fields, selectedCredentials.stringFields); } @@ -278,7 +277,7 @@ kpxcFill.fillInCredentials = async function(combination, predefinedUsername, uui // Fills StringFields defined in Custom Fields kpxcFill.fillInStringFields = function(fields, stringFields) { const filledInFields = []; - if (fields && stringFields && fields.length > 0 && stringFields.length > 0) { + if (fields && stringFields && fields?.length > 0 && stringFields?.length > 0) { for (let i = 0; i < fields.length; i++) { if (i >= stringFields.length) { continue; diff --git a/keepassxc-browser/content/form.js b/keepassxc-browser/content/form.js index b693f76..3e4c9bc 100644 --- a/keepassxc-browser/content/form.js +++ b/keepassxc-browser/content/form.js @@ -51,7 +51,7 @@ kpxcForm.getFormSubmitButton = function(form) { // If any formaction overriding the default action is set, ignore those buttons. const buttons = Array.from(form.querySelectorAll(kpxcForm.formButtonQuery)).filter(b => !b.getAttribute('formAction')); if (buttons.length > 0) { - return buttons[buttons.length - 1]; + return buttons.at(-1); } // Try to find similar buttons outside the form which are added via 'form' property diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index 067a196..2d1c43c 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -153,7 +153,7 @@ kpxc.getFormActionUrl = function(combination) { } let action = null; - if (combination.form && combination.form.length > 0) { + if (combination.form?.length > 0) { action = combination.form.action; } @@ -386,13 +386,13 @@ kpxc.initLoginPopup = function() { // Initialize login items const loginItems = []; - for (let i = 0; i < kpxc.credentials.length; i++) { - const loginItem = getLoginItem(kpxc.credentials[i], showGroupNameInAutocomplete, i); + for (const [ i, login ] of kpxc.credentials.entries()) { + const loginItem = getLoginItem(login, showGroupNameInAutocomplete, i); // Ignore a duplicate entry if the password is empty, but there's already a similar entry with a password. // An usual use case with TOTP in a separate database. const similarEntryFound = kpxc.credentials.some(c => c.password !== '' && c.login === loginItem.login); - if (kpxc.credentials[i].password === '' && similarEntryFound) { + if (login.password === '' && similarEntryFound) { continue; } @@ -586,7 +586,7 @@ kpxc.retrieveCredentials = async function(force = false) { // Handles credentials from 'retrieve_credentials' response kpxc.retrieveCredentialsCallback = async function(credentials) { _called.retrieveCredentials = true; - if (credentials && credentials.length > 0) { + if (credentials?.length > 0) { kpxc.credentials = credentials; await kpxc.prepareCredentials(); } @@ -599,7 +599,7 @@ kpxc.retrieveCredentialsCallback = async function(credentials) { // Retrieve submitted credentials if available const creds = await sendMessage('page_get_submitted'); - if (creds && creds.submitted) { + if (creds?.submitted) { await sendMessage('page_clear_submitted'); kpxc.rememberCredentials(creds.username, creds.password, creds.url, creds.oldCredentials); } diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index 8a99aea..3192d35 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -54,8 +54,8 @@ kpxcTOTPIcons.isAcceptedTOTPField = function(field) { const placeholder = field.getLowerCaseAttribute('placeholder'); // Checks if the field id, name or placeholder includes some of the acceptedOTPFields but not any from ignoredTypes - if ((acceptedOTPFields.some(f => (id && id.includes(f)) || (name && name.includes(f) || placeholder && placeholder.includes(f))) || acceptedParents.some(s => field.closest(s))) - && !ignoredTypes.some(f => (id && id.includes(f)) || (name && name.includes(f) || placeholder && placeholder.includes(f)))) { + if ((acceptedOTPFields.some(f => id?.includes(f) || (name?.includes(f) || placeholder?.includes(f))) || acceptedParents.some(s => field.closest(s))) + && !ignoredTypes.some(f => id?.includes(f) || (name?.includes(f) || placeholder?.includes(f)))) { return true; } diff --git a/keepassxc-browser/content/ui.js b/keepassxc-browser/content/ui.js index 382917a..58104bb 100644 --- a/keepassxc-browser/content/ui.js +++ b/keepassxc-browser/content/ui.js @@ -7,6 +7,12 @@ const MIN_INPUT_FIELD_OFFSET_WIDTH = 60; const MIN_OPACITY = 0.7; const MAX_OPACITY = 1; +const BLUE_BUTTON = 'kpxc-button kpxc-blue-button'; +const GREEN_BUTTON = 'kpxc-button kpxc-green-button'; +const ORANGE_BUTTON = 'kpxc-button kpxc-orange-button'; +const RED_BUTTON = 'kpxc-button kpxc-red-button'; +const GRAY_BUTTON_CLASS = 'kpxc-gray-button'; + const DatabaseState = { DISCONNECTED: 0, LOCKED: 1, @@ -107,7 +113,7 @@ kpxcUI.monitorIconPosition = function(iconClass) { }); window.addEventListener('transitionend', function(e) { - if (e.target && (e.target.nodeName === 'INPUT' || e.target.nodeName === 'TEXTAREA')) { + if (e.target?.nodeName === 'INPUT' || e.target?.nodeName === 'TEXTAREA') { kpxcUI.updateIconPosition(iconClass); } }); diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index 5270d35..863c950 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -158,7 +158,7 @@ "applications": { "gecko": { "id": "keepassxc-browser@keepassxc.org", - "strict_min_version": "74.0" + "strict_min_version": "91.0" } }, "default_locale": "en" diff --git a/keepassxc-browser/popups/popup.js b/keepassxc-browser/popups/popup.js index cef6dc0..af022a8 100644 --- a/keepassxc-browser/popups/popup.js +++ b/keepassxc-browser/popups/popup.js @@ -61,12 +61,12 @@ function statusResponse(r) { } const sendMessageToTab = async function(message) { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length === 0) { + const tab = await getCurrentTab(); + if (!tab) { return false; // Only the background devtools or a popup are opened } - await browser.tabs.sendMessage(tabs[0].id, { + await browser.tabs.sendMessage(tab.id, { action: message }); diff --git a/keepassxc-browser/popups/popup_functions.js b/keepassxc-browser/popups/popup_functions.js index ac2c5ea..c21a9bd 100644 --- a/keepassxc-browser/popups/popup_functions.js +++ b/keepassxc-browser/popups/popup_functions.js @@ -25,11 +25,10 @@ async function initSettings() { customLoginFieldsButton.addEventListener('click', async () => { await browser.windows.getCurrent(); - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - const tab = tabs[0]; + const tab = await getCurrentTab(); await browser.runtime.getBackgroundPage(); - browser.tabs.sendMessage(tab.id, { + browser.tabs.sendMessage(tab?.id, { action: 'choose_credential_fields' }); close(); @@ -49,8 +48,8 @@ async function initColorTheme() { } async function getLoginData() { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length === 0) { + const tab = getCurrentTab(); + if (!tab) { return []; } diff --git a/keepassxc-browser/popups/popup_httpauth.js b/keepassxc-browser/popups/popup_httpauth.js index 1950ef9..0a37bfd 100644 --- a/keepassxc-browser/popups/popup_httpauth.js +++ b/keepassxc-browser/popups/popup_httpauth.js @@ -8,10 +8,11 @@ const data = await getLoginData(); const ll = document.getElementById('login-list'); - for (let i = 0; i < data.logins.length; ++i) { + + for (const [ i, login ] of data.logins.entries()) { const a = document.createElement('a'); a.setAttribute('class', 'list-group-item'); - a.textContent = data.logins[i].login + ' (' + data.logins[i].name + ')'; + a.textContent = login.login + ' (' + login.name + ')'; a.setAttribute('id', '' + i); a.addEventListener('click', (e) => { diff --git a/keepassxc-browser/popups/popup_login.js b/keepassxc-browser/popups/popup_login.js index c6604d1..b46c460 100644 --- a/keepassxc-browser/popups/popup_login.js +++ b/keepassxc-browser/popups/popup_login.js @@ -6,18 +6,18 @@ $('#lock-database-button').show(); - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length === 0) { + const tab = await getCurrentTab(); + if (!tab) { return []; } const logins = await getLoginData(); const ll = document.getElementById('login-list'); - for (let i = 0; i < logins.length; i++) { - const uuid = logins[i].uuid; + for (const [ i, login ] of logins.entries()) { + const uuid = login.uuid; const a = document.createElement('a'); - a.textContent = logins[i].text; + a.textContent = login.text; a.setAttribute('class', 'list-group-item'); a.setAttribute('id', '' + i); @@ -27,7 +27,7 @@ } const id = e.target.id; - browser.tabs.sendMessage(tabs[0].id, { + browser.tabs.sendMessage(tab.id, { action: 'fill_user_pass_with_specific_login', id: Number(id), uuid: uuid