Update popup content dynamically

This commit is contained in:
varjolintu 2024-09-22 15:07:35 +03:00 committed by varjolintu
parent a2748198b0
commit 9e9314722b
16 changed files with 330 additions and 380 deletions

View file

@ -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",

View file

@ -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) {

View file

@ -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)}`);
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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}`);
@ -910,7 +910,7 @@ keepass.handleError = function(tab, errorCode, errorMessage = '') {
keepass.updatePopup = function() {
if (page && page.tabs.length > 0) {
browserAction.showDefault();
browserAction.updatePopup();
}
};

View file

@ -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 = []) {

View file

@ -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 = {

View file

@ -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) {

View file

@ -18,6 +18,7 @@
<body>
<div class="container">
<div id="settings" class="settings">
<!-- Buttons in the top area -->
<div class="action-buttons-area">
<button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText">
<i class="fa fa-cog" aria-hidden="true"></i>
@ -31,6 +32,7 @@
</div>
</div>
<!-- Alerts -->
<div id="popup-alerts">
<div id="update-available" class="alert alert-warning">
<span data-i18n="popupUpdateAvailable"></span>
@ -41,22 +43,28 @@
<div id="getting-started-guide" class="alert alert-info alert-dismissible">
<span data-i18n="popupGettingStartedText"></span>
<br>
<a target="_blank" class="alert-link" href="https://keepassxc.org/docs/KeePassXC_GettingStarted.html#_setup_browser_integration"><span data-i18n="popupGettingStartedLinkText"></span></a>
<a target="_blank" class="alert-link" href="https://keepassxc.org/docs/KeePassXC_GettingStarted.html#_setup_browser_integration">
<span data-i18n="popupGettingStartedLinkText"></span>
</a>
<button type="button" id="getting-started-alert-close-button" class="btn-close" data-bs-dismiss="alert" data-i18n="[title]popupAlertHide"></button>
</div>
<div id="troubleshooting-guide" class="alert alert-info alert-dismissible">
<span data-i18n="popupTroubleshootingText"></span>
<br>
<a target="_blank" class="alert-link" href="https://github.com/keepassxreboot/keepassxc-browser/wiki/Troubleshooting-guide"><span data-i18n="popupTroubleshootingLinkText"></span></a>
<a target="_blank" class="alert-link" href="https://github.com/keepassxreboot/keepassxc-browser/wiki/Troubleshooting-guide">
<span data-i18n="popupTroubleshootingLinkText"></span>
</a>
<button type="button" id="troubleshooting-guide-alert-close-button" class="btn-close" data-bs-dismiss="alert" data-i18n="[title]popupAlertHide"></button>
</div>
</div>
<!-- Loading -->
<div id="initial-state">
<div class="loader"></div> <p data-i18n="popupCheckingStatus"></p>
</div>
<!-- Database open but not connected -->
<div id="not-configured">
<p data-i18n="popupNotConfigured"></p>
<div class="right-align">
@ -67,6 +75,7 @@
</div>
</div>
<!-- Needs reconnect -->
<div id="need-reconfigure">
<p data-i18n="popupNeedReconfigure"></p>
<p id="left-margin">
@ -85,6 +94,7 @@
<span data-i18n="popupConfiguredNotAssociated" data-i18n-placeholder="<span class='bg-warning' id='unassociated-identifier'></span>"></span>
</div>
<!-- Database open and connected -->
<div id="configured-and-associated">
<p data-i18n="popupConfiguredAndAssociated" data-i18n-placeholder="<span class='bg-success' id='associated-identifier'></span>"></p>
<div class="right-align">
@ -95,6 +105,7 @@
</div>
</div>
<!-- Error message -->
<div id="error-encountered">
<p data-i18n="popupErrorEncountered"></p>
<p id="left-margin">
@ -108,6 +119,7 @@
</div>
</div>
<!-- Database not opened message -->
<div id="database-not-opened">
<p data-i18n="popupErrorEncountered"></p>
<p id="left-margin">
@ -121,6 +133,29 @@
</div>
</div>
<!-- Login list -->
<div id="credentials-list" class="credentials" style="display: none">
<p data-i18n="popupLoginText"></p>
<div id="filter-block">
<label for="login-filter" data-i18n="popupFilterText"></label>
<input type="text" id="login-filter">
</div>
<div id="login-list" class="list-group"></div>
</div>
<!-- HTTP Auth login list-->
<div id="http-auth-credentials-list" class="credentials" style="display: none">
<p data-i18n="popupAuthText"></p>
<div id="http-auth-login-list" class="list-group"></div>
<p>
<button id="btn-dismiss" class="btn btn-sm btn-danger" data-i18n="[title]dismissHttpAuthButtonTitle">
<i class="fa fa-remove" aria-hidden="true"></i>
<span data-i18n="popupButtonDismissHttpAuth"></span>
</button>
</p>
</div>
<!-- Username-only field detected message -->
<div id="username-field-detected">
<hr>
<p data-i18n="popupUsernameFieldDetected"></p>
@ -132,6 +167,7 @@
</div>
</div>
<!-- Iframe detected message -->
<div id="iframe-detected" style="display: none">
<hr>
<p data-i18n="popupIframeDetected"></p>

View file

@ -1,16 +1,10 @@
'use strict';
const POLLING_INTERVAL = 1000; // ms
let previousStatus;
let reloadCount = 0;
HTMLElement.prototype.show = function() {
this.style.display = 'block';
};
HTMLElement.prototype.hide = function() {
this.style.display = 'none';
};
function statusResponse(r) {
function hideAll() {
$('#initial-state').hide();
$('#error-encountered').hide();
$('#need-reconfigure').hide();
@ -20,49 +14,79 @@ function statusResponse(r) {
$('#lock-database-button').hide();
$('#getting-started-guide').hide();
$('#database-not-opened').hide();
$('#credentials-list').hide();
$('#http-auth-credentials-list').hide();
}
if (!r.keePassXCAvailable) {
$('#error-message').textContent = r.error;
function handleStatusResponse(response) {
hideAll();
// Error situations
if (!response.keePassXCAvailable) {
$('#error-message').textContent = response.error;
$('#error-encountered').show();
if (r.showGettingStartedGuideAlert) {
if (response.showGettingStartedGuideAlert) {
$('#getting-started-guide').show();
}
if (r.showTroubleshootingGuideAlert && reloadCount >= 2) {
if (response.showTroubleshootingGuideAlert && reloadCount >= 2) {
$('#troubleshooting-guide').show();
} else {
$('#troubleshooting-guide').hide();
}
} else if (r.keePassXCAvailable && r.databaseClosed) {
$('#database-error-message').textContent = r.error;
return;
} else if (response.keePassXCAvailable && response.databaseClosed) {
$('#database-error-message').textContent = response.error;
$('#database-not-opened').show();
} else if (!r.configured) {
return;
} else if (!response.configured) {
$('#not-configured').show();
} else if (r.encryptionKeyUnrecognized) {
return;
} else if (response.encryptionKeyUnrecognized) {
$('#need-reconfigure').show();
$('#need-reconfigure-message').textContent = r.error;
} else if (!r.associated) {
$('#need-reconfigure-message').textContent = response.error;
return;
} else if (!response.associated) {
$('#need-reconfigure').show();
$('#need-reconfigure-message').textContent = r.error;
} else if (r.error) {
$('#need-reconfigure-message').textContent = response.error;
return;
} else if (response.error) {
$('#error-encountered').show();
$('#error-message').textContent = r.error;
} else {
$('#configured-and-associated').show();
$('#associated-identifier').textContent = r.identifier;
$('#lock-database-button').show();
if (r.usernameFieldDetected) {
$('#username-field-detected').show();
}
if (r.iframeDetected) {
$('#iframe-detected').show();
}
reloadCount = 0;
$('#error-message').textContent = response.error;
return;
}
// Show the popup content based on status
if (response?.popupData?.popup === PopupState.LOGIN) {
$('#credentials-list').show();
$('#configured-and-associated').hide();
initializeLoginList();
} else if (response?.popupData?.popup === PopupState.HTTP_AUTH) {
$('#http-auth-credentials-list').show();
$('#configured-and-associated').hide();
initializeHttpAuthLoginList();
} else {
// PopupState.DEFAULT
$('#credentials-list').hide();
$('#http-auth-credentials-list').hide();
$('#configured-and-associated').show();
$('#associated-identifier').textContent = response.identifier;
}
$('#lock-database-button').show();
// Show button for adding Username-Only Detection for the site
if (response.usernameFieldDetected) {
$('#username-field-detected').show();
}
// Show button for allowing Cross-Origin IFrames for the site
if (response.iframeDetected) {
$('#iframe-detected').show();
}
reloadCount = 0;
}
const sendMessageToTab = async function(message) {
@ -99,7 +123,7 @@ const sendMessageToTab = async function(message) {
});
$('#reload-status-button').addEventListener('click', async () => {
statusResponse(await browser.runtime.sendMessage({
handleStatusResponse(await browser.runtime.sendMessage({
action: 'reconnect'
}));
@ -111,7 +135,7 @@ const sendMessageToTab = async function(message) {
});
$('#reopen-database-button').addEventListener('click', async () => {
statusResponse(await browser.runtime.sendMessage({
handleStatusResponse(await browser.runtime.sendMessage({
action: 'get_status',
args: [ false, true ] // Set forcePopup to true
}));
@ -124,13 +148,13 @@ const sendMessageToTab = async function(message) {
return;
}
statusResponse(await browser.runtime.sendMessage({
handleStatusResponse(await browser.runtime.sendMessage({
action: 'get_status'
}));
});
$('#lock-database-button').addEventListener('click', async () => {
statusResponse(await browser.runtime.sendMessage({
handleStatusResponse(await browser.runtime.sendMessage({
action: 'lock_database'
}));
});
@ -159,9 +183,37 @@ const sendMessageToTab = async function(message) {
});
});
statusResponse(await browser.runtime.sendMessage({
action: 'get_status'
}).catch((err) => {
logError('Could not get status: ' + err);
}));
// For HTTP Basic Auth
$('#btn-dismiss').addEventListener('click', async () => {
// Return empty credentials
browser.runtime.sendMessage({
action: 'fill_http_auth',
args: { login: '', password: '' }
});
close();
});
async function getNewStatus() {
return await browser.runtime.sendMessage({
action: 'get_status'
}).catch((err) => {
logError('Could not get status: ' + err);
});
}
// Get status right after popup has been opened
handleStatusResponse(await getNewStatus());
// Poll status
setInterval(async () => {
// Check if the popup state has been changed or database has been opened/closed
const currentStatus = await getNewStatus();
if (previousStatus?.popupData?.popup !== currentStatus?.popupData?.popup
|| previousStatus?.databaseClosed !== currentStatus?.databaseClosed
|| previousStatus?.keePassXCAvailable !== currentStatus?.keePassXCAvailable) {
previousStatus = currentStatus;
handleStatusResponse(currentStatus);
}
}, POLLING_INTERVAL);
})();

View file

@ -53,8 +53,103 @@ async function getLoginData() {
return logins;
}
// Initializes the default login list
async function initializeLoginList() {
const tab = await getCurrentTab();
if (!tab) {
return [];
}
const logins = await getLoginData();
const loginList = document.getElementById('login-list');
// Empty any existing values
removeAllChildren(loginList);
for (const [ i, login ] of logins.entries()) {
const a = document.createElement('a');
a.textContent = login.text;
a.setAttribute('class', 'list-group-item');
a.setAttribute('id', '' + i);
a.addEventListener('click', (e) => {
if (!e.isTrusted) {
return;
}
const id = e.target.id;
browser.tabs.sendMessage(tab?.id, {
action: 'fill_user_pass_with_specific_login',
id: Number(id),
uuid: login?.uuid
});
close();
});
loginList.appendChild(a);
}
if (logins.length > 1) {
$('#filter-block').show();
const filter = document.getElementById('login-filter');
filter.addEventListener('keyup', (e) => {
if (!e.isTrusted) {
return;
}
const val = filter.value;
const re = new RegExp(val, 'i');
const links = loginList.getElementsByTagName('a');
for (const i in links) {
if (links.hasOwnProperty(i)) {
const found = String(links[i].textContent).match(re) !== null;
links[i].style = found ? '' : 'display: none;';
}
}
});
filter.focus();
}
}
// Intitializes the login list for HTTP Basic Auth
async function initializeHttpAuthLoginList() {
const data = await getLoginData();
const loginList = document.getElementById('http-auth-login-list');
// Empty any existing values
removeAllChildren(loginList);
for (const [ i, login ] of data.logins.entries()) {
const a = document.createElement('a');
a.textContent = login.login + ' (' + login.name + ')';
a.setAttribute('class', 'list-group-item');
a.setAttribute('id', '' + i);
a.addEventListener('click', (e) => {
if (!e.isTrusted) {
return;
}
const credentials = data.logins[Number(e.target.id)];
browser.runtime.sendMessage({
action: 'fill_http_auth',
args: credentials
});
close();
});
loginList.appendChild(a);
}
}
(async () => {
if (document.readyState === 'complete' || (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
if (
document.readyState === 'complete' ||
(document.readyState !== 'loading' && !document.documentElement.doScroll)
) {
await initSettings();
} else {
document.addEventListener('DOMContentLoaded', initSettings);

View file

@ -1,65 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title data-i18n="popupTitle"></title>
<meta charset="UTF-8">
<link rel="stylesheet" href="../css/colors.css">
<link rel="stylesheet" href="popup.css">
<link rel="stylesheet" href="../bootstrap/bootstrap.min.css">
<link rel="stylesheet" href="../fonts/fork-awesome.min.css">
<script src="../common/browser-polyfill.min.js"></script>
<script src="../common/global.js"></script>
<script src="../common/global_ui.js"></script>
<script src="../bootstrap/bootstrap.min.js"></script>
<script src="popup_functions.js"></script>
<script defer src="popup_httpauth.js"></script>
<script defer src="../common/translate.js"></script>
</head>
<body>
<div class="container">
<div id="settings" class="settings">
<div class="function-buttons-area">
<button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText">
<i class="fa fa-cog" aria-hidden="true"></i>
</button>
<button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button>
</div>
<div class="lock-button-area">
<button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase">
<i class="fa fa-lock" aria-hidden="true"></i>
</button>
</div>
</div>
<div id="update-available" class="alert alert-warning">
<span data-i18n="popupUpdateAvailable"></span>
<br>
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
</div>
<div class="credentials">
<p data-i18n="popupAuthText"></p>
<div id="login-list" class="list-group"></div>
<p>
<button id="btn-dismiss" class="btn btn-sm btn-danger" data-i18n="[title]dismissHttpAuthButtonTitle">
<i class="fa fa-remove" aria-hidden="true"></i>
<span data-i18n="popupButtonDismissHttpAuth"></span>
</button>
</p>
</div>
<div id="database-not-opened">
<p data-i18n="popupErrorEncountered"></p>
<p style="margin-left: 1em">
<code id="database-error-message"></code>
</p>
<div class="right-align">
<button id="reopen-database-button" class="btn btn-sm btn-primary" data-i18n="[title]reopenDatabaseButtonTitle">
<i class="fa fa-lock" aria-hidden="true"></i>
<span data-i18n="popupReopenButton"></span>
</button>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,54 +0,0 @@
'use strict';
(async () => {
await initColorTheme();
$('#lock-database-button').show();
const data = await getLoginData();
const ll = document.getElementById('login-list');
for (const [ i, login ] of data.logins.entries()) {
const a = document.createElement('a');
a.setAttribute('class', 'list-group-item');
a.textContent = login.login + ' (' + login.name + ')';
a.setAttribute('id', '' + i);
a.addEventListener('click', (e) => {
if (!e.isTrusted) {
return;
}
const credentials = data.logins[Number(e.target.id)];
browser.runtime.sendMessage({
action: 'fill_http_auth',
args: credentials
});
close();
});
ll.appendChild(a);
}
$('#lock-database-button').addEventListener('click', function() {
browser.runtime.sendMessage({
action: 'lock_database'
});
$('.credentials').hide();
$('#btn-dismiss').hide();
$('#database-not-opened').show();
$('#lock-database-button').hide();
$('#database-error-message').textContent = tr('errorMessageDatabaseNotOpened');
});
$('#btn-dismiss').addEventListener('click', async () => {
// Return empty credentials
browser.runtime.sendMessage({
action: 'fill_http_auth',
args: { login: '', password: '' }
});
close();
});
})();

View file

@ -1,63 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title data-i18n="popupTitle"></title>
<meta charset="UTF-8">
<link rel="stylesheet" href="../css/colors.css">
<link rel="stylesheet" href="popup.css">
<link rel="stylesheet" href="../bootstrap/bootstrap.min.css">
<link rel="stylesheet" href="../fonts/fork-awesome.min.css">
<script src="../common/browser-polyfill.min.js"></script>
<script src="../common/global.js"></script>
<script src="../common/global_ui.js"></script>
<script src="../bootstrap/bootstrap.min.js"></script>
<script src="popup_functions.js"></script>
<script defer src="popup_login.js"></script>
<script defer src="../common/translate.js"></script>
</head>
<body>
<div class="container">
<div id="settings" class="settings">
<div class="function-buttons-area">
<button id="options-button" class="btn btn-sm btn-success" data-i18n="[title]popupSettingsText">
<i class="fa fa-cog" aria-hidden="true"></i>
</button>
<button id="choose-custom-login-fields-button" class="btn btn-sm btn-warning" data-i18n="[title]popupChooseCredentialsText"></button>
</div>
<div class="lock-button-area">
<button id="lock-database-button" class="btn btn-sm btn-danger" data-i18n="[title]lockDatabase">
<i class="fa fa-lock" aria-hidden="true"></i>
</button>
</div>
</div>
<div id="update-available" class="alert alert-warning">
<span data-i18n="popupUpdateAvailable"></span>
<br>
<a target="_blank" class="alert-link" href="https://keepassxc.org/download"><span data-i18n="popupDownloadNewVersion"></span></a>.
</div>
<div id="credentialsList" class="credentials">
<p data-i18n="popupLoginText"></p>
<div id="filter-block">
<label for="login-filter" data-i18n="popupFilterText"></label>
<input type="text" id="login-filter">
</div>
<div id="login-list" class="list-group"></div>
</div>
<div id="database-not-opened">
<p data-i18n="popupErrorEncountered"></p>
<p style="margin-left: 1em">
<code id="database-error-message"></code>
</p>
<div class="right-align">
<button id="reopen-database-button" class="btn btn-sm btn-primary" data-i18n="[title]reopenDatabaseButtonTitle">
<i class="fa fa-lock" aria-hidden="true"></i>
<span data-i18n="popupReopenButton"></span>
</button>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,80 +0,0 @@
'use strict';
(async () => {
await initColorTheme();
$('#lock-database-button').show();
const tab = await getCurrentTab();
if (!tab) {
return [];
}
const logins = await getLoginData();
const ll = document.getElementById('login-list');
for (const [ i, login ] of logins.entries()) {
const uuid = login.uuid;
const a = document.createElement('a');
a.textContent = login.text;
a.setAttribute('class', 'list-group-item');
a.setAttribute('id', '' + i);
a.addEventListener('click', (e) => {
if (!e.isTrusted) {
return;
}
const id = e.target.id;
browser.tabs.sendMessage(tab?.id, {
action: 'fill_user_pass_with_specific_login',
id: Number(id),
uuid: uuid
});
close();
});
ll.appendChild(a);
}
if (logins.length > 1) {
$('#filter-block').show();
const filter = document.getElementById('login-filter');
filter.addEventListener('keyup', (e) => {
if (!e.isTrusted) {
return;
}
const val = filter.value;
const re = new RegExp(val, 'i');
const links = ll.getElementsByTagName('a');
for (const i in links) {
if (links.hasOwnProperty(i)) {
const found = String(links[i].textContent).match(re) !== null;
links[i].style = found ? '' : 'display: none;';
}
}
});
filter.focus();
}
$('#lock-database-button').addEventListener('click', (e) => {
browser.runtime.sendMessage({
action: 'lock_database'
});
$('#credentialsList').hide();
$('#database-not-opened').show();
$('#lock-database-button').hide();
$('#database-error-message').textContent = tr('errorMessageDatabaseNotOpened');
});
$('#reopen-database-button').addEventListener('click', (e) => {
browser.runtime.sendMessage({
action: 'get_status',
args: [ false, true ] // Set forcePopup to true
});
});
})();