diff --git a/.eslintrc b/.eslintrc index 8bf1e162..fd5e6f05 100644 --- a/.eslintrc +++ b/.eslintrc @@ -120,6 +120,8 @@ "importScripts": "readonly", "IMPROVED_DETECTION_PREDEFINED_SITELIST": "readonly", "initColorTheme": "readonly", + "initializeHttpAuthLoginList": "readonly", + "initializeLoginList": "readonly", "isEdge": "readonly", "isFirefox": "readonly", "keepass": "readonly", @@ -162,9 +164,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/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index e1f18312..da99c914 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) { - browserActionWrapper.setPopup({ - tabId: tab.id, - popup: `popups/${popupData.popup}.html` - }); - let badgeText = ''; - if (popupData.popup === 'popup_login') { + if (popupData.popup === PopupState.LOGIN) { badgeText = String(page.tabs[tab.id]?.loginList?.length); - } else if (popupData.popup === 'popup_httpauth') { + } else if (popupData.popup === PopupState.HTTP_AUTH) { badgeText = String(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, String(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, String(page.tabs[tab.id]?.loginList?.logins?.length)); + } + + await browserAction.updatePopupIcon(tab, popupData); }; browserAction.updateIcon = async function(tab, iconType) { @@ -84,7 +87,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 c98b01de..b6f0d2d0 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 57f848a8..910cef05 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,12 +154,12 @@ kpxcEvent.onRemoveCredentialsFromTabInformation = async function(tab) { kpxcEvent.onLoginPopup = async function(tab, logins) { const popupData = { - iconType: 'normal', - popup: 'popup_login' + iconType: PopupIcon.NORMAL, + popup: PopupState.LOGIN }; page.tabs[tab.id].loginList = logins; - await browserAction.show(tab, popupData); + await browserAction.updatePopupIcon(tab, popupData); }; kpxcEvent.initHttpAuth = async function() { @@ -168,12 +168,12 @@ 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); + 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 add144c0..e578d445 100644 --- a/keepassxc-browser/background/init.js +++ b/keepassxc-browser/background/init.js @@ -80,7 +80,7 @@ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { } if (changeInfo.status === 'complete') { - browserAction.showDefault(tab); + browserAction.updatePopup(tab); if (!page.tabs[tab.id]) { page.createTabEntry(tab.id); } diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 69d2200f..1d96eb31 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 []; } @@ -625,7 +625,7 @@ keepass.passkeysRegister = async function(tab, args = []) { return response; } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`passkeysRegister failed: ${err}`); @@ -637,7 +637,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 []; } @@ -658,7 +658,7 @@ keepass.passkeysGet = async function(tab, args = []) { return response; } - browserAction.showDefault(tab); + browserAction.updatePopup(tab); return []; } catch (err) { logError(`passkeysGet failed: ${err}`); @@ -912,7 +912,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 ca6ba9a2..83da254a 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -53,8 +53,8 @@ page.submittedCredentials = {}; page.tabs = []; page.popupData = { - iconType: 'normal', - popup: 'popup' + iconType: PopupIcon.NORMAL, + popup: PopupState.DEFAULT }; page.initSettings = async function() { @@ -92,7 +92,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(); @@ -128,7 +128,7 @@ page.switchTab = async function(tab) { } }, page.settings.clearCredentialsTimeout * 1000); - browserAction.showDefault(tab); + browserAction.updatePopup(tab); browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => { logError('Cannot send activated_tab message: ' + e.message); }); @@ -334,7 +334,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 45fcbfb8..f424553f 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -26,6 +26,34 @@ const URL_WILDCARD = '1kpxcwc1'; const schemeSegment = '(\\*|http|https|ws|wss|ftp)'; const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?'; +const AssociatedAction = { + NOT_ASSOCIATED: 0, + ASSOCIATED: 1, + NEW_ASSOCIATION: 2, + CANCELED: 3 +}; + +const ManualFill = { + NONE: 0, + PASSWORD: 1, + 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; }; @@ -43,19 +71,6 @@ const showNotification = function(message) { }); }; -const AssociatedAction = { - NOT_ASSOCIATED: 0, - ASSOCIATED: 1, - NEW_ASSOCIATION: 2, - CANCELED: 3 -}; - -const ManualFill = { - NONE: 0, - PASSWORD: 1, - BOTH: 2 -}; - // Checks if element's nodeName matches const matchesWithNodeName = function(elem, name) { // Don't allow undefined element or 'name' @@ -156,6 +171,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 1e01b9f6..c0f6392b 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/popups/popup.html b/keepassxc-browser/popups/popup.html index 7f842d0c..3d63f974 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 @@
+