diff --git a/.eslintrc b/.eslintrc index 5ddc93e61..b43c55c69 100644 --- a/.eslintrc +++ b/.eslintrc @@ -122,6 +122,8 @@ "importScripts": "readonly", "IMPROVED_DETECTION_PREDEFINED_SITELIST": "readonly", "initColorTheme": "readonly", + "initializeHttpAuthLoginList": "readonly", + "initializeLoginList": "readonly", "isEdge": "readonly", "isFirefox": "readonly", "keepass": "readonly", @@ -164,9 +166,12 @@ "ORANGE_BUTTON": "readonly", "page": "readonly", "Pixels": "readonly", - "PublicKeyCredential": "readonly", + "PopupIcon": "readonly", + "PopupState": "readonly", "PREDEFINED_SITELIST": "readonly", + "PublicKeyCredential": "readonly", "RED_BUTTON": "readonly", + "removeAllChildren": "readonly", "retrieveColorScheme": "readonly", "sendMessage": "readonly", "showNotification": "readonly", diff --git a/dist/manifest_chromium.json b/dist/manifest_chromium.json index 25990f375..a7b1a16cb 100755 --- a/dist/manifest_chromium.json +++ b/dist/manifest_chromium.json @@ -118,7 +118,7 @@ "choose_credential_fields": { "description": "__MSG_popupChooseCredentialsText__" }, - "retrive_credentials_forced": { + "retrieve_credentials_forced": { "description": "__MSG_popupReopenButton__" }, "request_autotype": { diff --git a/dist/manifest_firefox.json b/dist/manifest_firefox.json index 9e90477b9..1d3918be0 100644 --- a/dist/manifest_firefox.json +++ b/dist/manifest_firefox.json @@ -131,7 +131,7 @@ "choose_credential_fields": { "description": "__MSG_popupChooseCredentialsText__" }, - "retrive_credentials_forced": { + "retrieve_credentials_forced": { "description": "__MSG_popupReopenButton__" }, "request_autotype": { diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index 1a28354b7..423dee752 100755 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -3,7 +3,7 @@ const browserActionWrapper = browser.action || browser.browserAction; const browserAction = {}; -browserAction.show = async function(tab, popupData) { +browserAction.updatePopupIcon = async function(tab, popupData) { popupData ??= page.popupData; page.popupData = popupData; @@ -12,15 +12,10 @@ browserAction.show = async function(tab, popupData) { }); if (popupData.popup && tab?.id) { - browserActionWrapper.setPopup({ - tabId: tab.id, - popup: `popups/${popupData.popup}.html` - }); - let badgeText = ''; - if (popupData.popup === 'popup_login') { + if (popupData.popup === PopupState.LOGIN) { badgeText = page.tabs[tab.id]?.loginList?.length; - } else if (popupData.popup === 'popup_httpauth') { + } else if (popupData.popup === PopupState.HTTP_AUTH) { badgeText = page.tabs[tab.id]?.loginList?.logins?.length; } @@ -28,10 +23,10 @@ browserAction.show = async function(tab, popupData) { } }; -browserAction.showDefault = async function(tab) { +browserAction.updatePopup = async function(tab) { const popupData = { - iconType: 'normal', - popup: 'popup' + iconType: PopupIcon.NORMAL, + popup: PopupState.DEFAULT }; const response = await keepass.isConfigured().catch((err) => { @@ -39,11 +34,11 @@ browserAction.showDefault = async function(tab) { }); if (!response && !keepass.isKeePassXCAvailable) { - popupData.iconType = 'cross'; + popupData.iconType = PopupIcon.CROSS; } else if (!keepass.isAssociated() && !keepass.isDatabaseClosed) { - popupData.iconType = 'bang'; + popupData.iconType = PopupIcon.BANG; } else if (keepass.isKeePassXCAvailable && keepass.isDatabaseClosed) { - popupData.iconType = 'locked'; + popupData.iconType = PopupIcon.LOCKED; } // Get the current tab if no tab given @@ -52,13 +47,21 @@ browserAction.showDefault = async function(tab) { return; } + // Credentials are available if (page?.tabs[tab.id]?.loginList.length > 0) { - popupData.iconType = 'normal'; - popupData.popup = 'popup_login'; + popupData.iconType = PopupIcon.NORMAL; + popupData.popup = PopupState.LOGIN; browserAction.setBadgeText(tab?.id, page.tabs[tab.id]?.loginList.length); } - await browserAction.show(tab, popupData); + // HTTP Basic Auth credentials are available + if (page.tabs[tab.id]?.loginList?.logins?.length > 0) { + popupData.iconType = PopupIcon.NORMAL; + popupData.popup = PopupState.HTTP_AUTH; + browserAction.setBadgeText(tab?.id, page.tabs[tab.id]?.loginList?.logins?.length); + } + + await browserAction.updatePopupIcon(tab, popupData); }; browserAction.setBadgeText = function(tabId, badgeText) { @@ -73,7 +76,7 @@ browserAction.setBadgeText = function(tabId, badgeText) { browserAction.generateIconName = async function(iconType) { let name = 'icon_'; name += (await keepass.keePassXCUpdateAvailable()) ? 'new_' : ''; - name += (!iconType || iconType === 'normal') ? 'normal' : iconType; + name += (!iconType || iconType === PopupIcon.NORMAL) ? PopupIcon.NORMAL : iconType; let style = 'colored'; if (page?.settings?.useMonochromeToolbarIcon) { diff --git a/keepassxc-browser/background/client.js b/keepassxc-browser/background/client.js index c98b01dec..b6f0d2d01 100644 --- a/keepassxc-browser/background/client.js +++ b/keepassxc-browser/background/client.js @@ -398,7 +398,7 @@ function onDisconnected() { keepass.databaseHash = ''; page.clearAllLogins(); - keepass.updatePopup('cross'); + keepass.updatePopup(PopupIcon.CROSS); keepass.updateDatabaseHashToContent(); logError(`Failed to connect: ${(browser.runtime.lastError === null ? 'Unknown error' : browser.runtime.lastError.message)}`); } diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index a6d30555a..d285de9b4 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -21,7 +21,7 @@ kpxcEvent.showStatus = async function(tab, configured, internalPoll) { } if (!internalPoll) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); } const errorMessage = page.tabs[tab.id]?.errorMessage ?? undefined; @@ -30,7 +30,6 @@ kpxcEvent.showStatus = async function(tab, configured, internalPoll) { return { associated: keepass.isAssociated(), - configured: configured, databaseClosed: keepass.isDatabaseClosed, encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized, @@ -38,6 +37,7 @@ kpxcEvent.showStatus = async function(tab, configured, internalPoll) { iframeDetected: iframeDetected, identifier: keyId, keePassXCAvailable: keepass.isKeePassXCAvailable, + popupData: page.popupData, showGettingStartedGuideAlert: page.settings.showGettingStartedGuideAlert, showTroubleshootingGuideAlert: page.settings.showTroubleshootingGuideAlert, usernameFieldDetected: usernameFieldDetected @@ -154,13 +154,13 @@ kpxcEvent.onRemoveCredentialsFromTabInformation = async function(tab) { kpxcEvent.onLoginPopup = async function(tab, logins) { const popupData = { - iconType: 'normal', - popup: 'popup_login' + iconType: PopupIcon.NORMAL, + popup: PopupState.LOGIN }; if (tab?.id) { page.tabs[tab.id].loginList = logins; - await browserAction.show(tab, popupData); + await browserAction.updatePopupIcon(tab, popupData); } }; @@ -170,12 +170,14 @@ kpxcEvent.initHttpAuth = async function() { kpxcEvent.onHTTPAuthPopup = async function(tab, data) { const popupData = { - iconType: 'normal', - popup: 'popup_httpauth' + iconType: PopupIcon.NORMAL, + popup: PopupState.HTTP_AUTH }; - page.tabs[tab.id].loginList = data; - await browserAction.show(tab, popupData); + if (tab?.id) { + page.tabs[tab.id].loginList = data; + await browserAction.updatePopupIcon(tab, popupData); + } }; kpxcEvent.onUsernameFieldDetected = async function(tab, detected) { diff --git a/keepassxc-browser/background/init.js b/keepassxc-browser/background/init.js index 73b2550d3..a819d5b89 100644 --- a/keepassxc-browser/background/init.js +++ b/keepassxc-browser/background/init.js @@ -82,7 +82,7 @@ const initListeners = async function() { } if (changeInfo.status === 'complete' && tab?.id) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); if (!page.tabs[tab.id]) { page.createTabEntry(tab.id); } @@ -115,7 +115,7 @@ const initListeners = async function() { if (contextMenuItems.some(e => e.action === command) || command === 'redetect_fields' || command === 'choose_credential_fields' - || command === 'retrive_credentials_forced' + || command === 'retrieve_credentials_forced' || command === 'reload_extension') { const tab = await getCurrentTab(); if (tab?.id) { diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index ae6ec86ae..7e9cdcfd1 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -55,7 +55,7 @@ keepass.updateCredentials = async function(tab, args = []) { const [ entryId, username, password, url, group, groupUuid ] = args; const taResponse = await keepass.testAssociation(tab); if (!taResponse) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } @@ -108,7 +108,7 @@ keepass.retrieveCredentials = async function(tab, args = []) { const [ url, submiturl, triggerUnlock = false, httpAuth = false ] = args; const taResponse = await keepass.testAssociation(tab, [ false, triggerUnlock ]); if (!taResponse) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } @@ -145,14 +145,14 @@ keepass.retrieveCredentials = async function(tab, args = []) { if (entries.length === 0) { // Questionmark-icon is not triggered, so we have to trigger for the normal symbol - browserAction.showDefault(tab); + browserAction.updatePopup(tab); } logDebug(`Found ${entries.length} entries for url ${url}`); return entries; } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`retrieveCredentials failed: ${err}`); @@ -168,7 +168,7 @@ keepass.generatePassword = async function(tab) { try { const taResponse = await keepass.testAssociation(tab); if (!taResponse) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return ''; } @@ -235,7 +235,7 @@ keepass.associate = async function(tab) { keepass.associated.value = true; keepass.associated.hash = response.hash || 0; - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return AssociatedAction.NEW_ASSOCIATION; } @@ -468,7 +468,7 @@ keepass.getDatabaseGroups = async function(tab) { try { const taResponse = await keepass.testAssociation(tab, [ false ]); if (!taResponse) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } @@ -495,7 +495,7 @@ keepass.getDatabaseGroups = async function(tab) { return groups; } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`getDatabaseGroups failed: ${err}`); @@ -508,7 +508,7 @@ keepass.createNewGroup = async function(tab, args = []) { const [ groupName ] = args; const taResponse = await keepass.testAssociation(tab, [ false ]); if (!taResponse) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } @@ -534,7 +534,7 @@ keepass.createNewGroup = async function(tab, args = []) { logError('getDatabaseGroups rejected'); } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`createNewGroup failed: ${err}`); @@ -602,7 +602,7 @@ keepass.passkeysRegister = async function(tab, args = []) { try { const taResponse = await keepass.testAssociation(tab, [ false ]); if (!taResponse || !keepass.isConnected || args.length < 2) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } @@ -623,7 +623,7 @@ keepass.passkeysRegister = async function(tab, args = []) { return response; } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`passkeysRegister failed: ${err}`); @@ -635,7 +635,7 @@ keepass.passkeysGet = async function(tab, args = []) { try { const taResponse = await keepass.testAssociation(tab, [ false ]); if (!taResponse || !keepass.isConnected || args.length < 2) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } @@ -656,7 +656,7 @@ keepass.passkeysGet = async function(tab, args = []) { return response; } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`passkeysGet failed: ${err}`); @@ -851,7 +851,11 @@ keepass.checkDatabaseHash = async function(tab) { }; keepass.isAssociated = function() { - return (keepass.associated.value && keepass.associated.hash && keepass.associated.hash === keepass.databaseHash); + if (!keepass.associated.value || keepass.associated.hash === '') { + return false; + } + + return keepass.associated.hash === keepass.databaseHash; }; keepass.setcurrentKeePassXCVersion = function(version) { @@ -910,7 +914,7 @@ keepass.handleError = function(tab, errorCode, errorMessage = '') { keepass.updatePopup = function() { if (page && page.tabs.length > 0) { - browserAction.showDefault(); + browserAction.updatePopup(); } }; diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 5a9f8a030..d5d3456a2 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -56,8 +56,8 @@ page.submittedCredentials = {}; page.tabs = []; page.popupData = { - iconType: 'normal', - popup: 'popup' + iconType: PopupIcon.NORMAL, + popup: PopupState.DEFAULT }; page.initSettings = async function() { @@ -118,7 +118,7 @@ page.initOpenedTabs = async function() { } page.currentTabId = currentTab?.id; - browserAction.showDefault(currentTab); + browserAction.updatePopup(currentTab); } catch (err) { logError('page.initOpenedTabs error: ' + err); return Promise.reject(); @@ -154,7 +154,7 @@ page.switchTab = async function(tab) { } }, page.settings.clearCredentialsTimeout * 1000); - browserAction.showDefault(tab); + browserAction.updatePopup(tab); if (tab?.id) { browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => { logError('Cannot send activated_tab message: ' + e.message); @@ -397,7 +397,7 @@ page.updateContextMenu = async function(tab, credentials) { }; page.updatePopup = function(tab) { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); }; page.setAllowIframes = async function(tab, args = []) { diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 55de510e7..03eac4eed 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -26,23 +26,6 @@ const URL_WILDCARD = '1kpxcwc1'; const schemeSegment = '(\\*|http|https|ws|wss|ftp)'; const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?'; -const isFirefox = function() { - return navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1; -}; - -const isEdge = function() { - return navigator.userAgent.indexOf('Edg') !== -1; -}; - -const showNotification = function(message) { - browser.notifications.create({ - 'type': 'basic', - 'iconUrl': browser.runtime.getURL('icons/keepassxc_64x64.png'), - 'title': 'KeePassXC-Browser', - 'message': message - }); -}; - const AssociatedAction = { NOT_ASSOCIATED: 0, ASSOCIATED: 1, @@ -61,6 +44,38 @@ const ManualFill = { BOTH: 2 }; +// Popup icon types +const PopupIcon = { + BANG: 'bang', + CROSS: 'cross', + LOCKED: 'locked', + NORMAL: 'normal' +}; + +// Popup states +const PopupState = { + DEFAULT: 'default', + HTTP_AUTH: 'http_auth', + LOGIN: 'login' +}; + +const isFirefox = function() { + return navigator.userAgent.indexOf('Firefox') !== -1 || navigator.userAgent.indexOf('Gecko/') !== -1; +}; + +const isEdge = function() { + return navigator.userAgent.indexOf('Edg') !== -1; +}; + +const showNotification = function(message) { + browser.notifications.create({ + 'type': 'basic', + 'iconUrl': browser.runtime.getURL('icons/keepassxc_64x64.png'), + 'title': 'KeePassXC-Browser', + 'message': message + }); +}; + const compareVersion = function(minimum, current, canBeEqual = true) { if (!minimum || !current || minimum?.indexOf('.') === -1 || current?.indexOf('.') === -1) { return false; @@ -182,6 +197,12 @@ const getCurrentTab = async function() { return tabs?.length > 0 ? tabs[0] : undefined; }; +const removeAllChildren = function(elem) { + while (elem?.hasChildNodes()) { + elem.removeChild(elem.lastChild); + } +}; + // Exports for tests if (typeof module === 'object') { module.exports = { diff --git a/keepassxc-browser/content/autocomplete.js b/keepassxc-browser/content/autocomplete.js index 4c98d4b00..6c5effa37 100644 --- a/keepassxc-browser/content/autocomplete.js +++ b/keepassxc-browser/content/autocomplete.js @@ -140,9 +140,7 @@ class Autocomplete { await kpxc.updateTOTPList(); // Clear the login items from div - while (this.list.hasChildNodes()) { - this.list.removeChild(this.list.lastChild); - } + removeAllChildren(this.list); // Update credentials to menu div for (const c of this.elements) { diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index 70ea565df..d4a352fd5 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -993,7 +993,7 @@ browser.runtime.onMessage.addListener(async function(req, sender) { sendMessage('reconnect'); } else if (req.action === 'save_credentials') { kpxc.rememberCredentialsFromContextMenu(); - } else if (req.action === 'retrive_credentials_forced') { + } else if (req.action === 'retrieve_credentials_forced') { await kpxc.retrieveCredentials(true); } else if (req.action === 'show_password_generator') { kpxcPasswordGenerator.showPasswordGenerator(); diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index 25990f375..a7b1a16cb 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -118,7 +118,7 @@ "choose_credential_fields": { "description": "__MSG_popupChooseCredentialsText__" }, - "retrive_credentials_forced": { + "retrieve_credentials_forced": { "description": "__MSG_popupReopenButton__" }, "request_autotype": { diff --git a/keepassxc-browser/popups/popup.html b/keepassxc-browser/popups/popup.html index 7f842d0c4..3d63f974c 100644 --- a/keepassxc-browser/popups/popup.html +++ b/keepassxc-browser/popups/popup.html @@ -18,6 +18,7 @@
+
+ +

@@ -121,6 +133,29 @@

+ + + + + + +

@@ -132,6 +167,7 @@
+