mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Merge pull request #351 from keepassxreboot/ux_improvements
UX improvements
This commit is contained in:
commit
2977a75c0f
34 changed files with 2851 additions and 2178 deletions
|
|
@ -21,16 +21,12 @@
|
|||
},
|
||||
"contextMenuSaveCredentials": {
|
||||
"message": "Save credentials",
|
||||
"description": "Save credentials using the extension popup icon."
|
||||
"description": "Save credentials using the credential banner."
|
||||
},
|
||||
"contextMenuShowPasswordGenerator": {
|
||||
"message": "Show password generator",
|
||||
"message": "Show Password Generator",
|
||||
"description": "Show password generator text."
|
||||
},
|
||||
"rememberCredentialsPopup": {
|
||||
"message": "Create or modify the credentials by clicking on the extension icon.",
|
||||
"description": "Notification text when saving credentials from the context menu."
|
||||
},
|
||||
"multipleCredentialsDetected": {
|
||||
"message": "HTTP authentication with multiple credentials detected. Click on the extension icon to choose the correct one.",
|
||||
"description": "Notification when HTTP Authentication has multiple credentials detected."
|
||||
|
|
@ -99,6 +95,10 @@
|
|||
"message": "No logins found.",
|
||||
"description": "No logins found."
|
||||
},
|
||||
"errorNotConnected": {
|
||||
"message": "Not connected to KeePassXC.",
|
||||
"description": "Error notification shown when not connected to KeePassXC"
|
||||
},
|
||||
"passwordGeneratorIcon": {
|
||||
"message": "Password generator icon",
|
||||
"description": "Alt attribute text of the password generator icon."
|
||||
|
|
@ -111,10 +111,6 @@
|
|||
"message": "Also fill in the next password-field",
|
||||
"description": "Checkbox text below the password generator input field."
|
||||
},
|
||||
"passwordGeneratorBits": {
|
||||
"message": "$1 Bits",
|
||||
"description": "Bits of the generated password."
|
||||
},
|
||||
"passwordGeneratorTitle": {
|
||||
"message": "Password Generator",
|
||||
"description": "Password generator dialog title."
|
||||
|
|
@ -131,9 +127,9 @@
|
|||
"message": "Copy",
|
||||
"description": "Copy button text in password generator."
|
||||
},
|
||||
"passwordGeneratorFillAndCopy": {
|
||||
"message": "Fill and copy",
|
||||
"description": "Fill and copy button text in password generator."
|
||||
"passwordGeneratorFill": {
|
||||
"message": "Fill password",
|
||||
"description": "Fill a password in password generator."
|
||||
},
|
||||
"passwordGeneratorErrorTooLong": {
|
||||
"message": "Error: The generated password is longer than the allowed length!",
|
||||
|
|
@ -159,6 +155,18 @@
|
|||
"message": "Is KeePassXC running and connected?",
|
||||
"description": "Password generator error text when KeePassXC is closed."
|
||||
},
|
||||
"usernameFieldText": {
|
||||
"message": "Fill credentials from KeePassXC",
|
||||
"description": "Username field icon hover text."
|
||||
},
|
||||
"usernameLockedFieldText": {
|
||||
"message": "Database is locked",
|
||||
"description": "Username field icon hover text when database is locked."
|
||||
},
|
||||
"usernameFieldIcon": {
|
||||
"message": "Username field icon",
|
||||
"description": "Username field icon text."
|
||||
},
|
||||
"defineDismiss": {
|
||||
"message": "Dismiss",
|
||||
"description": "Dismiss button text when choosing custom login fields."
|
||||
|
|
@ -224,11 +232,11 @@
|
|||
"description": "Shown when no username is set in the credentials."
|
||||
},
|
||||
"credentialsNoLoginsFound": {
|
||||
"message": "Error: No logins found.",
|
||||
"message": "No logins found.",
|
||||
"description": "Shown when no credentials are found for the current page."
|
||||
},
|
||||
"credentialsMultipleFound": {
|
||||
"message": "Error: More than one login was found in KeePassXC!",
|
||||
"message": "More than one login was found in KeePassXC!",
|
||||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
},
|
||||
"credentialsMultipleFoundSecondLine": {
|
||||
|
|
@ -236,7 +244,7 @@
|
|||
"description": "Alert message when trying to fill username and/or password when multiple credentials are found."
|
||||
},
|
||||
"credentialsNoUsernameFound": {
|
||||
"message": "Error: No credentials for the given username found.",
|
||||
"message": "No credentials for the given username found.",
|
||||
"description": "Alert message when no credentials are found for the given username."
|
||||
},
|
||||
"credentialExpired": {
|
||||
|
|
@ -287,6 +295,10 @@
|
|||
"message": "Reopen database",
|
||||
"description": "Popup reopen database button text."
|
||||
},
|
||||
"popupUsernameButton": {
|
||||
"message": "Add Username-only option for the site",
|
||||
"description": "Button text for adding Username-only option."
|
||||
},
|
||||
"popupErrorEncountered": {
|
||||
"message": "KeePassXC-Browser has encountered an error:",
|
||||
"description": "A text shown above error message in the popup."
|
||||
|
|
@ -323,46 +335,78 @@
|
|||
"message": "KeePassXC-Browser is connected to KeePassXC and has been configured using the following identifier: $1",
|
||||
"description": "A popuo message when a connection to KeePassXC is succesful."
|
||||
},
|
||||
"popupRememberInfoText": {
|
||||
"popupUsernameFieldDetected": {
|
||||
"message": "Only a single username field was detected. Add the URL to Site Preferences with Username-only option enabled?",
|
||||
"description": "Text shown when page can be added to Site Preferences with Username-only option enabled."
|
||||
},
|
||||
"rememberInfoText": {
|
||||
"message": "Username or password changed! Save it?",
|
||||
"description": "Popup message when username or password has changed."
|
||||
"description": "Message when username or password has changed."
|
||||
},
|
||||
"popupRememberSaving": {
|
||||
"rememberSaving": {
|
||||
"message": "Credentials will be saved in connected database with identifier $1",
|
||||
"description": "A popup message when saving or updating credentials."
|
||||
"description": "A message when saving or updating credentials."
|
||||
},
|
||||
"popupRememberNewUsername": {
|
||||
"rememberNewUsername": {
|
||||
"message": "The following username is currently not saved: $1",
|
||||
"description": "A popup message shown when a new username is detected when saving credentials."
|
||||
"description": "A message shown when a new username is detected when saving credentials."
|
||||
},
|
||||
"popupRememberUsernameExists": {
|
||||
"rememberUsernameExists": {
|
||||
"message": "The credentials with the used username are marked bold: $1",
|
||||
"description": "A popup message shown when a existing username is detected when saving credentials."
|
||||
"description": "A message shown when a existing username is detected when saving credentials."
|
||||
},
|
||||
"popupRememberChooseCredentials": {
|
||||
"rememberChooseCredentials": {
|
||||
"message": "Please choose the credentials you want to update.",
|
||||
"description": "A popup message shown choosing what credentials user wants to update."
|
||||
"description": "A message shown choosing what credentials user wants to update."
|
||||
},
|
||||
"popupRememberChooseGroup": {
|
||||
"rememberChooseGroup": {
|
||||
"message": "Please choose the group you want to add the new credentials.",
|
||||
"description": "A popup message shown choosing what group user wants to use for new credentials."
|
||||
"description": "A message shown choosing what group user wants to use for new credentials."
|
||||
},
|
||||
"popupRememberInfoDefaultGroupNotFound": {
|
||||
"message": "Info: Default group created in connected database.",
|
||||
"rememberInfoDefaultGroupNotFound": {
|
||||
"message": "Default group created in connected database.",
|
||||
"description": "Info message shown when default group set cannot be found."
|
||||
},
|
||||
"popupRememberErrorCreatingNewGroup": {
|
||||
"message": "Error: New group(s) cannot be created.",
|
||||
"rememberErrorCreatingNewGroup": {
|
||||
"message": "New group(s) cannot be created.",
|
||||
"description": "Error message shown when new groups cannot be created."
|
||||
},
|
||||
"popupRememberErrorPasswordNotChanged": {
|
||||
"message": "Error: Credentials not updated. The password has not been changed.",
|
||||
"rememberErrorPasswordNotChanged": {
|
||||
"message": "Credentials not updated. The password has not been changed.",
|
||||
"description": "Error message shown when credential password is not changed."
|
||||
},
|
||||
"popupRememberErrorCannotSaveCredentials": {
|
||||
"message": "Error: Credentials cannot be saved or updated.",
|
||||
"rememberErrorCannotSaveCredentials": {
|
||||
"message": "Credentials cannot be saved or updated.",
|
||||
"description": "Error message shown when credentials cannot be saved or updated."
|
||||
},
|
||||
"rememberErrorDatabaseClosed": {
|
||||
"message": "Credentials cannot be saved or updated to a closed database.",
|
||||
"description": "Error message shown when credentials cannot be saved or updated and the database is closed."
|
||||
},
|
||||
"rememberCredentialsNotSaved": {
|
||||
"message": "Operation cancelled. Credentials are not updated.",
|
||||
"description": "Message shown when saving operation was cancelled."
|
||||
},
|
||||
"rememberCredentialsSaved": {
|
||||
"message": "New credentials with username $1 saved.",
|
||||
"description": "Message shown when new credentials are saved."
|
||||
},
|
||||
"rememberCredentialsUpdated": {
|
||||
"message": "Credentials with username $1 updated.",
|
||||
"description": "Message shown when new credentials are succesfully updated."
|
||||
},
|
||||
"rememberCredentialsExists": {
|
||||
"message": "Identical credentials already exists. Nothing to update.",
|
||||
"description": "Message shown when new credentials already found in database."
|
||||
},
|
||||
"rememberEmptyUsername": {
|
||||
"message": "(empty)",
|
||||
"description": "Means an empty username."
|
||||
},
|
||||
"rememberNoPassword": {
|
||||
"message": "Please fill the password field before saving credentials.",
|
||||
"description": "Message shown when no password is set when saving credentials."
|
||||
},
|
||||
"popupLoginText": {
|
||||
"message": "Select the login information you would like to get entered into the page.",
|
||||
"description": "A popup message shown when one or multiple credentials are present."
|
||||
|
|
@ -400,7 +444,7 @@
|
|||
"description": "Dismiss button text in popup when updating credentials."
|
||||
},
|
||||
"popupButtonIgnore": {
|
||||
"message": "Never ask for this page",
|
||||
"message": "Don't ask again for this site",
|
||||
"description": "Ignore button text in popup or in notification when updating credentials."
|
||||
},
|
||||
"popupButtonClose": {
|
||||
|
|
@ -491,18 +535,6 @@
|
|||
"message": "Reset",
|
||||
"description": "Reset button text for keyboard shortcuts."
|
||||
},
|
||||
"optionsLabelBlinkTime": {
|
||||
"message": "Blink Time:",
|
||||
"description": "Blink Time option text."
|
||||
},
|
||||
"optionsLabelRedirectOffset": {
|
||||
"message": "Redirect Offset:",
|
||||
"description": "Redirect Offset option text."
|
||||
},
|
||||
"optionsLabelRedirectAllowance": {
|
||||
"message": "Maximum (Number of) Redirects:",
|
||||
"description": "RMaximum (Number of) Redirects options text."
|
||||
},
|
||||
"optionsLabelDefaultGroup": {
|
||||
"message": "Default group for saving new passwords:",
|
||||
"description": "Default group options text."
|
||||
|
|
@ -520,8 +552,8 @@
|
|||
"description": "Default checkbox explanation help text."
|
||||
},
|
||||
"optionsCheckboxUsePasswordGenerator": {
|
||||
"message": "Activate password generator.",
|
||||
"description": "Activate password generator checkbox text."
|
||||
"message": "Activate password generator icons.",
|
||||
"description": "Activate password generator icons checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoRetrieveCredentials": {
|
||||
"message": "Automatically retrieve credentials.",
|
||||
|
|
@ -560,7 +592,7 @@
|
|||
"description": "Save domain only checkbox text."
|
||||
},
|
||||
"optionsCheckboxShowLoginNotifications": {
|
||||
"message": "Show a notification when new credentials can be saved to the database.",
|
||||
"message": "Show a banner on the page when new credentials can be saved to the database.",
|
||||
"description": "Show login notifications checkbox text."
|
||||
},
|
||||
"optionsCheckboxAutoFillAndSend": {
|
||||
|
|
@ -591,24 +623,8 @@
|
|||
"message": "Keyboard shortcuts",
|
||||
"description": "Keyboard shortcut header text."
|
||||
},
|
||||
"optionsBlinkTimeHelpText": {
|
||||
"message": "Maximum time (ms) the icon should blink after detecting new credentials",
|
||||
"description": "Blink Time option help text."
|
||||
},
|
||||
"optionsRedirectOffsetHelpText": {
|
||||
"message": "Minimum time (ms) the icon should blink before deactivating due to page redirects.",
|
||||
"description": "Redirect Offset option help text."
|
||||
},
|
||||
"optionsRedirectOffsetHelpTextSecond": {
|
||||
"message": "-1 to only use blink time ignoring Maximum Redirects (old behavior)",
|
||||
"description": "Redirect Offset option help text, second part."
|
||||
},
|
||||
"optionsRedirectAllowanceHelpText": {
|
||||
"message": "How many pages should the tab cycle through after the redirect offset before deactivating the icon",
|
||||
"description": "Redirect Allowance option help text."
|
||||
},
|
||||
"optionsUsePasswordGeneratorHelpText": {
|
||||
"message": "Adds a button to password fields for generating a new password.",
|
||||
"message": "Adds an icon to password fields for generating a new password.",
|
||||
"description": "Password Generator option help text."
|
||||
},
|
||||
"optionsUsePasswordGeneratorHelpTextSecond": {
|
||||
|
|
@ -804,7 +820,7 @@
|
|||
"description": "Site preferences info text, first part."
|
||||
},
|
||||
"optionsSitePreferencesTabHelpTextSecond": {
|
||||
"message": "To ignore new/modified credentials on a specific site, add them below or click the blinking KeePassXC-Browser icon and select",
|
||||
"message": "To ignore new/modified credentials on a specific site, add them below or when using the credential banner on the page select",
|
||||
"description": "Site preferences info text, second part."
|
||||
},
|
||||
"optionsSitePreferencesTabHelpTextThird": {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,10 @@
|
|||
|
||||
const browserAction = {};
|
||||
|
||||
const BLINK_TIMEOUT_DEFAULT = 7500;
|
||||
const BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT = -1;
|
||||
const BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT = 1;
|
||||
|
||||
// Milliseconds for intervall (e.g. to update browserAction)
|
||||
const _interval = 250;
|
||||
let _loop = null;
|
||||
|
||||
browserAction.show = function(callback, tab) {
|
||||
browserAction.show = function(tab) {
|
||||
let data = {};
|
||||
if (!page.tabs[tab.id] || page.tabs[tab.id].stack.length === 0) {
|
||||
browserAction.showDefault(callback, tab);
|
||||
browserAction.showDefault(tab);
|
||||
return;
|
||||
} else {
|
||||
data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1];
|
||||
|
|
@ -32,106 +24,31 @@ browserAction.show = function(callback, tab) {
|
|||
}
|
||||
};
|
||||
|
||||
browserAction.update = function(interval) {
|
||||
if (!page.tabs[page.currentTabId] || page.tabs[page.currentTabId].stack.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = page.tabs[page.currentTabId].stack[page.tabs[page.currentTabId].stack.length - 1];
|
||||
|
||||
if (data.visibleForMilliSeconds !== undefined && data.visibleForMilliSeconds !== -1) {
|
||||
if (data.visibleForMilliSeconds <= 0) {
|
||||
browserAction.stackPop(page.currentTabId);
|
||||
browserAction.disableLoop();
|
||||
browserAction.show(null, { 'id': page.currentTabId });
|
||||
page.clearCredentials(page.currentTabId);
|
||||
return;
|
||||
}
|
||||
data.visibleForMilliSeconds -= interval;
|
||||
}
|
||||
|
||||
if (data.intervalIcon) {
|
||||
data.intervalIcon.counter += 1;
|
||||
if (data.intervalIcon.counter < data.intervalIcon.max) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.intervalIcon.counter = 0;
|
||||
data.intervalIcon.index += 1;
|
||||
|
||||
if (data.intervalIcon.index > data.intervalIcon.icons.length - 1) {
|
||||
data.intervalIcon.index = 0;
|
||||
}
|
||||
|
||||
browser.browserAction.setIcon({
|
||||
tabId: page.currentTabId,
|
||||
path: '/icons/toolbar/' + browserAction.generateIconName(null, data.intervalIcon.icons[data.intervalIcon.index])
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
browserAction.showDefault = function(callback, tab) {
|
||||
browserAction.showDefault = async function(tab) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
iconType: 'normal',
|
||||
popup: 'popup.html'
|
||||
};
|
||||
keepass.isConfigured().then((response) => {
|
||||
if (!response || keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable || page.tabs[tab.id].errorMessage) {
|
||||
stackData.iconType = 'cross';
|
||||
}
|
||||
|
||||
if (page.tabs[tab.id].loginList.length > 0) {
|
||||
stackData.iconType = 'questionmark';
|
||||
stackData.popup = 'popup_login.html';
|
||||
}
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
browserAction.disableLoop();
|
||||
browserAction.show(null, tab);
|
||||
const response = await keepass.isConfigured().catch((err) => {
|
||||
console.log('Error: Cannot show default popup: ' + err);
|
||||
});
|
||||
|
||||
if (!response || keepass.isDatabaseClosed || !keepass.isKeePassXCAvailable) {
|
||||
stackData.iconType = 'cross';
|
||||
}
|
||||
|
||||
if (page.tabs[tab.id] && page.tabs[tab.id].loginList.length > 0) {
|
||||
stackData.iconType = 'questionmark';
|
||||
stackData.popup = 'popup_login.html';
|
||||
}
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
browserAction.show(tab);
|
||||
};
|
||||
|
||||
browserAction.stackAdd = function(callback, tab, icon, popup, level, push, visibleForMilliSeconds, visibleForPageUpdates, redirectOffset, dontShow) {
|
||||
const id = tab.id || page.currentTabId;
|
||||
|
||||
if (!level) {
|
||||
level = 1;
|
||||
}
|
||||
|
||||
const stackData = {
|
||||
level: level,
|
||||
icon: icon
|
||||
};
|
||||
|
||||
if (popup) {
|
||||
stackData.popup = popup;
|
||||
}
|
||||
|
||||
if (visibleForMilliSeconds !== undefined) {
|
||||
stackData.visibleForMilliSeconds = visibleForMilliSeconds;
|
||||
}
|
||||
|
||||
if (visibleForPageUpdates !== undefined) {
|
||||
stackData.visibleForPageUpdates = visibleForPageUpdates;
|
||||
}
|
||||
|
||||
if (redirectOffset !== undefined) {
|
||||
stackData.redirectOffset = redirectOffset;
|
||||
}
|
||||
|
||||
if (push) {
|
||||
browserAction.stackPush(stackData, id);
|
||||
} else {
|
||||
browserAction.stackUnshift(stackData, id);
|
||||
}
|
||||
|
||||
if (!dontShow) {
|
||||
browserAction.show(null, { 'id': id });
|
||||
}
|
||||
};
|
||||
|
||||
browserAction.removeLevelFromStack = function(callback, tab, level, type, dontShow) {
|
||||
browserAction.removeLevelFromStack = function(tab, level, type, dontShow) {
|
||||
if (!page.tabs[tab.id]) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -156,7 +73,7 @@ browserAction.removeLevelFromStack = function(callback, tab, level, type, dontSh
|
|||
page.tabs[tab.id].stack = newStack;
|
||||
|
||||
if (!dontShow) {
|
||||
browserAction.show(callback, tab);
|
||||
browserAction.show(tab);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -167,132 +84,16 @@ browserAction.stackPop = function(tabId) {
|
|||
|
||||
browserAction.stackPush = function(data, tabId) {
|
||||
const id = tabId || page.currentTabId;
|
||||
browserAction.removeLevelFromStack(null, { 'id': id }, data.level, '<=', true);
|
||||
browserAction.removeLevelFromStack({ 'id': id }, data.level, '<=', true);
|
||||
page.tabs[id].stack.push(data);
|
||||
};
|
||||
|
||||
browserAction.stackUnshift = function(data, tabId) {
|
||||
const id = tabId || page.currentTabId;
|
||||
browserAction.removeLevelFromStack(null, { 'id': id }, data.level, '<=', true);
|
||||
browserAction.removeLevelFromStack({ 'id': id }, data.level, '<=', true);
|
||||
page.tabs[id].stack.unshift(data);
|
||||
};
|
||||
|
||||
|
||||
browserAction.removeRememberPopup = function(callback, tab, removeImmediately) {
|
||||
if (!page.tabs[tab.id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (page.tabs[tab.id].stack.length === 0) {
|
||||
page.clearCredentials(tab.id);
|
||||
return;
|
||||
}
|
||||
const data = page.tabs[tab.id].stack[page.tabs[tab.id].stack.length - 1];
|
||||
|
||||
if (removeImmediately || !isNaN(data.visibleForPageUpdates)) {
|
||||
const currentMS = Date.now();
|
||||
if (removeImmediately || (data.visibleForPageUpdates <= 0 && data.redirectOffset > 0)) {
|
||||
browserAction.stackPop(tab.id);
|
||||
browserAction.show(null, { 'id': tab.id });
|
||||
page.clearCredentials(tab.id);
|
||||
} else if (!isNaN(data.visibleForPageUpdates) && data.redirectOffset > 0 && currentMS >= data.redirectOffset) {
|
||||
data.visibleForPageUpdates -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
browserAction.setRememberPopup = function(tabId, username, password, url, usernameExists, credentialsList) {
|
||||
browser.storage.local.get({ 'settings': {} }).then(function(item) {
|
||||
const settings = item.settings;
|
||||
|
||||
// Don't show anything if the site is in the ignore
|
||||
if (settings.sitePreferences !== undefined) {
|
||||
for (const site of settings.sitePreferences) {
|
||||
if (site.ignore === IGNORE_NORMAL && (site.url === url || siteMatch(site.url, url))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const id = tabId || page.currentTabId;
|
||||
let timeoutMinMillis = Number(getValueOrDefault(settings, 'blinkMinTimeout', BLINK_TIMEOUT_REDIRECT_THRESHOLD_TIME_DEFAULT, -1));
|
||||
|
||||
if (timeoutMinMillis > 0) {
|
||||
timeoutMinMillis += Date.now();
|
||||
}
|
||||
|
||||
const blinkTimeout = getValueOrDefault(settings, 'blinkTimeout', BLINK_TIMEOUT_DEFAULT, -1);
|
||||
const pageUpdateAllowance = getValueOrDefault(settings, 'allowedRedirect', BLINK_TIMEOUT_REDIRECT_COUNT_DEFAULT, 0);
|
||||
|
||||
const stackData = {
|
||||
visibleForMilliSeconds: blinkTimeout,
|
||||
visibleForPageUpdates: pageUpdateAllowance,
|
||||
redirectOffset: timeoutMinMillis,
|
||||
level: 10,
|
||||
intervalIcon: {
|
||||
index: 0,
|
||||
counter: 0,
|
||||
max: 2,
|
||||
icons: [ 'icon_remember_red_background.png', 'icon_remember_red_lock.png' ]
|
||||
},
|
||||
icon: 'icon_remember_red_background.png',
|
||||
popup: 'popup_remember.html'
|
||||
};
|
||||
|
||||
browserAction.activateLoop();
|
||||
browserAction.stackPush(stackData, id);
|
||||
|
||||
page.tabs[id].credentials = {
|
||||
username: username,
|
||||
password: password,
|
||||
url: url,
|
||||
usernameExists: usernameExists,
|
||||
list: credentialsList
|
||||
};
|
||||
|
||||
browserAction.show(null, { 'id': id });
|
||||
|
||||
if (page.settings.showLoginNotifications) {
|
||||
const message = tr('rememberCredentialsPopup');
|
||||
const buttons = [
|
||||
{
|
||||
'title': tr('popupButtonClose')
|
||||
},
|
||||
{
|
||||
'title': tr('popupButtonIgnore')
|
||||
}
|
||||
];
|
||||
|
||||
browser.notifications.create({
|
||||
'type': 'basic',
|
||||
'iconUrl': browser.extension.getURL('icons/keepassxc_64x64.png'),
|
||||
'title': 'KeePassXC-Browser',
|
||||
'message': message,
|
||||
'buttons': buttons
|
||||
});
|
||||
|
||||
browser.notifications.onButtonClicked.addListener((notificationId, index) => {
|
||||
browser.notifications.clear(notificationId);
|
||||
if (index === 1) {
|
||||
browserAction.ignoreSite(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getValueOrDefault(settings, key, defaultVal, min) {
|
||||
try {
|
||||
let val = settings[key];
|
||||
if (isNaN(val) || val < min) {
|
||||
val = defaultVal;
|
||||
}
|
||||
return val;
|
||||
} catch (e) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
browserAction.generateIconName = function(iconType, icon) {
|
||||
if (icon) {
|
||||
return icon;
|
||||
|
|
@ -306,33 +107,17 @@ browserAction.generateIconName = function(iconType, icon) {
|
|||
return name;
|
||||
};
|
||||
|
||||
browserAction.ignoreSite = function(url) {
|
||||
browser.windows.getCurrent().then(() => {
|
||||
// Get current active window
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
|
||||
const tab = tabs[0];
|
||||
browserAction.ignoreSite = async function(url) {
|
||||
await browser.windows.getCurrent();
|
||||
|
||||
// Send the message to the current tab's content script
|
||||
browser.runtime.getBackgroundPage().then(() => {
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'ignore_site',
|
||||
args: [ url ]
|
||||
});
|
||||
});
|
||||
});
|
||||
// Get current active window
|
||||
const tabs = await browser.tabs.query({ 'active': true, 'currentWindow': true });
|
||||
const tab = tabs[0];
|
||||
|
||||
// Send the message to the current tab's content script
|
||||
await browser.runtime.getBackgroundPage();
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'ignore_site',
|
||||
args: [ url ]
|
||||
});
|
||||
};
|
||||
|
||||
// Interval which updates the browserAction (e.g. blinking icon)
|
||||
browserAction.activateLoop = function() {
|
||||
if (_loop === null) {
|
||||
_loop = setInterval(function() {
|
||||
browserAction.update(_interval);
|
||||
}, _interval);
|
||||
}
|
||||
};
|
||||
|
||||
browserAction.disableLoop = function() {
|
||||
clearInterval(_loop);
|
||||
_loop = null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,302 +2,263 @@
|
|||
|
||||
const kpxcEvent = {};
|
||||
|
||||
kpxcEvent.onMessage = function(request, sender, callback) {
|
||||
kpxcEvent.onMessage = async function(request, sender) {
|
||||
if (request.action in kpxcEvent.messageHandlers) {
|
||||
//console.log('onMessage(' + request.action + ') for #' + sender.tab.id);
|
||||
if (!sender.hasOwnProperty('tab') || sender.tab.id < 1) {
|
||||
sender.tab = {};
|
||||
sender.tab.id = page.currentTabId;
|
||||
}
|
||||
|
||||
kpxcEvent.invoke(kpxcEvent.messageHandlers[request.action], callback, sender.tab.id, request.args);
|
||||
|
||||
// onMessage closes channel for callback automatically
|
||||
// if this method does not return true
|
||||
if (callback !== undefined) {
|
||||
return true;
|
||||
}
|
||||
return await kpxcEvent.messageHandlers[request.action](sender.tab, request.args);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get interesting information about the given tab.
|
||||
* Function adapted from AdBlock-Plus.
|
||||
*
|
||||
* @param {function} handler to call after invoke
|
||||
* @param {function} callback to call after handler or null
|
||||
* @param {integer} senderTabId
|
||||
* @param {array} args
|
||||
* @param {bool} secondTime
|
||||
* @returns null (asynchronous)
|
||||
*/
|
||||
kpxcEvent.invoke = function(handler, callback, senderTabId, args, secondTime) {
|
||||
if (senderTabId < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!page.tabs[senderTabId]) {
|
||||
page.createTabEntry(senderTabId);
|
||||
}
|
||||
|
||||
// Remove information from no longer existing tabs
|
||||
page.removePageInformationFromNotExistingTabs();
|
||||
|
||||
browser.tabs.get(senderTabId).then((tab) => {
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tab.url) {
|
||||
// Issue 6877: tab URL is not set directly after you opened a window
|
||||
// using window.open()
|
||||
if (!secondTime) {
|
||||
window.setTimeout(function() {
|
||||
kpxcEvent.invoke(handler, callback, senderTabId, args, true);
|
||||
}, 250);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!page.tabs[tab.id]) {
|
||||
page.createTabEntry(tab.id);
|
||||
}
|
||||
|
||||
args = args || [];
|
||||
|
||||
args.unshift(tab);
|
||||
args.unshift(callback);
|
||||
|
||||
if (handler) {
|
||||
handler.apply(this, args);
|
||||
} else {
|
||||
console.log('undefined handler for tab ' + tab.id);
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
|
||||
kpxcEvent.onShowNotification = function(callback, tab, message) {
|
||||
if (page.settings.showNotifications) {
|
||||
showNotification(message);
|
||||
}
|
||||
};
|
||||
|
||||
kpxcEvent.showStatus = function(configured, tab, callback) {
|
||||
kpxcEvent.showStatus = async function(tab, configured) {
|
||||
let keyId = null;
|
||||
if (configured && keepass.databaseHash !== '') {
|
||||
keyId = keepass.keyRing[keepass.databaseHash].id;
|
||||
}
|
||||
|
||||
browserAction.showDefault(null, tab);
|
||||
browserAction.showDefault(tab);
|
||||
const errorMessage = page.tabs[tab.id].errorMessage;
|
||||
callback({
|
||||
return {
|
||||
identifier: keyId,
|
||||
configured: configured,
|
||||
databaseClosed: keepass.isDatabaseClosed,
|
||||
keePassXCAvailable: keepass.isKeePassXCAvailable,
|
||||
encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized,
|
||||
associated: keepass.isAssociated(),
|
||||
error: errorMessage || null
|
||||
error: errorMessage || null,
|
||||
usernameFieldDetected: page.usernameFieldDetected
|
||||
};
|
||||
};
|
||||
|
||||
kpxcEvent.onLoadSettings = async function() {
|
||||
return await page.initSettings().catch((err) => {
|
||||
console.log('onLoadSettings error: ' + err);
|
||||
return Promise.reject();
|
||||
});
|
||||
};
|
||||
|
||||
kpxcEvent.onLoadSettings = function(callback, tab) {
|
||||
page.initSettings().then((settings) => {
|
||||
callback(settings);
|
||||
}, (err) => {
|
||||
console.log('error loading settings: ' + err);
|
||||
kpxcEvent.onLoadKeyRing = async function() {
|
||||
const item = await browser.storage.local.get({ 'keyRing': {} }).catch((err) => {
|
||||
console.log('kpxcEvent.onLoadKeyRing error: ' + err);
|
||||
return Promise.reject();
|
||||
});
|
||||
|
||||
keepass.keyRing = item.keyRing;
|
||||
if (keepass.isAssociated() && !keepass.keyRing[keepass.associated.hash]) {
|
||||
keepass.associated = {
|
||||
'value': false,
|
||||
'hash': null
|
||||
};
|
||||
}
|
||||
|
||||
return item.keyRing;
|
||||
};
|
||||
|
||||
kpxcEvent.onLoadKeyRing = function(callback, tab) {
|
||||
browser.storage.local.get({ 'keyRing': {} }).then(function(item) {
|
||||
keepass.keyRing = item.keyRing;
|
||||
if (keepass.isAssociated() && !keepass.keyRing[keepass.associated.hash]) {
|
||||
keepass.associated = {
|
||||
'value': false,
|
||||
'hash': null
|
||||
};
|
||||
}
|
||||
callback(item.keyRing);
|
||||
}, (err) => {
|
||||
console.log('error loading keyRing: ' + err);
|
||||
});
|
||||
kpxcEvent.onSaveSettings = async function(tab, args = []) {
|
||||
const [ settings ] = args;
|
||||
browser.storage.local.set({ 'settings': settings });
|
||||
kpxcEvent.onLoadSettings(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onSaveSettings = function(callback, tab, settings) {
|
||||
browser.storage.local.set({ 'settings': settings }).then(function() {
|
||||
kpxcEvent.onLoadSettings(callback, tab);
|
||||
});
|
||||
};
|
||||
|
||||
kpxcEvent.onGetStatus = function(callback, tab, internalPoll = false, triggerUnlock = false) {
|
||||
kpxcEvent.onGetStatus = async function(tab, args = []) {
|
||||
// When internalPoll is true the event is triggered from content script in intervals -> don't poll KeePassXC
|
||||
if (!internalPoll) {
|
||||
keepass.testAssociation((response) => {
|
||||
try {
|
||||
const [ internalPoll = false, triggerUnlock = false ] = args;
|
||||
if (!internalPoll) {
|
||||
const response = await keepass.testAssociation(tab, [ true, triggerUnlock ]);
|
||||
if (!response) {
|
||||
kpxcEvent.showStatus(false, tab, callback);
|
||||
return;
|
||||
return kpxcEvent.showStatus(tab, false);
|
||||
}
|
||||
}
|
||||
|
||||
keepass.isConfigured().then((configured) => {
|
||||
kpxcEvent.showStatus(configured, tab, callback);
|
||||
});
|
||||
}, tab, true, triggerUnlock);
|
||||
} else {
|
||||
keepass.isConfigured().then((configured) => {
|
||||
kpxcEvent.showStatus(configured, tab, callback);
|
||||
});
|
||||
const configured = await keepass.isConfigured();
|
||||
return kpxcEvent.showStatus(tab, configured);
|
||||
} catch (err) {
|
||||
console.log('Error: No status shown: ' + err);
|
||||
return Promise.reject();
|
||||
}
|
||||
};
|
||||
|
||||
kpxcEvent.onReconnect = async function(callback, tab) {
|
||||
const configured = await keepass.reconnect(callback, tab);
|
||||
kpxcEvent.onReconnect = async function(tab) {
|
||||
const configured = await keepass.reconnect(tab);
|
||||
if (configured) {
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'redetect_fields'
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
return;
|
||||
});
|
||||
}
|
||||
kpxcEvent.showStatus(configured, tab, callback);
|
||||
|
||||
return kpxcEvent.showStatus(tab, configured);
|
||||
};
|
||||
|
||||
kpxcEvent.lockDatabase = function(callback, tab) {
|
||||
keepass.lockDatabase(tab).then(() => {
|
||||
kpxcEvent.showStatus(true, tab, callback);
|
||||
});
|
||||
kpxcEvent.lockDatabase = async function(tab) {
|
||||
try {
|
||||
await keepass.lockDatabase(tab);
|
||||
return kpxcEvent.showStatus(tab, false);
|
||||
} catch (err) {
|
||||
console.log('kpxcEvent.lockDatabase error: ' + err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
kpxcEvent.onPopStack = function(callback, tab) {
|
||||
kpxcEvent.onPopStack = function(tab) {
|
||||
browserAction.stackPop(tab.id);
|
||||
browserAction.show(null, tab);
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onGetTabInformation = function(callback, tab) {
|
||||
kpxcEvent.onGetTabInformation = function(tab) {
|
||||
const id = tab.id || page.currentTabId;
|
||||
callback(page.tabs[id]);
|
||||
return Promise.resolve(page.tabs[id]);
|
||||
};
|
||||
|
||||
kpxcEvent.onGetConnectedDatabase = function(callback, tab) {
|
||||
callback({
|
||||
kpxcEvent.onGetConnectedDatabase = function() {
|
||||
return Promise.resolve({
|
||||
count: Object.keys(keepass.keyRing).length,
|
||||
identifier: (keepass.keyRing[keepass.associated.hash]) ? keepass.keyRing[keepass.associated.hash].id : null
|
||||
});
|
||||
};
|
||||
|
||||
kpxcEvent.onGetKeePassXCVersions = function(callback, tab) {
|
||||
kpxcEvent.onGetKeePassXCVersions = async function(tab) {
|
||||
if (keepass.currentKeePassXC === '') {
|
||||
keepass.getDatabaseHash((res) => {
|
||||
callback({ 'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version });
|
||||
}, tab);
|
||||
} else {
|
||||
callback({ 'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version });
|
||||
await keepass.getDatabaseHash(tab);
|
||||
return { 'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version };
|
||||
}
|
||||
|
||||
return Promise.resolve({ 'current': keepass.currentKeePassXC, 'latest': keepass.latestKeePassXC.version });
|
||||
};
|
||||
|
||||
kpxcEvent.onCheckUpdateKeePassXC = function(callback, tab) {
|
||||
kpxcEvent.onCheckUpdateKeePassXC = async function() {
|
||||
keepass.checkForNewKeePassXCVersion();
|
||||
callback({ current: keepass.currentKeePassXC.version, latest: keepass.latestKeePassXC.version });
|
||||
return { current: keepass.currentKeePassXC.version, latest: keepass.latestKeePassXC.version };
|
||||
};
|
||||
|
||||
kpxcEvent.onUpdateAvailableKeePassXC = function(callback, tab) {
|
||||
callback(page.settings.checkUpdateKeePassXC > 0 ? keepass.keePassXCUpdateAvailable() : false);
|
||||
kpxcEvent.onUpdateAvailableKeePassXC = async function() {
|
||||
return (page.settings.checkUpdateKeePassXC > 0) ? keepass.keePassXCUpdateAvailable() : false;
|
||||
};
|
||||
|
||||
kpxcEvent.onRemoveCredentialsFromTabInformation = function(callback, tab) {
|
||||
kpxcEvent.onRemoveCredentialsFromTabInformation = function(tab) {
|
||||
const id = tab.id || page.currentTabId;
|
||||
page.clearCredentials(id);
|
||||
page.clearSubmittedCredentials();
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onSetRememberPopup = function(callback, tab, username, password, url, usernameExists, credentialsList) {
|
||||
keepass.testAssociation((response) => {
|
||||
if (response) {
|
||||
keepass.isConfigured().then((configured) => {
|
||||
if (configured) {
|
||||
browserAction.setRememberPopup(tab.id, username, password, url, usernameExists, credentialsList);
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
}, tab);
|
||||
};
|
||||
|
||||
kpxcEvent.onLoginPopup = function(callback, tab, logins) {
|
||||
kpxcEvent.onLoginPopup = function(tab, logins) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
iconType: 'questionmark',
|
||||
popup: 'popup_login.html'
|
||||
};
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
page.tabs[tab.id].loginList = logins;
|
||||
browserAction.show(null, tab);
|
||||
|
||||
if (logins.length > 0) {
|
||||
page.tabs[tab.id].loginList = logins[0];
|
||||
}
|
||||
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.initHttpAuth = function(callback) {
|
||||
kpxcEvent.initHttpAuth = function() {
|
||||
httpAuth.init();
|
||||
callback();
|
||||
};
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
kpxcEvent.onHTTPAuthPopup = function(callback, tab, data) {
|
||||
kpxcEvent.onHTTPAuthPopup = function(tab, data) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
iconType: 'questionmark',
|
||||
popup: 'popup_httpauth.html'
|
||||
};
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
page.tabs[tab.id].loginList = data;
|
||||
browserAction.show(null, tab);
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onMultipleFieldsPopup = function(callback, tab) {
|
||||
kpxcEvent.onMultipleFieldsPopup = function(tab) {
|
||||
const stackData = {
|
||||
level: 1,
|
||||
iconType: 'normal',
|
||||
popup: 'popup_multiple-fields.html'
|
||||
};
|
||||
|
||||
browserAction.stackUnshift(stackData, tab.id);
|
||||
browserAction.show(null, tab);
|
||||
browserAction.show(tab);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageClearLogins = function(callback, tab, alreadyCalled) {
|
||||
kpxcEvent.pageClearLogins = function(tab, alreadyCalled) {
|
||||
if (!alreadyCalled) {
|
||||
page.clearLogins(tab.id);
|
||||
}
|
||||
callback();
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageGetLoginId = function(callback, tab) {
|
||||
callback(page.loginId);
|
||||
kpxcEvent.pageGetLoginId = function() {
|
||||
return Promise.resolve(page.loginId);
|
||||
};
|
||||
|
||||
kpxcEvent.pageSetLoginId = function(callback, tab, loginId) {
|
||||
kpxcEvent.pageSetLoginId = function(tab, loginId) {
|
||||
page.loginId = loginId;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.pageClearSubmitted = function() {
|
||||
page.clearSubmittedCredentials();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
kpxcEvent.pageGetSubmitted = function() {
|
||||
return Promise.resolve(page.submittedCredentials);
|
||||
};
|
||||
|
||||
kpxcEvent.pageSetSubmitted = function(tab, args = []) {
|
||||
const [ submitted, username, password, url, oldCredentials ] = args;
|
||||
page.setSubmittedCredentials(submitted, username, password, url, oldCredentials);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
kpxcEvent.onUsernameFieldDetected = function(tab, args = []) {
|
||||
page.usernameFieldDetected = args[0];
|
||||
};
|
||||
|
||||
// All methods named in this object have to be declared BEFORE this!
|
||||
kpxcEvent.messageHandlers = {
|
||||
'add_credentials': keepass.addCredentials,
|
||||
'associate': keepass.associate,
|
||||
'check_database_hash': keepass.checkDatabaseHash,
|
||||
'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC,
|
||||
'create_new_group': keepass.createNewGroup,
|
||||
'enable_automatic_reconnect': keepass.enableAutomaticReconnect,
|
||||
'disable_automatic_reconnect': keepass.disableAutomaticReconnect,
|
||||
'generate_password': keepass.generatePassword,
|
||||
'get_connected_database': kpxcEvent.onGetConnectedDatabase,
|
||||
'get_database_hash': keepass.getDatabaseHash,
|
||||
'get_database_groups': keepass.getDatabaseGroups,
|
||||
'get_keepassxc_versions': kpxcEvent.onGetKeePassXCVersions,
|
||||
'get_status': kpxcEvent.onGetStatus,
|
||||
'get_tab_information': kpxcEvent.onGetTabInformation,
|
||||
'init_http_auth': kpxcEvent.initHttpAuth,
|
||||
'is_connected': keepass.getIsKeePassXCAvailable,
|
||||
'load_keyring': kpxcEvent.onLoadKeyRing,
|
||||
'load_settings': kpxcEvent.onLoadSettings,
|
||||
'lock-database': kpxcEvent.lockDatabase,
|
||||
'page_clear_logins': kpxcEvent.pageClearLogins,
|
||||
'page_clear_submitted': kpxcEvent.pageClearSubmitted,
|
||||
'page_get_login_id': kpxcEvent.pageGetLoginId,
|
||||
'page_get_submitted': kpxcEvent.pageGetSubmitted,
|
||||
'page_set_login_id': kpxcEvent.pageSetLoginId,
|
||||
'page_set_submitted': kpxcEvent.pageSetSubmitted,
|
||||
'pop_stack': kpxcEvent.onPopStack,
|
||||
'popup_login': kpxcEvent.onLoginPopup,
|
||||
'popup_multiple-fields': kpxcEvent.onMultipleFieldsPopup,
|
||||
|
|
@ -306,9 +267,7 @@ kpxcEvent.messageHandlers = {
|
|||
'retrieve_credentials': keepass.retrieveCredentials,
|
||||
'show_default_browseraction': browserAction.showDefault,
|
||||
'update_credentials': keepass.updateCredentials,
|
||||
'username_field_detected': kpxcEvent.onUsernameFieldDetected,
|
||||
'save_settings': kpxcEvent.onSaveSettings,
|
||||
'set_remember_credentials': kpxcEvent.onSetRememberPopup,
|
||||
'show_notification': kpxcEvent.onShowNotification,
|
||||
'stack_add': browserAction.stackAdd,
|
||||
'update_available_keepassxc': kpxcEvent.onUpdateAvailableKeePassXC
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,11 +47,10 @@ httpAuth.handleRequestCallback = function(details, callback) {
|
|||
httpAuth.processPendingCallbacks(details, callback, callback);
|
||||
};
|
||||
|
||||
httpAuth.retrieveCredentials = function(tabId, url, submitUrl, forceCallback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
keepass.retrieveCredentials((logins) => {
|
||||
resolve(logins);
|
||||
}, tabId, url, submitUrl, forceCallback, false, true);
|
||||
httpAuth.retrieveCredentials = async function(tabId, url, submitUrl) {
|
||||
return await keepass.retrieveCredentials(tabId, [ url, submitUrl, false, true ]).catch((err) => {
|
||||
console.log('httpAuth.retrieveCredentials error: ' + err);
|
||||
return Promise.reject();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -69,7 +68,7 @@ httpAuth.processPendingCallbacks = async function(details, resolve, reject) {
|
|||
|
||||
details.searchUrl = (details.isProxy && details.proxyUrl) ? details.proxyUrl : details.url;
|
||||
|
||||
const logins = await httpAuth.retrieveCredentials({ 'id': details.tabId }, details.searchUrl, details.searchUrl, true);
|
||||
const logins = await httpAuth.retrieveCredentials({ 'id': details.tabId }, details.searchUrl, details.searchUrl);
|
||||
httpAuth.loginOrShowCredentials(logins, details, resolve, reject);
|
||||
};
|
||||
|
||||
|
|
@ -87,7 +86,7 @@ httpAuth.loginOrShowCredentials = function(logins, details, resolve, reject) {
|
|||
if (page.settings.showNotifications) {
|
||||
showNotification(tr('multipleCredentialsDetected'));
|
||||
}
|
||||
kpxcEvent.onHTTPAuthPopup(null, { 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve });
|
||||
kpxcEvent.onHTTPAuthPopup({ 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve });
|
||||
}
|
||||
} else {
|
||||
reject({ cancel: false }); // No logins found
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
keepass.migrateKeyRing().then(() => {
|
||||
page.initSettings().then(() => {
|
||||
page.initOpenedTabs().then(() => {
|
||||
httpAuth.init();
|
||||
keepass.reconnect().then(() => {
|
||||
keepass.enableAutomaticReconnect();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
(async () => {
|
||||
try {
|
||||
await keepass.migrateKeyRing();
|
||||
await page.initSettings();
|
||||
await page.initOpenedTabs();
|
||||
await httpAuth.init();
|
||||
await keepass.reconnect();
|
||||
await keepass.enableAutomaticReconnect();
|
||||
} catch (e) {
|
||||
console.log('init.js failed');
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Generate information structure for created tab and invoke all needed
|
||||
|
|
@ -18,10 +20,12 @@ keepass.migrateKeyRing().then(() => {
|
|||
*/
|
||||
browser.tabs.onCreated.addListener((tab) => {
|
||||
if (tab.id > 0) {
|
||||
//console.log('browser.tabs.onCreated(' + tab.id+ ')');
|
||||
if (tab.selected) {
|
||||
page.currentTabId = tab.id;
|
||||
kpxcEvent.invoke(page.switchTab, null, tab.id, []);
|
||||
if (!page.tabs[tab.id]) {
|
||||
page.createTabEntry(tab.id);
|
||||
}
|
||||
page.switchTab(tab);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -43,20 +47,24 @@ browser.tabs.onRemoved.addListener((tabId, removeInfo) => {
|
|||
* Invoke functions to retrieve credentials for focused tab
|
||||
* @param {object} activeInfo
|
||||
*/
|
||||
browser.tabs.onActivated.addListener((activeInfo) => {
|
||||
browser.tabs.onActivated.addListener(async function(activeInfo) {
|
||||
// Remove possible credentials from old tab information
|
||||
page.clearCredentials(page.currentTabId, true);
|
||||
browserAction.removeRememberPopup(null, { 'id': page.currentTabId }, true);
|
||||
|
||||
browser.tabs.get(activeInfo.tabId).then((info) => {
|
||||
try {
|
||||
const info = await browser.tabs.get(activeInfo.tabId);
|
||||
if (info && info.id) {
|
||||
page.currentTabId = info.id;
|
||||
if (info.status === 'complete') {
|
||||
//console.log('kpxcEvent.invoke(page.switchTab, null, '+info.id + ', []);');
|
||||
kpxcEvent.invoke(page.switchTab, null, info.id, []);
|
||||
if (!page.tabs[info.id]) {
|
||||
page.createTabEntry(info.id);
|
||||
}
|
||||
page.switchTab(info);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('Error: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -71,8 +79,10 @@ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
|||
}
|
||||
|
||||
if (changeInfo.status === 'complete') {
|
||||
browserAction.showDefault(null, tab);
|
||||
kpxcEvent.invoke(browserAction.removeRememberPopup, null, tabId, []);
|
||||
browserAction.showDefault(tab);
|
||||
if (!page.tabs[tab.id]) {
|
||||
page.createTabEntry(tab.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -82,7 +92,6 @@ const contextMenuItems = [
|
|||
{ title: tr('contextMenuFillUsernameAndPassword'), action: 'fill_username_password' },
|
||||
{ title: tr('contextMenuFillPassword'), action: 'fill_password' },
|
||||
{ title: tr('contextMenuFillTOTP'), action: 'fill_totp' },
|
||||
{ title: tr('contextMenuShowPasswordGeneratorIcons'), action: 'activate_password_generator' },
|
||||
{ title: tr('contextMenuShowPasswordGenerator'), action: 'show_password_generator' },
|
||||
{ title: tr('contextMenuSaveCredentials'), action: 'remember_credentials' }
|
||||
];
|
||||
|
|
@ -109,12 +118,11 @@ for (const item of contextMenuItems) {
|
|||
}
|
||||
|
||||
// Listen for keyboard shortcuts specified by user
|
||||
browser.commands.onCommand.addListener((command) => {
|
||||
browser.commands.onCommand.addListener(async (command) => {
|
||||
if (contextMenuItems.some(e => e.action === command)) {
|
||||
browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
|
||||
if (tabs.length) {
|
||||
browser.tabs.sendMessage(tabs[0].id, { action: command });
|
||||
}
|
||||
});
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (tabs.length) {
|
||||
browser.tabs.sendMessage(tabs[0].id, { action: command });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@ const defaultSettings = {
|
|||
checkUpdateKeePassXC: 3,
|
||||
autoCompleteUsernames: true,
|
||||
autoFillAndSend: false,
|
||||
usePasswordGenerator: true,
|
||||
usePasswordGeneratorIcons: false,
|
||||
autoFillSingleEntry: false,
|
||||
autoSubmit: false,
|
||||
autoRetrieveCredentials: true,
|
||||
|
|
@ -17,79 +17,86 @@ const defaultSettings = {
|
|||
};
|
||||
|
||||
var page = {};
|
||||
page.tabs = [];
|
||||
page.currentTabId = -1;
|
||||
page.blockedTabs = [];
|
||||
page.currentTabId = -1;
|
||||
page.loginId = -1;
|
||||
page.submitted = false;
|
||||
page.submittedCredentials = {};
|
||||
page.tabs = [];
|
||||
page.usernameFieldDetected = false;
|
||||
|
||||
page.initSettings = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.storage.local.get({ 'settings': {} }).then((item) => {
|
||||
page.settings = item.settings;
|
||||
if (!('checkUpdateKeePassXC' in page.settings)) {
|
||||
page.settings.checkUpdateKeePassXC = defaultSettings.checkUpdateKeePassXC;
|
||||
}
|
||||
if (!('autoCompleteUsernames' in page.settings)) {
|
||||
page.settings.autoCompleteUsernames = defaultSettings.autoCompleteUsernames;
|
||||
}
|
||||
if (!('autoFillAndSend' in page.settings)) {
|
||||
page.settings.autoFillAndSend = defaultSettings.autoFillAndSend;
|
||||
}
|
||||
if (!('usePasswordGenerator' in page.settings)) {
|
||||
page.settings.usePasswordGenerator = defaultSettings.usePasswordGenerator;
|
||||
}
|
||||
if (!('autoFillSingleEntry' in page.settings)) {
|
||||
page.settings.autoFillSingleEntry = defaultSettings.autoFillSingleEntry;
|
||||
}
|
||||
if (!('autoSubmit' in page.settings)) {
|
||||
page.settings.autoSubmit = defaultSettings.autoSubmit;
|
||||
}
|
||||
if (!('autoRetrieveCredentials' in page.settings)) {
|
||||
page.settings.autoRetrieveCredentials = defaultSettings.autoRetrieveCredentials;
|
||||
}
|
||||
if (!('showNotifications' in page.settings)) {
|
||||
page.settings.showNotifications = defaultSettings.showNotifications;
|
||||
}
|
||||
if (!('showLoginNotifications' in page.settings)) {
|
||||
page.settings.showLoginNotifications = defaultSettings.showLoginNotifications;
|
||||
}
|
||||
if (!('saveDomainOnly' in page.settings)) {
|
||||
page.settings.saveDomainOnly = defaultSettings.saveDomainOnly;
|
||||
}
|
||||
if (!('autoReconnect' in page.settings)) {
|
||||
page.settings.autoReconnect = defaultSettings.autoReconnect;
|
||||
}
|
||||
if (!('defaultGroup' in page.settings)) {
|
||||
page.settings.defaultGroup = defaultSettings.defaultGroup;
|
||||
}
|
||||
if (!('defaultGroupAlwaysAsk' in page.settings)) {
|
||||
page.settings.defaultGroupAlwaysAsk = defaultSettings.defaultGroupAlwaysAsk;
|
||||
}
|
||||
browser.storage.local.set({ 'settings': page.settings });
|
||||
resolve(page.settings);
|
||||
});
|
||||
});
|
||||
page.initSettings = async function() {
|
||||
try {
|
||||
const item = await browser.storage.local.get({ 'settings': {} });
|
||||
page.settings = item.settings;
|
||||
|
||||
if (!('checkUpdateKeePassXC' in page.settings)) {
|
||||
page.settings.checkUpdateKeePassXC = defaultSettings.checkUpdateKeePassXC;
|
||||
}
|
||||
if (!('autoCompleteUsernames' in page.settings)) {
|
||||
page.settings.autoCompleteUsernames = defaultSettings.autoCompleteUsernames;
|
||||
}
|
||||
if (!('autoFillAndSend' in page.settings)) {
|
||||
page.settings.autoFillAndSend = defaultSettings.autoFillAndSend;
|
||||
}
|
||||
if (!('usePasswordGeneratorIcons' in page.settings)) {
|
||||
page.settings.usePasswordGeneratorIcons = defaultSettings.usePasswordGeneratorIcons;
|
||||
}
|
||||
if (!('autoFillSingleEntry' in page.settings)) {
|
||||
page.settings.autoFillSingleEntry = defaultSettings.autoFillSingleEntry;
|
||||
}
|
||||
if (!('autoSubmit' in page.settings)) {
|
||||
page.settings.autoSubmit = defaultSettings.autoSubmit;
|
||||
}
|
||||
if (!('autoRetrieveCredentials' in page.settings)) {
|
||||
page.settings.autoRetrieveCredentials = defaultSettings.autoRetrieveCredentials;
|
||||
}
|
||||
if (!('showNotifications' in page.settings)) {
|
||||
page.settings.showNotifications = defaultSettings.showNotifications;
|
||||
}
|
||||
if (!('showLoginNotifications' in page.settings)) {
|
||||
page.settings.showLoginNotifications = defaultSettings.showLoginNotifications;
|
||||
}
|
||||
if (!('saveDomainOnly' in page.settings)) {
|
||||
page.settings.saveDomainOnly = defaultSettings.saveDomainOnly;
|
||||
}
|
||||
if (!('autoReconnect' in page.settings)) {
|
||||
page.settings.autoReconnect = defaultSettings.autoReconnect;
|
||||
}
|
||||
if (!('defaultGroup' in page.settings)) {
|
||||
page.settings.defaultGroup = defaultSettings.defaultGroup;
|
||||
}
|
||||
if (!('defaultGroupAlwaysAsk' in page.settings)) {
|
||||
page.settings.defaultGroupAlwaysAsk = defaultSettings.defaultGroupAlwaysAsk;
|
||||
}
|
||||
|
||||
await browser.storage.local.set({ 'settings': page.settings });
|
||||
return page.settings;
|
||||
} catch (err) {
|
||||
console.log('page.initSettings error: ' + err);
|
||||
return Promise.reject();
|
||||
}
|
||||
};
|
||||
|
||||
page.initOpenedTabs = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.tabs.query({}).then((tabs) => {
|
||||
for (const i of tabs) {
|
||||
page.createTabEntry(i.id);
|
||||
}
|
||||
page.initOpenedTabs = async function() {
|
||||
try {
|
||||
const tabs = await browser.tabs.query({});
|
||||
for (const i of tabs) {
|
||||
page.createTabEntry(i.id);
|
||||
}
|
||||
|
||||
// Set initial tab-ID
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((currentTabs) => {
|
||||
if (currentTabs.length === 0) {
|
||||
resolve();
|
||||
return; // For example: only the background devtools or a popup are opened
|
||||
}
|
||||
page.currentTabId = currentTabs[0].id;
|
||||
browserAction.show(null, currentTabs[0]);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
// Set initial tab-ID
|
||||
const currentTabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (currentTabs.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
page.currentTabId = currentTabs[0].id;
|
||||
browserAction.show(currentTabs[0]);
|
||||
return Promise.resolve();
|
||||
} catch (err) {
|
||||
console.log('page.initOpenedTabs error: ' + err);
|
||||
return Promise.reject();
|
||||
}
|
||||
};
|
||||
|
||||
page.isValidProtocol = function(url) {
|
||||
|
|
@ -98,8 +105,8 @@ page.isValidProtocol = function(url) {
|
|||
return !(url.indexOf('.') === -1 || (protocol !== 'http' && protocol !== 'https' && protocol !== 'ftp' && protocol !== 'sftp'));
|
||||
};
|
||||
|
||||
page.switchTab = function(callback, tab) {
|
||||
browserAction.showDefault(null, tab);
|
||||
page.switchTab = function(tab) {
|
||||
browserAction.showDefault(tab);
|
||||
browser.tabs.sendMessage(tab.id, { action: 'activated_tab' }).catch((e) => {});
|
||||
};
|
||||
|
||||
|
|
@ -108,7 +115,8 @@ page.clearCredentials = function(tabId, complete) {
|
|||
return;
|
||||
}
|
||||
|
||||
page.tabs[tabId].credentials = {};
|
||||
page.usernameFieldDetected = false;
|
||||
page.tabs[tabId].credentials = [];
|
||||
delete page.tabs[tabId].credentials;
|
||||
|
||||
if (complete) {
|
||||
|
|
@ -128,30 +136,43 @@ page.clearLogins = function(tabId) {
|
|||
page.tabs[tabId].loginList = [];
|
||||
};
|
||||
|
||||
page.setSubmittedCredentials = function(submitted, username, password, url, oldCredentials) {
|
||||
page.submittedCredentials.submitted = submitted;
|
||||
page.submittedCredentials.username = username;
|
||||
page.submittedCredentials.password = password;
|
||||
page.submittedCredentials.url = url;
|
||||
page.submittedCredentials.oldCredentials = oldCredentials;
|
||||
};
|
||||
|
||||
page.clearSubmittedCredentials = function() {
|
||||
page.submitted = false;
|
||||
page.submittedCredentials = {};
|
||||
};
|
||||
|
||||
page.createTabEntry = function(tabId) {
|
||||
page.tabs[tabId] = {
|
||||
'stack': [],
|
||||
'errorMessage': null,
|
||||
'loginList': []
|
||||
};
|
||||
page.clearSubmittedCredentials();
|
||||
};
|
||||
|
||||
page.removePageInformationFromNotExistingTabs = function() {
|
||||
page.removePageInformationFromNotExistingTabs = async function() {
|
||||
const rand = Math.floor(Math.random() * 1001);
|
||||
if (rand === 28) {
|
||||
browser.tabs.query({}).then(function(tabs) {
|
||||
const tabIds = [];
|
||||
const infoIds = Object.keys(page.tabs);
|
||||
const tabs = await browser.tabs.query({});
|
||||
const tabIds = [];
|
||||
const infoIds = Object.keys(page.tabs);
|
||||
|
||||
for (const t of tabs) {
|
||||
tabIds[t.id] = true;
|
||||
}
|
||||
for (const t of tabs) {
|
||||
tabIds[t.id] = true;
|
||||
}
|
||||
|
||||
for (const i of infoIds) {
|
||||
if (!(i in tabIds)) {
|
||||
delete page.tabs[i];
|
||||
}
|
||||
for (const i of infoIds) {
|
||||
if (!(i in tabIds)) {
|
||||
delete page.tabs[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,13 +34,9 @@ kpxcAutocomplete.create = function(input, showListInstantly = false, autoSubmit
|
|||
kpxcAutocomplete.showList = function(inputField) {
|
||||
kpxcAutocomplete.closeList();
|
||||
kpxcAutocomplete.input = inputField;
|
||||
const div = kpxcUI.createElement('div', 'kpxcAutocomplete-items', { 'id': 'kpxcAutocomplete-list' });
|
||||
|
||||
// Element position
|
||||
const rect = inputField.getBoundingClientRect();
|
||||
div.style.top = String((rect.top + document.body.scrollTop) + inputField.offsetHeight) + 'px';
|
||||
div.style.left = String((rect.left + document.body.scrollLeft)) + 'px';
|
||||
div.style.minWidth = String(inputField.offsetWidth) + 'px';
|
||||
const div = kpxcUI.createElement('div', 'kpxcAutocomplete-items', { 'id': 'kpxcAutocomplete-list' });
|
||||
kpxcAutocomplete.updatePosition(inputField, div);
|
||||
div.style.zIndex = '2147483646';
|
||||
document.body.append(div);
|
||||
|
||||
|
|
@ -56,6 +52,10 @@ kpxcAutocomplete.showList = function(inputField) {
|
|||
|
||||
// Save index for combination.loginId
|
||||
const index = Array.prototype.indexOf.call(e.currentTarget.parentElement.childNodes, e.currentTarget);
|
||||
browser.runtime.sendMessage({
|
||||
action: 'page_set_login_id', args: [ index ]
|
||||
});
|
||||
|
||||
inputField.value = this.getElementsByTagName('input')[0].value;
|
||||
kpxcAutocomplete.fillPassword(inputField.value, index);
|
||||
kpxcAutocomplete.closeList();
|
||||
|
|
@ -188,6 +188,7 @@ kpxcAutocomplete.keyPress = function(e) {
|
|||
kpxcAutocomplete.fillPassword = function(value, index) {
|
||||
const fieldId = kpxcAutocomplete.input.getAttribute('data-kpxc-id');
|
||||
kpxcFields.prepareId(fieldId);
|
||||
|
||||
const givenType = kpxcAutocomplete.input.type === 'password' ? 'password' : 'username';
|
||||
const combination = kpxcFields.getCombination(givenType, fieldId);
|
||||
combination.loginId = index;
|
||||
|
|
@ -196,6 +197,18 @@ kpxcAutocomplete.fillPassword = function(value, index) {
|
|||
kpxcAutocomplete.input.setAttribute('fetched', true);
|
||||
};
|
||||
|
||||
kpxcAutocomplete.updatePosition = function(inputField, elem) {
|
||||
const div = elem || $('.kpxcAutocomplete-items');
|
||||
if (!div) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = inputField.getBoundingClientRect();
|
||||
div.style.top = Pixels((rect.top + document.scrollingElement.scrollTop) + inputField.offsetHeight);
|
||||
div.style.left = Pixels((rect.left + document.scrollingElement.scrollLeft));
|
||||
div.style.minWidth = Pixels(inputField.offsetWidth);
|
||||
};
|
||||
|
||||
// Detect click outside autocomplete
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
|
|
@ -207,7 +220,31 @@ document.addEventListener('click', function(e) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (e.target !== kpxcAutocomplete.input && (e.target.nodeName !== kpxcAutocomplete.input.nodeName)) {
|
||||
if (e.target !== kpxcAutocomplete.input &&
|
||||
!e.target.classList.contains('kpxc-username-icon') &&
|
||||
e.target.nodeName !== kpxcAutocomplete.input.nodeName) {
|
||||
kpxcAutocomplete.closeList(e.target);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle autocomplete position on window resize
|
||||
window.addEventListener('resize', function() {
|
||||
if (!kpxc.settings.autoCompleteUsernames) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kpxcAutocomplete.input) {
|
||||
kpxcAutocomplete.updatePosition(kpxcAutocomplete.input);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle autocomplete position on scroll
|
||||
window.addEventListener('scroll', function() {
|
||||
if (!kpxc.settings.autoCompleteUsernames) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kpxcAutocomplete.input) {
|
||||
kpxcAutocomplete.updatePosition(kpxcAutocomplete.input);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
390
keepassxc-browser/content/banner.js
Normal file
390
keepassxc-browser/content/banner.js
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
'use strict';
|
||||
|
||||
const DEFAULT_BROWSER_GROUP = 'KeePassXC-Browser Passwords';
|
||||
|
||||
var kpxcBanner = {};
|
||||
kpxcBanner.banner = undefined;
|
||||
kpxcBanner.created = false;
|
||||
kpxcBanner.credentials = {};
|
||||
|
||||
kpxcBanner.destroy = function() {
|
||||
kpxcBanner.created = false;
|
||||
kpxcBanner.credentials = {};
|
||||
|
||||
const dialog = $('.kpxc-banner-dialog');
|
||||
if (dialog) {
|
||||
document.body.removeChild(dialog);
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'remove_credentials_from_tab_information'
|
||||
});
|
||||
|
||||
const banners = document.querySelectorAll('.kpxc-banner');
|
||||
if (banners.length > 0) {
|
||||
for (const b of banners) {
|
||||
document.body.removeChild(b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.removeChild(kpxcBanner.banner);
|
||||
};
|
||||
|
||||
kpxcBanner.create = async function(credentials = {}) {
|
||||
if (!kpxc.settings.showLoginNotifications || kpxcBanner.created) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if database is closed
|
||||
const state = await browser.runtime.sendMessage({ action: 'check_database_hash' });
|
||||
if (state === '') {
|
||||
//kpxcUI.createNotification('error', tr('rememberErrorDatabaseClosed'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show anything if the site is in the ignore
|
||||
if (kpxc.settings.sitePreferences !== undefined) {
|
||||
for (const site of kpxc.settings.sitePreferences) {
|
||||
if (site.ignore !== IGNORE_NOTHING && (site.url === credentials.url || siteMatch(site.url, credentials.url))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kpxcBanner.created = true;
|
||||
kpxcBanner.credentials = credentials;
|
||||
|
||||
const banner = kpxcUI.createElement('div', 'kpxc-banner', { 'id': 'container' });
|
||||
banner.style.zIndex = '2147483646';
|
||||
|
||||
const bannerInfo = kpxcUI.createElement('div', 'banner-info');
|
||||
const bannerButtons = kpxcUI.createElement('div', 'banner-buttons');
|
||||
|
||||
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
|
||||
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
|
||||
|
||||
const infoText = kpxcUI.createElement('span', '', {}, tr('rememberInfoText'));
|
||||
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 separator = kpxcUI.createElement('div', 'separator');
|
||||
const ignoreCheckbox = kpxcUI.createElement('input', 'kpxc-checkbox', { type: 'checkbox', name: 'ignoreCheckbox' });
|
||||
const checkboxLabel = kpxcUI.createElement('label', 'kpxc-checkbox-label', { for: 'ignoreCheckbox' }, tr('popupButtonIgnore'));
|
||||
|
||||
// No existing credentials to update --> disable Update button
|
||||
if (credentials.list.length === 0) {
|
||||
updateButton.classList.remove('kpxc-orange-button');
|
||||
updateButton.disabled = true;
|
||||
}
|
||||
|
||||
newButton.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
kpxcBanner.saveNewCredentials(credentials);
|
||||
});
|
||||
|
||||
updateButton.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
kpxcBanner.updateCredentials(credentials);
|
||||
});
|
||||
|
||||
dismissButton.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a banner dialog is shown, display the main banner
|
||||
const dialog = $('.kpxc-banner-dialog');
|
||||
if (dialog) {
|
||||
$('#kpxc-banner-btn-new').hidden = false;
|
||||
$('#kpxc-banner-btn-update').hidden = false;
|
||||
$('.kpxc-checkbox').disabled = false;
|
||||
document.body.removeChild(dialog);
|
||||
} else {
|
||||
if (ignoreCheckbox.checked) {
|
||||
kpxc.ignoreSite([ window.top.location.href ]);
|
||||
}
|
||||
kpxcBanner.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
kpxcBanner.banner = banner;
|
||||
bannerInfo.appendMultiple(icon, infoText, usernameText, usernameSpan);
|
||||
bannerButtons.appendMultiple(newButton, updateButton, separator, ignoreCheckbox, checkboxLabel, dismissButton);
|
||||
banner.appendMultiple(bannerInfo, bannerButtons);
|
||||
document.body.appendChild(banner);
|
||||
};
|
||||
|
||||
kpxcBanner.saveNewCredentials = async function(credentials = {}) {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
action: 'get_database_groups'
|
||||
});
|
||||
|
||||
// Only the Root group and no KeePassXC-Browser passwords -> save to default
|
||||
// Or when default group is not set and defaultGroupAskAlways is disabled -> save to default
|
||||
if ((result.groups === undefined || (result.groups.length > 0 && result.groups[0].children.length === 0)) ||
|
||||
(!result.defaultGroupAlwaysAsk && (result.defaultGroup === '' || result.defaultGroup === DEFAULT_BROWSER_GROUP))) {
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
return;
|
||||
} else if (!result.defaultGroupAlwaysAsk && (result.defaultGroup !== '' || result.defaultGroup !== DEFAULT_BROWSER_GROUP)) {
|
||||
// Another group name has been specified
|
||||
const [ gname, guuid ] = kpxcBanner.getDefaultGroup(result.groups[0].children, result.defaultGroup);
|
||||
if (gname === '' && guuid === '') {
|
||||
// Root group is used -> use the root path
|
||||
if (result.defaultGroup.toLowerCase() === 'root') {
|
||||
result.defaultGroup = '/';
|
||||
}
|
||||
|
||||
// Create a new group
|
||||
const newGroup = await browser.runtime.sendMessage({
|
||||
action: 'create_new_group',
|
||||
args: [ result.defaultGroup ]
|
||||
});
|
||||
|
||||
if (newGroup.name && newGroup.uuid) {
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url, newGroup.name, newGroup.uuid ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
} else {
|
||||
kpxcUI.createNotification('error', tr('rememberErrorCreatingNewGroup'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url, gname, guuid ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const addChildren = function(group, parentElement, depth) {
|
||||
++depth;
|
||||
const padding = depth * 20;
|
||||
|
||||
for (const child of group.children) {
|
||||
const a = createLink(child.name, child.uuid, child.children.length > 0);
|
||||
a.setAttribute('id', 'child');
|
||||
a.style.paddingLeft = Pixels(padding);
|
||||
|
||||
if (parentElement.getAttribute('id') === 'root') {
|
||||
a.setAttribute('id', 'root-child');
|
||||
}
|
||||
|
||||
$('ul#list').appendChild(a);
|
||||
addChildren(child, a, depth);
|
||||
}
|
||||
};
|
||||
|
||||
const createLink = function(group, groupUuid, hasChildren) {
|
||||
const a = kpxcUI.createElement('a', 'list-group-item', { 'href': '#' }, group);
|
||||
a.addEventListener('click', async function(e) {
|
||||
e.preventDefault();
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ credentials.username, credentials.password, credentials.url, group, groupUuid ]
|
||||
});
|
||||
|
||||
kpxcBanner.verifyResult(res);
|
||||
});
|
||||
|
||||
if (hasChildren) {
|
||||
a.textContent = '\u25BE ' + group;
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
kpxcBanner.createGroupDialog();
|
||||
|
||||
// Create the link list for group selection
|
||||
let depth = 0;
|
||||
for (const g of result.groups) {
|
||||
const a = createLink(g.name, g.uuid, g.children.length > 0);
|
||||
a.setAttribute('id', 'root');
|
||||
|
||||
$('ul#list').appendChild(a);
|
||||
addChildren(g, a, depth);
|
||||
}
|
||||
|
||||
$('.kpxc-banner-dialog').style.display = 'block';
|
||||
};
|
||||
|
||||
kpxcBanner.updateCredentials = async function(credentials = {}) {
|
||||
// Only one entry which could be updated
|
||||
if (credentials.list.length === 1) {
|
||||
// Use the current username if it's empty
|
||||
if (!credentials.username) {
|
||||
credentials.username = credentials.list[0].login;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'update_credentials',
|
||||
args: [ credentials.list[0].uuid, credentials.username, credentials.password, credentials.url ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
} else {
|
||||
await kpxcBanner.createCredentialDialog();
|
||||
$('.kpxc-banner-dialog .username-new .strong').textContent = credentials.username;
|
||||
$('.kpxc-banner-dialog .username-exists .strong').textContent = credentials.username;
|
||||
|
||||
if (credentials.usernameExists) {
|
||||
$('.kpxc-banner-dialog .username-new').style.display = 'none';
|
||||
$('.kpxc-banner-dialog .username-exists').style.display = 'block';
|
||||
} else {
|
||||
$('.kpxc-banner-dialog .username-new').style.display = 'block';
|
||||
$('.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})`);
|
||||
a.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entryId = e.target.getAttribute('entryId');
|
||||
|
||||
// Use the current username if it's empty
|
||||
if (!credentials.username) {
|
||||
credentials.username = credentials.list[entryId].login;
|
||||
}
|
||||
|
||||
let url = credentials.url;
|
||||
url = (url.length > 50) ? url.substring(0, 50) + '...' : url;
|
||||
|
||||
// Check if the password has changed for the updated credentials
|
||||
browser.runtime.sendMessage({
|
||||
action: 'retrieve_credentials',
|
||||
args: [ url, '', true ] // Sets triggerUnlock to true
|
||||
}).then(async (creds) => {
|
||||
if (!creds || creds.length !== credentials.list.length) {
|
||||
kpxcBanner.verifyResult('error');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await browser.runtime.sendMessage({
|
||||
action: 'update_credentials',
|
||||
args: [ credentials.list[entryId].uuid, credentials.username, credentials.password, credentials.url ]
|
||||
});
|
||||
kpxcBanner.verifyResult(res);
|
||||
});
|
||||
});
|
||||
|
||||
if (credentials.usernameExists && credentials.username === credentials.list[i].login) {
|
||||
a.style.fontWeight = 'bold';
|
||||
}
|
||||
|
||||
$('ul#list').appendChild(a);
|
||||
}
|
||||
|
||||
$('.kpxc-banner-dialog').style.display = 'block';
|
||||
}
|
||||
};
|
||||
|
||||
kpxcBanner.verifyResult = function(code) {
|
||||
if (code === 'error') {
|
||||
kpxcUI.createNotification('error', tr('rememberErrorCannotSaveCredentials'));
|
||||
} else if (code === 'created') {
|
||||
kpxcUI.createNotification('success', tr('rememberCredentialsSaved', kpxcBanner.credentials.username || tr('rememberEmptyUsername')));
|
||||
} else if (code === 'updated') {
|
||||
kpxcUI.createNotification('success', tr('rememberCredentialsUpdated', kpxcBanner.credentials.username || tr('rememberEmptyUsername')));
|
||||
} else if (code === 'canceled') {
|
||||
kpxcUI.createNotification('warning', tr('rememberCredentialsNotSaved'));
|
||||
} else {
|
||||
kpxcUI.createNotification('error', tr('rememberErrorDatabaseClosed'));
|
||||
}
|
||||
kpxcBanner.destroy();
|
||||
};
|
||||
|
||||
// Traverse the groups and ensure all paths are found
|
||||
kpxcBanner.getDefaultGroup = function(groups, defaultGroup) {
|
||||
const getGroup = function(group, splitted, depth) {
|
||||
++depth;
|
||||
for (const g of group) {
|
||||
if (g.name === splitted[depth]) {
|
||||
if (splitted.length === (depth + 1)) {
|
||||
return [ g.name, g.uuid ];
|
||||
}
|
||||
return getGroup(g.children, splitted, depth);
|
||||
}
|
||||
}
|
||||
return [ '', '' ];
|
||||
};
|
||||
|
||||
let depth = -1;
|
||||
const splitted = defaultGroup.split('/');
|
||||
return getGroup(groups, splitted, depth);
|
||||
};
|
||||
|
||||
kpxcBanner.createCredentialDialog = async function() {
|
||||
$('#kpxc-banner-btn-new').hidden = true;
|
||||
$('#kpxc-banner-btn-update').hidden = true;
|
||||
$('.kpxc-checkbox').disabled = true;
|
||||
|
||||
const connectedDatabase = await browser.runtime.sendMessage({
|
||||
action: 'get_connected_database'
|
||||
});
|
||||
const databaseName = connectedDatabase.count > 0 ? connectedDatabase.identifier : '';
|
||||
|
||||
const dialog = kpxcUI.createElement('div', 'kpxc-banner-dialog');
|
||||
const databaseText = kpxcUI.createElement('p');
|
||||
const spanDatabaseText = kpxcUI.createElement('span', '', {}, tr('rememberSaving'));
|
||||
const usernameNew = kpxcUI.createElement('p', 'username-new');
|
||||
const usernameExists = kpxcUI.createElement('p', 'username-exists');
|
||||
const chooseCreds = kpxcUI.createElement('p', '', {}, tr('rememberChooseCredentials'));
|
||||
const list = kpxcUI.createElement('ul', 'list-group', { 'id': 'list' });
|
||||
const spanNewUsername = kpxcUI.createElement('span', '', {}, tr('rememberNewUsername'));
|
||||
const spanUsernameExists = kpxcUI.createElement('span', '', {}, tr('rememberUsernameExists'));
|
||||
|
||||
// Set dialog position
|
||||
dialog.style.top = Pixels(kpxcBanner.banner.offsetHeight);
|
||||
dialog.style.right = '0';
|
||||
|
||||
databaseText.appendChild(spanDatabaseText);
|
||||
const spanName = kpxcUI.createElement('span', 'strong', {}, databaseName);
|
||||
databaseText.append(spanName);
|
||||
|
||||
usernameNew.appendChild(spanNewUsername);
|
||||
usernameExists.appendChild(spanUsernameExists);
|
||||
usernameNew.append(kpxcUI.createElement('span', 'strong'));
|
||||
usernameExists.append(kpxcUI.createElement('span', 'strong'));
|
||||
dialog.appendMultiple(databaseText, usernameNew, usernameExists, chooseCreds, list);
|
||||
document.body.appendChild(dialog);
|
||||
};
|
||||
|
||||
kpxcBanner.createGroupDialog = function() {
|
||||
$('#kpxc-banner-btn-new').hidden = true;
|
||||
$('#kpxc-banner-btn-update').hidden = true;
|
||||
$('.kpxc-checkbox').disabled = true;
|
||||
|
||||
const dialog = kpxcUI.createElement('div', 'kpxc-banner-dialog');
|
||||
const chooseGroup = kpxcUI.createElement('p', '', {}, tr('rememberChooseGroup'));
|
||||
const list = kpxcUI.createElement('ul', 'list-group', { 'id': 'list' });
|
||||
|
||||
// Set dialog position
|
||||
dialog.style.top = Pixels(kpxcBanner.banner.offsetHeight);
|
||||
dialog.style.right = Pixels(0);
|
||||
|
||||
dialog.appendMultiple(chooseGroup, list);
|
||||
document.body.appendChild(dialog);
|
||||
};
|
||||
|
|
@ -134,6 +134,10 @@ kpxcDefine.isFieldSelected = function(kpxcId) {
|
|||
|
||||
kpxcDefine.markAllUsernameFields = function(chooser) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
kpxcDefine.selection.username = field.getAttribute('data-kpxc-id');
|
||||
field.classList.add('kpxcDefine-fixed-username-field');
|
||||
|
|
@ -147,6 +151,10 @@ kpxcDefine.markAllUsernameFields = function(chooser) {
|
|||
|
||||
kpxcDefine.markAllPasswordFields = function(chooser, more = false) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
kpxcDefine.selection.password = field.getAttribute('data-kpxc-id');
|
||||
field.classList.add('kpxcDefine-fixed-password-field');
|
||||
|
|
@ -164,6 +172,10 @@ kpxcDefine.markAllPasswordFields = function(chooser, more = false) {
|
|||
|
||||
kpxcDefine.markAllStringFields = function(chooser) {
|
||||
kpxcDefine.eventFieldClick = function(e, elem) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const field = elem || e.currentTarget;
|
||||
const value = field.getAttribute('data-kpxc-id');
|
||||
kpxcDefine.selection.fields[value] = true;
|
||||
|
|
@ -189,10 +201,10 @@ kpxcDefine.markFields = function(chooser, pattern) {
|
|||
if (kpxcFields.isVisible(i)) {
|
||||
const field = kpxcUI.createElement('div', 'kpxcDefine-fixed-field', { 'data-kpxc-id': i.getAttribute('data-kpxc-id') });
|
||||
const rect = i.getBoundingClientRect();
|
||||
field.style.top = rect.top + 'px';
|
||||
field.style.left = rect.left + 'px';
|
||||
field.style.width = rect.width + 'px';
|
||||
field.style.height = rect.height + 'px';
|
||||
field.style.top = Pixels(rect.top);
|
||||
field.style.left = Pixels(rect.left);
|
||||
field.style.width = Pixels(rect.width);
|
||||
field.style.height = Pixels(rect.height);
|
||||
field.textContent = String(index);
|
||||
field.addEventListener('click', function(e) {
|
||||
kpxcDefine.eventFieldClick(e);
|
||||
|
|
@ -262,7 +274,11 @@ kpxcDefine.prepareStep3 = function() {
|
|||
$('#kpxcDefine-btn-confirm').style.display = 'inline-block';
|
||||
};
|
||||
|
||||
kpxcDefine.skip = function() {
|
||||
kpxcDefine.skip = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kpxcDefine.dataStep === 1) {
|
||||
kpxcDefine.selection.username = null;
|
||||
kpxcDefine.prepareStep2();
|
||||
|
|
@ -274,20 +290,32 @@ kpxcDefine.skip = function() {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcDefine.again = function() {
|
||||
kpxcDefine.again = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
kpxcDefine.resetSelection();
|
||||
kpxcDefine.prepareStep1();
|
||||
kpxcDefine.markAllUsernameFields('#kpxcDefine-fields');
|
||||
};
|
||||
|
||||
kpxcDefine.more = function() {
|
||||
kpxcDefine.more = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kpxcDefine.dataStep === 2) {
|
||||
kpxcDefine.prepareStep2();
|
||||
kpxcDefine.markAllPasswordFields('#kpxcDefine-fields', true);
|
||||
}
|
||||
};
|
||||
|
||||
kpxcDefine.confirm = function() {
|
||||
kpxcDefine.confirm = async function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kpxcDefine.dataStep !== 3) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -317,7 +345,7 @@ kpxcDefine.confirm = function() {
|
|||
fields: fieldIds
|
||||
};
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'save_settings',
|
||||
args: [ kpxc.settings ]
|
||||
});
|
||||
|
|
@ -325,7 +353,11 @@ kpxcDefine.confirm = function() {
|
|||
kpxcDefine.close();
|
||||
};
|
||||
|
||||
kpxcDefine.discard = function() {
|
||||
kpxcDefine.discard = async function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$('#kpxcDefine-btn-discard')) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -333,12 +365,12 @@ kpxcDefine.discard = function() {
|
|||
const location = kpxc.getDocumentLocation();
|
||||
delete kpxc.settings['defined-custom-fields'][location];
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'save_settings',
|
||||
args: [ kpxc.settings ]
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'load_settings'
|
||||
});
|
||||
|
||||
|
|
@ -347,6 +379,10 @@ kpxcDefine.discard = function() {
|
|||
|
||||
// Handle the keyboard events
|
||||
kpxcDefine.keyDown = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
kpxcDefine.close();
|
||||
} else if (e.key === 'Enter') {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -11,41 +11,18 @@ kpxcPassword.diffX = 0;
|
|||
kpxcPassword.diffY = 0;
|
||||
kpxcPassword.dialog = null;
|
||||
kpxcPassword.titleBar = null;
|
||||
kpxcPassword.useIcons = false;
|
||||
|
||||
const DOMRectToArray = function(domRect) {
|
||||
return [ domRect.bottom, domRect.height, domRect.left, domRect.right, domRect.top, domRect.width, domRect.x, domRect.y ];
|
||||
};
|
||||
|
||||
/**
|
||||
* Detects if the input field appears or disappears -> show/hide the icon
|
||||
* - boundingClientRect with slightly (< -10) negative values -> hidden
|
||||
* - intersectionRatio === 0 -> hidden
|
||||
* - isIntersecting === false -> hidden
|
||||
* - intersectionRatio > 0 -> shown
|
||||
* - isIntersecting === true -> shown
|
||||
*/
|
||||
try {
|
||||
kpxcPassword.observer = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const rect = DOMRectToArray(entry.boundingClientRect);
|
||||
|
||||
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
|
||||
kpxcPassword.icon.style.display = 'none';
|
||||
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
|
||||
kpxcPassword.icon.style.display = 'block';
|
||||
|
||||
// Wait for possible DOM animations
|
||||
setTimeout(() => {
|
||||
kpxcPassword.setIconPosition(kpxcPassword.icon, entry.target);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
kpxcUI.updateFromIntersectionObserver(kpxcPassword, entries);
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
kpxcPassword.init = function() {
|
||||
kpxcPassword.init = function(useIcons) {
|
||||
kpxcPassword.useIcons = useIcons;
|
||||
if ('initPasswordGenerator' in _called) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -58,24 +35,26 @@ kpxcPassword.initField = function(field, inputs, pos) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Observer the visibility
|
||||
if (kpxcPassword.observer) {
|
||||
kpxcPassword.observer.observe(field);
|
||||
}
|
||||
|
||||
if (field.getAttribute('kpxc-password-generator')) {
|
||||
return;
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-password-generator', true);
|
||||
|
||||
kpxcPassword.createIcon(field);
|
||||
if (kpxcPassword.useIcons) {
|
||||
// Observer the visibility
|
||||
if (kpxcPassword.observer) {
|
||||
kpxcPassword.observer.observe(field);
|
||||
}
|
||||
kpxcPassword.createIcon(field);
|
||||
}
|
||||
|
||||
kpxcPassword.inputField = field;
|
||||
|
||||
let found = false;
|
||||
if (inputs) {
|
||||
for (let i = pos + 1; i < inputs.length; i++) {
|
||||
if (inputs[i] && inputs[i].getAttribute('type') && inputs[i].getAttribute('type').toLowerCase() === 'password') {
|
||||
if (inputs[i] && inputs[i].getLowerCaseAttribute('type') === 'password') {
|
||||
field.setAttribute('kpxc-pwgen-next-field-id', inputs[i].getAttribute('data-kpxc-id'));
|
||||
field.setAttribute('kpxc-pwgen-next-is-password-field', (i === 0));
|
||||
found = true;
|
||||
|
|
@ -101,27 +80,33 @@ kpxcPassword.createIcon = function(field) {
|
|||
'offset': offset,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
|
||||
});
|
||||
icon.style.zIndex = '9999';
|
||||
icon.style.width = String(size) + 'px';
|
||||
icon.style.height = String(size) + 'px';
|
||||
icon.style.zIndex = '10000000';
|
||||
icon.style.width = Pixels(size);
|
||||
icon.style.height = Pixels(size);
|
||||
|
||||
icon.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
kpxcPassword.showDialog(field, icon);
|
||||
});
|
||||
|
||||
kpxcPassword.setIconPosition(icon, field);
|
||||
kpxcUI.setIconPosition(icon, field);
|
||||
kpxcPassword.icon = icon;
|
||||
document.body.appendChild(icon);
|
||||
};
|
||||
|
||||
kpxcPassword.setIconPosition = function(icon, field) {
|
||||
const rect = field.getBoundingClientRect();
|
||||
const offset = Number(icon.getAttribute('offset'));
|
||||
const size = Number(icon.getAttribute('size'));
|
||||
|
||||
icon.style.top = String((rect.top + document.scrollingElement.scrollTop) + offset + 1) + 'px';
|
||||
icon.style.left = String((rect.left + document.scrollingElement.scrollLeft) + field.offsetWidth - size - offset) + 'px';
|
||||
kpxcPassword.removeIcon = function(field) {
|
||||
if (field.getAttribute('kpxc-password-generator')) {
|
||||
const pwgenIcons = document.querySelectorAll('.kpxc-pwgen-icon');
|
||||
for (const i of pwgenIcons) {
|
||||
if (i.getAttribute('kpxc-pwgen-field-id') === field.getAttribute('data-kpxc-id')) {
|
||||
document.body.removeChild(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.createDialog = function() {
|
||||
|
|
@ -140,45 +125,50 @@ kpxcPassword.createDialog = function() {
|
|||
const titleBar = kpxcUI.createElement('div', 'kpxc-pwgen-titlebar', {}, tr('passwordGeneratorTitle'));
|
||||
const closeButton = kpxcUI.createElement('div', 'kpxc-pwgen-close', {}, '×');
|
||||
closeButton.addEventListener('click', function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
kpxcPassword.openDialog();
|
||||
});
|
||||
titleBar.append(closeButton);
|
||||
|
||||
const passwordRow = kpxcUI.createElement('div', 'kpxc-pwgen-password-row');
|
||||
const input = kpxcUI.createElement('input', 'kpxc-pwgen-input', { 'placeholder': tr('passwordGeneratorPlaceholder'), 'type': 'text', 'tabindex': '-1' });
|
||||
const inputLabel = kpxcUI.createElement('label', 'kpxc-pwgen-bits', {}, tr('passwordGeneratorBits', '???'));
|
||||
passwordRow.appendMultiple(input, inputLabel);
|
||||
|
||||
const nextFillRow = kpxcUI.createElement('div', 'kpxc-pwgen-nextfill-row');
|
||||
const checkbox = kpxcUI.createElement('input', 'kpxc-pwgen-checkbox', { 'id': 'kpxc-pwgen-checkbox', 'type': 'checkbox' });
|
||||
const checkboxLabel = kpxcUI.createElement('label', 'kpxc-pwgen-checkbox-label', { 'for': 'kpxc-pwgen-checkbox' }, tr('passwordGeneratorLabel'));
|
||||
nextFillRow.appendMultiple(checkbox, checkboxLabel);
|
||||
passwordRow.appendMultiple(input);
|
||||
|
||||
// Buttons
|
||||
const buttonsRow = kpxcUI.createElement('div', 'kpxc-pwgen-buttons');
|
||||
const generateButton = kpxcUI.createElement('button', 'kpxc-button kpxc-white-button', { 'id': 'kpxc-pwgen-btn-generate' }, tr('passwordGeneratorGenerate'));
|
||||
const copyButton = kpxcUI.createElement('button', 'kpxc-button', { 'id': 'kpxc-pwgen-btn-copy' }, tr('passwordGeneratorCopy'));
|
||||
const fillButton = kpxcUI.createElement('button', 'kpxc-button', { 'id': 'kpxc-pwgen-btn-fill' }, tr('passwordGeneratorFillAndCopy'));
|
||||
const generateButton = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxc-pwgen-btn-generate' }, tr('passwordGeneratorGenerate'));
|
||||
const copyButton = kpxcUI.createElement('button', 'kpxc-button kpxc-orange-button', { 'id': 'kpxc-pwgen-btn-copy' }, tr('passwordGeneratorCopy'));
|
||||
const fillButton = kpxcUI.createElement('button', 'kpxc-button kpxc-green-button', { 'id': 'kpxc-pwgen-btn-fill' }, tr('passwordGeneratorFill'));
|
||||
|
||||
generateButton.addEventListener('click', function(e) {
|
||||
kpxcPassword.generate(e);
|
||||
});
|
||||
|
||||
fillButton.addEventListener('click', function(e) {
|
||||
kpxcPassword.fill(e);
|
||||
kpxcPassword.openDialog();
|
||||
});
|
||||
|
||||
copyButton.addEventListener('click', function(e) {
|
||||
kpxcPassword.copy(e);
|
||||
});
|
||||
|
||||
fillButton.addEventListener('click', function(e) {
|
||||
kpxcPassword.fill(e);
|
||||
});
|
||||
|
||||
buttonsRow.appendMultiple(generateButton, copyButton, fillButton);
|
||||
dialog.appendMultiple(titleBar, passwordRow, nextFillRow, buttonsRow);
|
||||
dialog.appendMultiple(titleBar, passwordRow, buttonsRow);
|
||||
wrapper.append(dialog);
|
||||
|
||||
const icon = $('.kpxc-pwgen-icon');
|
||||
dialog.style.top = String(icon.offsetTop + icon.offsetHeight) + 'px';
|
||||
dialog.style.left = icon.style.left;
|
||||
if (icon) {
|
||||
dialog.style.top = Pixels(icon.offsetTop + icon.offsetHeight);
|
||||
dialog.style.left = icon.style.left;
|
||||
} else {
|
||||
const rect = document.activeElement.getBoundingClientRect();
|
||||
dialog.style.top = Pixels(rect.top + rect.height);
|
||||
dialog.style.left = Pixels(rect.left);
|
||||
}
|
||||
|
||||
document.body.append(wrapper);
|
||||
|
||||
|
|
@ -209,7 +199,7 @@ kpxcPassword.openDialog = function() {
|
|||
};
|
||||
|
||||
kpxcPassword.trigger = function() {
|
||||
kpxcPassword.showDialog(kpxcPassword.inputField, kpxcPassword.icon);
|
||||
kpxcPassword.showDialog(kpxcPassword.inputField || document.activeElement, kpxcPassword.icon);
|
||||
};
|
||||
|
||||
kpxcPassword.showDialog = function(field, icon) {
|
||||
|
|
@ -224,8 +214,14 @@ kpxcPassword.showDialog = function(field, icon) {
|
|||
|
||||
// Adjust the dialog location
|
||||
if (kpxcPassword.dialog) {
|
||||
kpxcPassword.dialog.style.top = String(icon.offsetTop + icon.offsetHeight) + 'px';
|
||||
kpxcPassword.dialog.style.left = icon.style.left;
|
||||
if (icon) {
|
||||
kpxcPassword.dialog.style.top = Pixels(icon.offsetTop + icon.offsetHeight);
|
||||
kpxcPassword.dialog.style.left = icon.style.left;
|
||||
} else {
|
||||
const rect = document.activeElement.getBoundingClientRect();
|
||||
kpxcPassword.dialog.style.top = Pixels(rect.top + rect.height);
|
||||
kpxcPassword.dialog.style.left = Pixels(rect.left);
|
||||
}
|
||||
|
||||
kpxcPassword.dialog.setAttribute('kpxc-pwgen-field-id', field.getAttribute('data-kpxc-id'));
|
||||
kpxcPassword.dialog.setAttribute('kpxc-pwgen-next-field-id', field.getAttribute('kpxc-pwgen-next-field-id'));
|
||||
|
|
@ -244,27 +240,34 @@ kpxcPassword.showDialog = function(field, icon) {
|
|||
}
|
||||
};
|
||||
|
||||
kpxcPassword.generate = function(e) {
|
||||
kpxcPassword.generate = async function(e) {
|
||||
// This function can be also called from non-events
|
||||
if (e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
callbackGeneratedPassword(await browser.runtime.sendMessage({
|
||||
action: 'generate_password'
|
||||
}).then(kpxcPassword.callbackGeneratedPassword).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
kpxcPassword.copy = function(e) {
|
||||
e.preventDefault();
|
||||
if (kpxcPassword.copyPasswordToClipboard()) {
|
||||
kpxcPassword.greenButton('#kpxc-pwgen-btn-copy');
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-fill');
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
kpxcPassword.copyPasswordToClipboard();
|
||||
};
|
||||
|
||||
kpxcPassword.fill = function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Use the active input field
|
||||
|
|
@ -285,19 +288,10 @@ kpxcPassword.fill = function(e) {
|
|||
}
|
||||
|
||||
field.value = password.value;
|
||||
if ($('.kpxc-pwgen-checkbox').checked) {
|
||||
if (field.getAttribute('kpxc-pwgen-next-field-id')) {
|
||||
const nextFieldId = field.getAttribute('kpxc-pwgen-next-field-id');
|
||||
const nextField = $('input[data-kpxc-id=\'' + nextFieldId + '\']');
|
||||
if (nextField) {
|
||||
nextField.value = password.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kpxcPassword.copyPasswordToClipboard()) {
|
||||
kpxcPassword.greenButton('#kpxc-pwgen-btn-fill');
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-copy');
|
||||
const nextFieldId = field.getAttribute('kpxc-pwgen-next-field-id');
|
||||
const nextField = $('input[data-kpxc-id=\'' + nextFieldId + '\']');
|
||||
if (nextField) {
|
||||
nextField.value = password.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -312,33 +306,20 @@ kpxcPassword.copyPasswordToClipboard = function() {
|
|||
return false;
|
||||
};
|
||||
|
||||
kpxcPassword.callbackGeneratedPassword = function(entries) {
|
||||
const callbackGeneratedPassword = function(entries) {
|
||||
if (entries && entries.length >= 1) {
|
||||
const errorMessage = $('#kpxc-pwgen-error');
|
||||
if (errorMessage) {
|
||||
kpxcPassword.enableButtons();
|
||||
|
||||
$('.kpxc-pwgen-checkbox').parentElement.style.display = 'block';
|
||||
$('.kpxc-pwgen-bits').style.display = 'block';
|
||||
enableButtons();
|
||||
|
||||
const input = $('.kpxc-pwgen-input');
|
||||
input.style.display = 'block';
|
||||
errorMessage.remove();
|
||||
}
|
||||
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-fill');
|
||||
kpxcPassword.whiteButton('#kpxc-pwgen-btn-copy');
|
||||
$('.kpxc-pwgen-input').value = entries[0].password;
|
||||
if (entries[0].entropy) {
|
||||
$('.kpxc-pwgen-bits').textContent = tr('passwordGeneratorBits', (Number.isNaN(entries[0].entropy) ? '???' : String(entries[0].entropy.toFixed(2))));
|
||||
} else {
|
||||
$('.kpxc-pwgen-bits').textContent = tr('passwordGeneratorBits', (isNaN(entries[0].login) ? '???' : entries[0].login));
|
||||
}
|
||||
} else {
|
||||
if (document.querySelectorAll('div#kpxc-pwgen-error').length === 0) {
|
||||
$('.kpxc-pwgen-checkbox').parentElement.style.display = 'none';
|
||||
$('.kpxc-pwgen-bits').style.display = 'none';
|
||||
|
||||
const input = $('.kpxc-pwgen-input');
|
||||
input.style.display = 'none';
|
||||
|
||||
|
|
@ -347,34 +328,18 @@ kpxcPassword.callbackGeneratedPassword = function(entries) {
|
|||
errorMessage.style.whiteSpace = 'pre';
|
||||
input.parentElement.append(errorMessage);
|
||||
|
||||
kpxcPassword.disableButtons();
|
||||
disableButtons();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
kpxcPassword.onRequestPassword = function() {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'generate_password'
|
||||
}).then(kpxcPassword.callbackGeneratedPassword);
|
||||
};
|
||||
|
||||
kpxcPassword.greenButton = function(button) {
|
||||
$(button).classList.remove('kpxc-white-button');
|
||||
$(button).classList.add('kpxc-green-button');
|
||||
};
|
||||
|
||||
kpxcPassword.whiteButton = function(button) {
|
||||
$(button).classList.remove('kpxc-green-button');
|
||||
$(button).classList.add('kpxc-white-button');
|
||||
};
|
||||
|
||||
kpxcPassword.enableButtons = function() {
|
||||
const enableButtons = function() {
|
||||
$('#kpxc-pwgen-btn-generate').textContent = tr('passwordGeneratorGenerate');
|
||||
$('#kpxc-pwgen-btn-copy').style.display = 'inline-block';
|
||||
$('#kpxc-pwgen-btn-fill').style.display = 'inline-block';
|
||||
};
|
||||
|
||||
kpxcPassword.disableButtons = function() {
|
||||
const disableButtons = function() {
|
||||
$('#kpxc-pwgen-btn-generate').textContent = tr('passwordGeneratorTryAgain');
|
||||
$('#kpxc-pwgen-btn-copy').style.display = 'none';
|
||||
$('#kpxc-pwgen-btn-fill').style.display = 'none';
|
||||
|
|
@ -382,28 +347,10 @@ kpxcPassword.disableButtons = function() {
|
|||
|
||||
// Handle icon position on window resize
|
||||
window.addEventListener('resize', function(e) {
|
||||
if (kpxcPassword.inputField && kpxcPassword.icon) {
|
||||
kpxcPassword.setIconPosition(kpxcPassword.icon, kpxcPassword.inputField);
|
||||
}
|
||||
kpxcUI.updateIconPosition(kpxcPassword);
|
||||
});
|
||||
|
||||
// Handle icon position on scroll
|
||||
window.addEventListener('scroll', function(e) {
|
||||
if (kpxcPassword.inputField && kpxcPassword.icon) {
|
||||
kpxcPassword.setIconPosition(kpxcPassword.icon, kpxcPassword.inputField);
|
||||
}
|
||||
});
|
||||
|
||||
// Closes the dialog when clicked outside of it)
|
||||
document.addEventListener('click', function(e) {
|
||||
if (kpxcPassword.dialog && kpxcPassword.dialog.style.display === 'block') {
|
||||
const dialogEndX = kpxcPassword.dialog.offsetLeft + kpxcPassword.dialog.offsetWidth;
|
||||
const dialogEndY = kpxcPassword.dialog.offsetTop + kpxcPassword.dialog.offsetHeight;
|
||||
|
||||
if ((e.clientX < kpxcPassword.dialog.offsetLeft || e.clientX > dialogEndX) ||
|
||||
(e.clientY < kpxcPassword.dialog.offsetTop || e.clientY > dialogEndY) &&
|
||||
!e.target.classList.contains('kpxc-pwgen-icon')) {
|
||||
kpxcPassword.openDialog();
|
||||
}
|
||||
}
|
||||
kpxcUI.updateIconPosition(kpxcPassword);
|
||||
});
|
||||
|
|
|
|||
35
keepassxc-browser/content/sites.js
Normal file
35
keepassxc-browser/content/sites.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
// List of sites that need special handling. 'rep' is the replacement URL.
|
||||
const siteList = [
|
||||
{
|
||||
url: 'accounts.google.com',
|
||||
rep: 'https://accounts.google.com/*'
|
||||
}
|
||||
];
|
||||
|
||||
var kpxcSites = {};
|
||||
|
||||
// Returns a predefined URL for certain sites to ensure compatibility with Site Preferences
|
||||
kpxcSites.definedURL = function(url) {
|
||||
for (const site of siteList) {
|
||||
if (url.includes(site.url)) {
|
||||
return site.rep;
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
// UNUSED: Adds all common sites with multi-page login to Site Preferences
|
||||
kpxcSites.addAllCommonSites = function() {
|
||||
kpxc.initializeSitePreferences();
|
||||
|
||||
for (const site of siteList) {
|
||||
kpxc.settings['sitePreferences'].push({
|
||||
url: site.rep,
|
||||
ignore: IGNORE_NOTHING,
|
||||
usernameOnly: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -5,6 +5,11 @@ var $ = function(elem) {
|
|||
return document.querySelector(elem);
|
||||
};
|
||||
|
||||
// Returns a string with 'px' for CSS styles
|
||||
var Pixels = function(value) {
|
||||
return String(value) + 'px';
|
||||
};
|
||||
|
||||
var kpxcUI = {};
|
||||
|
||||
// Wrapper for creating elements
|
||||
|
|
@ -31,6 +36,83 @@ kpxcUI.createElement = function(type, classes, attributes, textContent) {
|
|||
return element;
|
||||
};
|
||||
|
||||
kpxcUI.updateIconPosition = function(iconClass) {
|
||||
if (iconClass.inputField && iconClass.icon) {
|
||||
kpxcUI.setIconPosition(iconClass.icon, iconClass.inputField);
|
||||
}
|
||||
};
|
||||
|
||||
kpxcUI.setIconPosition = function(icon, field) {
|
||||
const rect = field.getBoundingClientRect();
|
||||
const offset = Number(icon.getAttribute('offset'));
|
||||
const size = Number(icon.getAttribute('size'));
|
||||
|
||||
icon.style.top = Pixels((rect.top + document.scrollingElement.scrollTop) + offset + 1);
|
||||
icon.style.left = Pixels((rect.left + document.scrollingElement.scrollLeft) + field.offsetWidth - size - offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detects if the input field appears or disappears -> show/hide the icon
|
||||
* - boundingClientRect with slightly (< -10) negative values -> hidden
|
||||
* - intersectionRatio === 0 -> hidden
|
||||
* - isIntersecting === false -> hidden
|
||||
* - intersectionRatio > 0 -> shown
|
||||
* - isIntersecting === true -> shown
|
||||
*/
|
||||
kpxcUI.updateFromIntersectionObserver = function(iconClass, entries) {
|
||||
for (const entry of entries) {
|
||||
const rect = DOMRectToArray(entry.boundingClientRect);
|
||||
|
||||
if ((entry.intersectionRatio === 0 && !entry.isIntersecting) || (rect.some(x => x < -10))) {
|
||||
iconClass.icon.style.display = 'none';
|
||||
} else if (entry.intersectionRatio > 0 && entry.isIntersecting) {
|
||||
iconClass.icon.style.display = 'block';
|
||||
|
||||
// Wait for possible DOM animations
|
||||
setTimeout(() => {
|
||||
kpxcUI.setIconPosition(iconClass.icon, entry.target);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a self-disappearing notification banner to DOM
|
||||
* @param {string} type Notification type: (success, info, warning, error)
|
||||
* @param {string} message The message shown
|
||||
*/
|
||||
kpxcUI.createNotification = function(type, message) {
|
||||
if (!kpxc.settings.showNotifications || !type || !message) {
|
||||
return;
|
||||
}
|
||||
|
||||
const banner = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {});
|
||||
type = type.charAt(0).toUpperCase() + type.slice(1) + '!';
|
||||
|
||||
const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon');
|
||||
const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' });
|
||||
const label = kpxcUI.createElement('span', 'kpxc-label', {}, type);
|
||||
const msg = kpxcUI.createElement('span', '', {}, message);
|
||||
|
||||
banner.addEventListener('click', function() {
|
||||
document.body.removeChild(banner);
|
||||
});
|
||||
|
||||
banner.appendMultiple(icon, label, msg);
|
||||
document.body.appendChild(banner);
|
||||
|
||||
// Destroy the banner after five seconds
|
||||
setTimeout(() => {
|
||||
if ($('.kpxc-notification')) {
|
||||
document.body.removeChild(banner);
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
const DOMRectToArray = function(domRect) {
|
||||
return [ domRect.bottom, domRect.height, domRect.left, domRect.right, domRect.top, domRect.width, domRect.x, domRect.y ];
|
||||
};
|
||||
|
||||
// Enables dragging
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
if (kpxcPassword.selected === kpxcPassword.titleBar) {
|
||||
|
|
@ -38,8 +120,8 @@ document.addEventListener('mousemove', function(e) {
|
|||
const yPos = e.clientY - kpxcPassword.diffY;
|
||||
|
||||
if (kpxcPassword.selected !== null) {
|
||||
kpxcPassword.dialog.style.left = xPos + 'px';
|
||||
kpxcPassword.dialog.style.top = yPos + 'px';
|
||||
kpxcPassword.dialog.style.left = Pixels(xPos);
|
||||
kpxcPassword.dialog.style.top = Pixels(yPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,8 +130,8 @@ document.addEventListener('mousemove', function(e) {
|
|||
const yPos = e.clientY - kpxcDefine.diffY;
|
||||
|
||||
if (kpxcDefine.selected !== null) {
|
||||
kpxcDefine.dialog.style.left = xPos + 'px';
|
||||
kpxcDefine.dialog.style.top = yPos + 'px';
|
||||
kpxcDefine.dialog.style.left = Pixels(xPos);
|
||||
kpxcDefine.dialog.style.top = Pixels(yPos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -64,3 +146,7 @@ HTMLDivElement.prototype.appendMultiple = function(...args) {
|
|||
this.append(a);
|
||||
}
|
||||
};
|
||||
|
||||
Element.prototype.getLowerCaseAttribute = function(attr) {
|
||||
return this.getAttribute(attr) ? this.getAttribute(attr).toLowerCase() : undefined;
|
||||
};
|
||||
|
|
|
|||
137
keepassxc-browser/content/username-field.js
Normal file
137
keepassxc-browser/content/username-field.js
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
'use strict';
|
||||
|
||||
var kpxcUsernameField = {};
|
||||
kpxcUsernameField.icon = null;
|
||||
kpxcUsernameField.inputField = null;
|
||||
|
||||
try {
|
||||
kpxcUsernameField.observer = new IntersectionObserver((entries) => {
|
||||
kpxcUI.updateFromIntersectionObserver(kpxcUsernameField, entries);
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
kpxcUsernameField.initField = function(field, databaseClosed = true) {
|
||||
if (!field || field.getAttribute('kpxc-username-field') === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
field.setAttribute('kpxc-username-field', 'true');
|
||||
|
||||
// Observer the visibility
|
||||
if (kpxcUsernameField.observer) {
|
||||
kpxcUsernameField.observer.observe(field);
|
||||
}
|
||||
|
||||
createIcon(field, databaseClosed);
|
||||
kpxcUsernameField.inputField = field;
|
||||
};
|
||||
|
||||
kpxcUsernameField.switchIcon = function(locked) {
|
||||
const icons = $('.kpxc-username-icon');
|
||||
if (!icons || icons.length === 0) {
|
||||
return;
|
||||
}
|
||||
const icon = icons;
|
||||
|
||||
if (locked) {
|
||||
icon.classList.remove(getIconClassName());
|
||||
icon.classList.add(getIconClassName(true));
|
||||
icon.title = tr('usernameLockedFieldText');
|
||||
} else {
|
||||
icon.classList.remove(getIconClassName(true));
|
||||
icon.classList.add(getIconClassName());
|
||||
icon.title = tr('usernameFieldText');
|
||||
}
|
||||
};
|
||||
|
||||
const createIcon = function(target, databaseClosed) {
|
||||
// Remove any existing password generator icons from the input field
|
||||
if (target.getAttribute('kpxc-password-generator')) {
|
||||
kpxcPassword.removeIcon(target);
|
||||
}
|
||||
|
||||
const field = target;
|
||||
const className = getIconClassName(databaseClosed);
|
||||
const size = (field.offsetHeight > 28) ? 24 : 16;
|
||||
let offset = Math.floor((field.offsetHeight - size) / 3);
|
||||
offset = (offset < 0) ? 0 : offset;
|
||||
|
||||
const icon = kpxcUI.createElement('div', 'kpxc kpxc-username-icon ' + className,
|
||||
{
|
||||
'title': databaseClosed ? tr('usernameLockedFieldText') : tr('usernameFieldText'),
|
||||
'alt': tr('usernameFieldIcon'),
|
||||
'size': size,
|
||||
'offset': offset,
|
||||
'kpxc-pwgen-field-id': field.getAttribute('data-kpxc-id')
|
||||
});
|
||||
icon.style.zIndex = '10000000';
|
||||
icon.style.width = Pixels(size);
|
||||
icon.style.height = Pixels(size);
|
||||
|
||||
icon.addEventListener('click', async function(e) {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (!kpxcFields.isVisible(field)) {
|
||||
document.body.removeChild(icon);
|
||||
field.removeAttribute('kpxc-username-field');
|
||||
return;
|
||||
}
|
||||
|
||||
const connected = await browser.runtime.sendMessage({ action: 'is_connected' });
|
||||
if (!connected) {
|
||||
kpxcUI.createNotification('error', tr('errorNotConnected'));
|
||||
return;
|
||||
}
|
||||
|
||||
const databaseHash = await browser.runtime.sendMessage({ action: 'check_database_hash' });
|
||||
if (databaseHash === '') {
|
||||
// Triggers database unlock
|
||||
_called.manualFillRequested = ManualFill.BOTH;
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'get_database_hash',
|
||||
args: [ false, true ] // Set triggerUnlock to true
|
||||
});
|
||||
}
|
||||
|
||||
if (icon.className.includes('unlock')) {
|
||||
fillCredentials();
|
||||
}
|
||||
});
|
||||
|
||||
kpxcUI.setIconPosition(icon, field);
|
||||
kpxcUsernameField.icon = icon;
|
||||
document.body.appendChild(icon);
|
||||
};
|
||||
|
||||
const getIconClassName = function(locked = false) {
|
||||
if (locked) {
|
||||
return (isFirefox() ? 'lock-moz' : 'lock');
|
||||
}
|
||||
return (isFirefox() ? 'unlock-moz' : 'unlock');
|
||||
};
|
||||
|
||||
const fillCredentials = function() {
|
||||
const fieldId = kpxcUsernameField.inputField.getAttribute('data-kpxc-id');
|
||||
kpxcFields.prepareId(fieldId);
|
||||
|
||||
const givenType = kpxcUsernameField.inputField.type === 'password' ? 'password' : 'username';
|
||||
const combination = kpxcFields.getCombination(givenType, fieldId);
|
||||
|
||||
kpxc.fillInCredentials(combination, givenType === 'password', false);
|
||||
};
|
||||
|
||||
// Handle icon position on window resize
|
||||
window.addEventListener('resize', function(e) {
|
||||
kpxcUI.updateIconPosition(kpxcUsernameField);
|
||||
});
|
||||
|
||||
// Handle icon position on scroll
|
||||
window.addEventListener('scroll', function(e) {
|
||||
kpxcUI.updateIconPosition(kpxcUsernameField);
|
||||
});
|
||||
138
keepassxc-browser/css/banner.css
Normal file
138
keepassxc-browser/css/banner.css
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
div.kpxc-banner {
|
||||
animation-duration: 0.2s;
|
||||
animation-name: slidein;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #38383d !important;
|
||||
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2) !important;
|
||||
box-sizing: border-box !important;
|
||||
color: #000000 !important;
|
||||
font-family: sans-serif !important;
|
||||
font-size: 1em !important;
|
||||
height: auto !important;
|
||||
left: 0px !important;
|
||||
line-height: 1 !important;
|
||||
margin: 0 auto !important;
|
||||
min-height: 2em !important;
|
||||
max-height: 4em !important;
|
||||
overflow-x: hidden !important;
|
||||
padding: 4px !important;
|
||||
position: fixed !important;
|
||||
top: 0px !important;
|
||||
width: 100% !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
}
|
||||
|
||||
@keyframes slidein {
|
||||
from {
|
||||
max-height: 0em;
|
||||
}
|
||||
|
||||
to {
|
||||
max-height: 4em;
|
||||
}
|
||||
}
|
||||
|
||||
div.kpxc-banner .banner-info > span {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
div.kpxc-banner div.banner-info, div.banner-buttons {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
div.kpxc-banner .small {
|
||||
font-size: 80%;
|
||||
color: #787878;
|
||||
}
|
||||
|
||||
div.kpxc-banner .kpxc-banner-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
div.kpxc-banner .kpxc-banner-icon-moz {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.separator {
|
||||
border-left: 1px solid #ccc;
|
||||
height: 100% !important;
|
||||
margin: 10px !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner .kpxc-checkbox {
|
||||
margin: 2px !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner label {
|
||||
font-family: 'Lato', sans-serif !important;
|
||||
font-size: 12px !important;
|
||||
font-weight: normal !important;
|
||||
margin: 4px !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog {
|
||||
display: block !important;
|
||||
position: fixed !important;
|
||||
margin: 0 auto;
|
||||
width: 680px;
|
||||
max-height: 600px;
|
||||
max-width: 460px;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0 0 4px 4px;
|
||||
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2);
|
||||
padding: 8px;
|
||||
z-index: 10000000;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog > ul {
|
||||
list-style-type: disc !important;
|
||||
margin-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog > p > span.strong {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog .list-group {
|
||||
font-size: .9em !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog .list-group-item {
|
||||
border: 1px solid #ddd !important;
|
||||
color: #000 !important;
|
||||
display: block !important;
|
||||
padding: 5px 10px;
|
||||
position: relative !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog .list-group-item:first-child {
|
||||
border-top-left-radius: 4px !important;
|
||||
border-top-right-radius: 4px !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog .list-group-item:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
border-bottom-right-radius: 4px !important;
|
||||
border-bottom-left-radius: 4px !important;
|
||||
}
|
||||
|
||||
div.kpxc-banner-dialog .list-group-item:hover {
|
||||
cursor: pointer !important;;
|
||||
background-color: #eee !important;
|
||||
}
|
||||
|
|
@ -1,48 +1,55 @@
|
|||
.kpxc-button {
|
||||
background-color: #fff;
|
||||
background-image: none !important;
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: .25em !important;
|
||||
font-size: 12px !important;
|
||||
font-family: 'Lato', sans-serif !important;
|
||||
height: 28px !important;
|
||||
margin: .2em !important;
|
||||
background-color: #fff;
|
||||
border: 1px solid #888 !important;
|
||||
border-radius: 4px !important;
|
||||
padding: 1px 7px 2px !important;
|
||||
}
|
||||
|
||||
.kpxc-white-button {
|
||||
color: #333 !important;
|
||||
background-color: #fff !important;
|
||||
border-color: #ccc !important;
|
||||
background-color: #f8f9fa !important;
|
||||
border-color: #f8f9fa !important;
|
||||
color: #212529 !important;
|
||||
}
|
||||
|
||||
.kpxc-red-button {
|
||||
background-color: #dc3545 !important;
|
||||
border-color: #dc3545 !important;
|
||||
color: #fff !important;
|
||||
background-color: #d9534f !important;
|
||||
border-color: #d43f3a !important;
|
||||
}
|
||||
|
||||
.kpxc-orange-button {
|
||||
color: #fff !important;
|
||||
background-color: #f0ad4e !important;
|
||||
border-color: #eea236 !important;
|
||||
background-color: #ffc107 !important;
|
||||
border-color: #ffc107 !important;
|
||||
color: #212529 !important;
|
||||
}
|
||||
|
||||
.kpxc-blue-button {
|
||||
background-color: #007bff !important;
|
||||
border-color: #007bff !important;
|
||||
color: #fff !important;
|
||||
background-color: #337ab7 !important;
|
||||
border-color: #2e6da4 !important;
|
||||
}
|
||||
|
||||
.kpxc-green-button {
|
||||
background-color: #28a745 !important;
|
||||
border-color: #28a745 !important;
|
||||
color: #fff !important;
|
||||
background-color: #5cb85c !important;
|
||||
border-color: #4cae4c !important;
|
||||
}
|
||||
|
||||
.kpxc-button:hover {
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
cursor: pointer !important;
|
||||
filter: brightness(92%) !important;
|
||||
transition: all .15s;
|
||||
}
|
||||
|
||||
.kpxc-button:active {
|
||||
background-color: #ddd;
|
||||
}
|
||||
.kpxc-button:disabled {
|
||||
border-color: #ccc !important;
|
||||
}
|
||||
|
||||
.kpxc-button:disabled:hover {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
|
|
|||
69
keepassxc-browser/css/notification.css
Normal file
69
keepassxc-browser/css/notification.css
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
.kpxc-notification {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2);
|
||||
cursor: pointer;
|
||||
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0px auto;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
padding-right: 35px;
|
||||
position: fixed;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
width: 520px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.kpxc-notification span {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.kpxc-notification .kpxc-banner-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 10px;
|
||||
margin-right: 4px;
|
||||
overflow: hidden;
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-notification .kpxc-banner-icon-moz {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 10px;
|
||||
margin-right: 4px;
|
||||
overflow: hidden;
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-notification .kpxc-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.kpxc-notification-success {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
}
|
||||
|
||||
.kpxc-notification-warning {
|
||||
color: #856404;
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffeeba;
|
||||
}
|
||||
|
||||
.kpxc-notification-info {
|
||||
color: #004085;
|
||||
background-color: #cce5ff;
|
||||
border-color: #b8daff;
|
||||
}
|
||||
|
||||
.kpxc-notification-error {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
}
|
||||
|
|
@ -1,67 +1,41 @@
|
|||
.kpxc-pwgen-dialog {
|
||||
font-family: 'Lato', sans-serif !important;
|
||||
width: 350px;
|
||||
min-height: 80px;
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2);
|
||||
z-index: 2147483645;
|
||||
font-family: 'Lato', sans-serif !important;
|
||||
min-height: 80px;
|
||||
padding: .1em .2em .1em .2em;
|
||||
position: absolute !important;
|
||||
width: 350px;
|
||||
z-index: 10000000 !important;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-titlebar {
|
||||
font-size: 14px !important;
|
||||
background-color: #6cac4d;
|
||||
color: #fff;
|
||||
padding: .4em .2em .4em .8em;
|
||||
margin: .2em 0 .2em 0;
|
||||
position: relative;
|
||||
cursor: move;
|
||||
font-size: 12px !important;
|
||||
margin: .2em 0 .2em 0;
|
||||
padding: .4em .2em .4em .8em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-password-row {
|
||||
color: #fff;
|
||||
padding: .4em 1em;
|
||||
margin: .2em 0 .2em 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-nextfill-row {
|
||||
font-size: 12px !important;
|
||||
color: #000;
|
||||
padding: .4em 1em;
|
||||
margin: .2em 0 .2em 0;
|
||||
padding: .4em 1em;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-buttons {
|
||||
align-items: center;
|
||||
border-top: 1px solid #ccc;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
padding: .4em 1em;
|
||||
margin: .2em 0 .2em 0;
|
||||
border-top: 1px solid #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-bits {
|
||||
font-size: 12px !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
text-align: center;
|
||||
color: #000;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: .4em;
|
||||
border-radius: 4px;
|
||||
width: 30%;
|
||||
min-width: 60px;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
border-collapse: separate;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-close {
|
||||
|
|
@ -80,9 +54,14 @@
|
|||
}
|
||||
|
||||
.kpxc-pwgen-input {
|
||||
width: 100%;
|
||||
background: none !important;
|
||||
border-radius: 4px !important;
|
||||
color: #000 !important;
|
||||
font-size: 12px !important;
|
||||
font-weight: normal !important;
|
||||
height: auto !important;
|
||||
padding: 4px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-checkbox-label {
|
||||
|
|
@ -100,8 +79,8 @@
|
|||
}
|
||||
|
||||
.kpxc-pwgen-icon {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.kpxc-pwgen-icon.key {
|
||||
|
|
|
|||
24
keepassxc-browser/css/username.css
Normal file
24
keepassxc-browser/css/username.css
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.kpxc-username-icon {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.lock {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.lock-moz {
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.unlock {
|
||||
background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.kpxc-username-icon.unlock-moz {
|
||||
background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
|
@ -21,6 +21,13 @@ var showNotification = function(message) {
|
|||
});
|
||||
};
|
||||
|
||||
var AssociatedAction = {
|
||||
NOT_ASSOCIATED: 0,
|
||||
ASSOCIATED: 1,
|
||||
NEW_ASSOCIATION: 2,
|
||||
CANCELED: 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms a valid match pattern into a regular expression
|
||||
* which matches all URLs included by that pattern.
|
||||
|
|
@ -71,6 +78,8 @@ var matchPatternToRegExp = function(pattern) {
|
|||
}
|
||||
|
||||
if (path) {
|
||||
path = trimURL(path);
|
||||
|
||||
if (path === '*') {
|
||||
regex += '(/.*)?';
|
||||
} else if (path.charAt(0) !== '/') {
|
||||
|
|
@ -97,3 +106,8 @@ var slashNeededForUrl = function(pattern) {
|
|||
function tr(key, params) {
|
||||
return browser.i18n.getMessage(key, params);
|
||||
}
|
||||
|
||||
// Removes everything after '?' from URL
|
||||
var trimURL = function(url) {
|
||||
return url.indexOf('?') !== -1 ? url.split('?')[0] : url;
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 1 KiB |
208
keepassxc-browser/icons/locked.svg
Normal file
208
keepassxc-browser/icons/locked.svg
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 99.999997 100"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
sodipodi:docname="locked.svg"
|
||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1231"
|
||||
inkscape:window-height="877"
|
||||
id="namedview29"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.36"
|
||||
inkscape:cx="137.9347"
|
||||
inkscape:cy="-19.107205"
|
||||
inkscape:window-x="867"
|
||||
inkscape:window-y="158"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4257">
|
||||
<stop
|
||||
style="stop-color:#808080;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4259" />
|
||||
<stop
|
||||
style="stop-color:#4d4d4d;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4261" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4316-3-6">
|
||||
<stop
|
||||
style="stop-color:#226e23;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4173" />
|
||||
<stop
|
||||
style="stop-color:#63ab3a;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4175" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4316-3">
|
||||
<stop
|
||||
id="stop4167"
|
||||
offset="0"
|
||||
style="stop-color:#226e23;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4169"
|
||||
offset="1"
|
||||
style="stop-color:#63ab3a;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4316">
|
||||
<stop
|
||||
style="stop-color:#226e23;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4318" />
|
||||
<stop
|
||||
style="stop-color:#63ab3a;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop4320" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4153"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#b3b3b3;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4155" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4316"
|
||||
id="linearGradient4324"
|
||||
x1="50.757614"
|
||||
y1="964.83679"
|
||||
x2="50.757614"
|
||||
y2="1042.2632"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0561225,0,0,1.0561225,-2.8061215,-1008.6172)" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="43.571938"
|
||||
fy="41.189114"
|
||||
fx="-82.91127"
|
||||
cy="41.189114"
|
||||
cx="-82.91127"
|
||||
id="radialGradient5106"
|
||||
xlink:href="#linearGradient4316" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4316"
|
||||
id="linearGradient4324-3"
|
||||
x1="50.757614"
|
||||
y1="964.83679"
|
||||
x2="50.757614"
|
||||
y2="1042.2632"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0561225,0,0,1.0561225,118.96071,-1109.1994)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4316"
|
||||
id="linearGradient4324-3-6"
|
||||
x1="50.757614"
|
||||
y1="964.83679"
|
||||
x2="50.757614"
|
||||
y2="1042.2632"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0561225,0,0,1.0561225,-2.8061235,-1008.6171)" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="86.356995"
|
||||
x2="53.238865"
|
||||
y1="12.753036"
|
||||
x1="53.238865"
|
||||
id="linearGradient5199"
|
||||
xlink:href="#linearGradient4316-3-6" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4257"
|
||||
id="linearGradient4263"
|
||||
x1="50.09866"
|
||||
y1="86.831215"
|
||||
x2="49.526104"
|
||||
y2="8.6772995"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<circle
|
||||
cy="50"
|
||||
cx="50"
|
||||
id="path5201"
|
||||
style="opacity:0.87099998;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.13186812;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.78431373"
|
||||
r="45.934067" />
|
||||
<circle
|
||||
r="42.32143"
|
||||
cy="50.000023"
|
||||
cx="50"
|
||||
id="path3336"
|
||||
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4264"
|
||||
d="M 50,6.3242189 A 43.676472,43.676472 0 0 0 6.3242189,50 43.676472,43.676472 0 0 0 50,93.675781 43.676472,43.676472 0 0 0 93.675781,50 43.676472,43.676472 0 0 0 50,6.3242189 Z m 0,6.7109371 c 2.26423,0.0083 4.523397,0.224904 6.748047,0.646485 0.10716,0.48368 0.167969,0.98402 0.167969,1.5 0,3.82265 -3.097269,6.918015 -6.917969,6.916015 -3.82069,0.0022 -6.916016,-3.091382 -6.916016,-6.914062 0,-0.5181 0.06204,-1.02034 0.169922,-1.50586 C 45.476743,13.257569 47.7359,13.042035 50,13.035156 Z m -18.994141,5.298828 c -0.24053,1.21916 -0.371333,2.474142 -0.371093,3.763672 0,7.71966 4.528393,14.367959 11.064453,17.480469 l 0,35.089844 8.298828,8.298828 8.300781,-8.300781 -0.476562,-7.34961 3.914062,-3.912109 -3.914062,-3.912109 5.871093,-5.867188 -5.871093,-5.871094 0.476562,-8.177734 c 6.53802,-3.11057 11.066406,-9.758856 11.066406,-17.478516 0,-1.28696 -0.133397,-2.540982 -0.373046,-3.757812 C 80.124647,25.007387 86.946634,37.023584 86.964844,50 86.964304,70.414588 70.41459,86.964292 50,86.964844 29.58541,86.964292 13.035701,70.414589 13.035156,50 13.050706,37.021755 19.872279,25.003208 31.005859,18.333984 Z m 13.458985,25.898438 2.767578,0 0,27.666016 -2.765625,0 -0.002,-27.664063 0,-0.002 z"
|
||||
style="opacity:1;fill:#f9f9f9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3336-3-2-7"
|
||||
d="m 56.747561,13.681815 c 0.107159,0.48368 0.16838,0.98465 0.16838,1.50063 0,3.822651 -3.097351,6.917921 -6.91805,6.915921 -3.82069,0.0022 -6.91594,-3.09115 -6.91594,-6.913831 0,-0.5181 0.0626,-1.02142 0.17048,-1.50694 C 45.477221,13.25743 47.7359,13.042554 50,13.035675 c 2.26423,0.0083 4.52291,0.224559 6.74756,0.64614 z m -26.11261,8.416551 c 0,7.71966 4.528179,14.36677 11.064239,17.47928 l 0,35.091179 8.2987,8.2987 8.30082,-8.3008 -0.47566,-7.3495 3.91258,-3.9126 -3.91258,-3.9126 5.86992,-5.867799 -5.86992,-5.8699 0.47565,-8.17878 c 6.53802,-3.11057 11.06635,-9.75752 11.06635,-17.47718 0,-1.28696 -0.13288,-2.54209 -0.37253,-3.75892 11.132459,6.667543 17.953549,18.684164 17.97176,31.66058 C 86.96374,70.414615 70.414591,86.963775 50,86.964325 29.58541,86.963775 13.03626,70.414615 13.03572,50.000026 c 0.0155,-12.978245 6.83607,-24.997676 17.96965,-31.6669 -0.24053,1.21916 -0.37066,2.47571 -0.37042,3.76524 z m 13.829769,22.13472 2.76764,0 0,27.665839 -2.76554,0 -0.002,-27.663749 0,-0.002 z"
|
||||
style="opacity:0.94;fill:url(#linearGradient4263);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:#fe0101;fill-opacity:1;stroke:none;stroke-width:10.35855961;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.78431373;paint-order:fill markers stroke"
|
||||
id="rect838"
|
||||
width="30"
|
||||
height="30"
|
||||
x="69"
|
||||
y="69.152542" />
|
||||
<rect
|
||||
y="65.152542"
|
||||
x="65.099998"
|
||||
height="34"
|
||||
width="34"
|
||||
id="rect840"
|
||||
style="fill:#fe0101;fill-opacity:0.28240739;stroke:none;stroke-width:11.73970127;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.78431373;paint-order:fill markers stroke" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.78431373;paint-order:fill markers stroke"
|
||||
id="rect865"
|
||||
width="6.0801892"
|
||||
height="29.562309"
|
||||
x="115.6887"
|
||||
y="-14.881159"
|
||||
transform="matrix(0.70561918,0.70859126,-0.70561918,0.70859126,0,0)" />
|
||||
<rect
|
||||
transform="matrix(-0.70859126,0.70561918,-0.70859126,-0.70561918,0,0)"
|
||||
y="-133.51057"
|
||||
x="-2.6410608"
|
||||
height="29.562309"
|
||||
width="6.0801892"
|
||||
id="rect867"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.78431373;paint-order:fill markers stroke" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
|
|
@ -54,18 +54,24 @@
|
|||
],
|
||||
"js": [
|
||||
"browser-polyfill.min.js",
|
||||
"content/banner.js",
|
||||
"content/autocomplete.js",
|
||||
"content/define.js",
|
||||
"content/keepassxc-browser.js",
|
||||
"content/pwgen.js",
|
||||
"content/sites.js",
|
||||
"content/ui.js",
|
||||
"content/username-field.js",
|
||||
"global.js"
|
||||
],
|
||||
"css": [
|
||||
"css/autocomplete.css",
|
||||
"css/banner.css",
|
||||
"css/button.css",
|
||||
"css/define.css",
|
||||
"css/pwgen.css"
|
||||
"css/notification.css",
|
||||
"css/pwgen.css",
|
||||
"css/username.css"
|
||||
],
|
||||
"run_at": "document_idle",
|
||||
"all_frames": true
|
||||
|
|
@ -102,7 +108,9 @@
|
|||
}
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
"icons/key.svg"
|
||||
"icons/keepassxc.svg",
|
||||
"icons/key.svg",
|
||||
"icons/locked.svg"
|
||||
],
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
|
|
|
|||
|
|
@ -38,56 +38,6 @@
|
|||
<span data-i18n="contextMenuShowPasswordGenerator"></span>: <span id="show_password_generator-shortcut">error</span>
|
||||
</p>
|
||||
<p id="chrome-only"><button class="btn btn-sm btn-primary" id="configureCommands" type="button" data-i18n="[title]openNewTab"><span class="glyphicon glyphicon-cog"></span><span data-i18n="optionsConfigureShortcuts"></span></button></p>
|
||||
<p>
|
||||
<div class="form-group">
|
||||
<label for="blinkTimeout" data-i18n="optionsLabelBlinkTime"></label>
|
||||
<div class="control-group">
|
||||
<div class="input-append">
|
||||
<input type="number" id="blinkTimeout" placeholder="7500" value="7500" min="-1"/>
|
||||
<button class="btn btn-sm btn-primary" id="blinkTimeoutButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help-inline">
|
||||
<span data-i18n="optionsBlinkTimeHelpText"></span>
|
||||
<br />
|
||||
<span data-i18n="optionsDefault" i18n-placeholder="7500"></span>, <span data-i18n="optionsInfinite" i18n-placeholder="-1"></span>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
<div class="form-group">
|
||||
<label for="blinkMinTimeout" data-i18n="optionsLabelRedirectOffset"></label>
|
||||
<div class="control-group">
|
||||
<div class="input-append">
|
||||
<input type="number" id="blinkMinTimeout" placeholder="2000" value="2000" min="-1"/>
|
||||
<button class="btn btn-sm btn-primary" id="blinkMinTimeoutButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help-inline">
|
||||
<span data-i18n="optionsRedirectOffsetHelpText"></span>
|
||||
<br />
|
||||
<span data-i18n="optionsRedirectOffsetHelpTextSecond"></span>
|
||||
<br />
|
||||
<span data-i18n="optionsDefault" i18n-placeholder="-1"></span>, <span data-i18n="optionsRecommended" i18n-placeholder="2000"></span>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
<div class="form-group">
|
||||
<label for="allowedRedirect" data-i18n="optionsLabelRedirectAllowance"></label>
|
||||
<div class="control-group">
|
||||
<div class="input-append">
|
||||
<input type="number" id="allowedRedirect" placeholder="1" value="1" min="1"/>
|
||||
<button class="btn btn-sm btn-primary" id="allowedRedirectButton" type="button"><span class="glyphicon glyphicon-floppy-disk"></span> <span data-i18n="optionsButtonSave"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help-inline">
|
||||
<span data-i18n="optionsRedirectAllowanceHelpText"></span>
|
||||
<br />
|
||||
<span data-i18n="optionsDefault" i18n-placeholder="1"></span>
|
||||
</span>
|
||||
</div>
|
||||
</p>
|
||||
<hr />
|
||||
<p>
|
||||
<div class="form-group">
|
||||
|
|
@ -116,7 +66,7 @@
|
|||
<p>
|
||||
<div class="checkbox">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="usePasswordGenerator" value="true"/><span data-i18n="optionsCheckboxUsePasswordGenerator"></span>
|
||||
<input type="checkbox" name="usePasswordGeneratorIcons" value="true"/><span data-i18n="optionsCheckboxUsePasswordGenerator"></span>
|
||||
</label>
|
||||
<span class="help-block">
|
||||
<span data-i18n="optionsUsePasswordGeneratorHelpText"></span>
|
||||
|
|
|
|||
|
|
@ -4,28 +4,22 @@ if (jQuery) {
|
|||
var $ = jQuery.noConflict(true);
|
||||
}
|
||||
|
||||
const defaultSettings = {
|
||||
blinkTimeout: 7500,
|
||||
redirectOffset: -1,
|
||||
redirectAllowance: 1
|
||||
};
|
||||
|
||||
$(function() {
|
||||
browser.runtime.sendMessage({ action: 'load_settings' }).then((settings) => {
|
||||
$(async function() {
|
||||
try {
|
||||
const settings = await browser.runtime.sendMessage({ action: 'load_settings' });
|
||||
options.settings = settings;
|
||||
browser.runtime.sendMessage({ action: 'load_keyring' }).then((keyRing) => {
|
||||
options.keyRing = keyRing;
|
||||
options.initMenu();
|
||||
options.initGeneralSettings();
|
||||
options.initConnectedDatabases();
|
||||
options.initCustomCredentialFields();
|
||||
options.initSitePreferences();
|
||||
options.initAbout();
|
||||
});
|
||||
});
|
||||
|
||||
// Set options page language to HTML element from locale
|
||||
$('html').attr('lang', browser.i18n.getUILanguage());
|
||||
const keyRing = await browser.runtime.sendMessage({ action: 'load_keyring' });
|
||||
options.keyRing = keyRing;
|
||||
options.initMenu();
|
||||
options.initGeneralSettings();
|
||||
options.initConnectedDatabases();
|
||||
options.initCustomCredentialFields();
|
||||
options.initSitePreferences();
|
||||
options.initAbout();
|
||||
} catch (err) {
|
||||
console.log('Error loading options page: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
var options = options || {};
|
||||
|
|
@ -54,7 +48,7 @@ options.saveSettingsPromise = function() {
|
|||
});
|
||||
};
|
||||
|
||||
options.saveSetting = function(name) {
|
||||
options.saveSetting = async function(name) {
|
||||
const id = '#' + name;
|
||||
$(id).closest('.control-group').removeClass('error').addClass('success');
|
||||
setTimeout(() => {
|
||||
|
|
@ -62,21 +56,21 @@ options.saveSetting = function(name) {
|
|||
}, 2500);
|
||||
|
||||
browser.storage.local.set({ 'settings': options.settings });
|
||||
browser.runtime.sendMessage({
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'load_settings'
|
||||
});
|
||||
};
|
||||
|
||||
options.saveSettings = function() {
|
||||
options.saveSettings = async function() {
|
||||
browser.storage.local.set({ 'settings': options.settings });
|
||||
browser.runtime.sendMessage({
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'load_settings'
|
||||
});
|
||||
};
|
||||
|
||||
options.saveKeyRing = function() {
|
||||
options.saveKeyRing = async function() {
|
||||
browser.storage.local.set({ 'keyRing': options.keyRing });
|
||||
browser.runtime.sendMessage({
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'load_keyring'
|
||||
});
|
||||
};
|
||||
|
|
@ -123,7 +117,7 @@ options.initGeneralSettings = function() {
|
|||
});
|
||||
|
||||
$('#tab-general-settings input[type=radio]').change(function() {
|
||||
options.settings[$(this).attr('name')] = $(this).val();
|
||||
options.settings[$(this).attr('name')] = Number($(this).val());
|
||||
options.saveSettings();
|
||||
});
|
||||
|
||||
|
|
@ -139,13 +133,9 @@ options.initGeneralSettings = function() {
|
|||
}).then(options.showKeePassXCVersions);
|
||||
});
|
||||
|
||||
$('#blinkTimeout').val(options.settings['blinkTimeout']);
|
||||
$('#blinkMinTimeout').val(options.settings['blinkMinTimeout']);
|
||||
$('#allowedRedirect').val(options.settings['allowedRedirect']);
|
||||
|
||||
browser.commands.getAll().then(function(commands) {
|
||||
commands.forEach(function(command) {
|
||||
var shortcut = document.getElementById(`${command.name}-shortcut`);
|
||||
let shortcut = document.getElementById(`${command.name}-shortcut`);
|
||||
if (!shortcut) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -159,35 +149,6 @@ options.initGeneralSettings = function() {
|
|||
});
|
||||
});
|
||||
|
||||
$('#blinkTimeoutButton').click(function() {
|
||||
const input = document.querySelector('#blinkTimeout');
|
||||
if (!input.validity.valid) {
|
||||
options.createWarning(input, tr('optionsErrorInvalidValue'));
|
||||
}
|
||||
|
||||
const blinkTimeout = input.value;
|
||||
const blinkTimeoutval = blinkTimeout !== '' ? Number(blinkTimeout) : defaultSettings.blinkTimeout;
|
||||
|
||||
options.settings['blinkTimeout'] = blinkTimeoutval;
|
||||
options.saveSetting('blinkTimeout');
|
||||
});
|
||||
|
||||
$('#blinkMinTimeoutButton').click(function() {
|
||||
const blinkMinTimeout = $.trim($('#blinkMinTimeout').val());
|
||||
const blinkMinTimeoutval = blinkMinTimeout !== '' ? Number(blinkMinTimeout) : defaultSettings.redirectOffset;
|
||||
|
||||
options.settings['blinkMinTimeout'] = blinkMinTimeoutval;
|
||||
options.saveSetting('blinkMinTimeout');
|
||||
});
|
||||
|
||||
$('#allowedRedirectButton').click(function() {
|
||||
const allowedRedirect = $.trim($('#allowedRedirect').val());
|
||||
const allowedRedirectval = allowedRedirect !== '' ? Number(allowedRedirect) : defaultSettings.redirectAllowance;
|
||||
|
||||
options.settings['allowedRedirect'] = allowedRedirectval;
|
||||
options.saveSetting('allowedRedirect');
|
||||
});
|
||||
|
||||
$('#defaultGroupButton').click(function() {
|
||||
const value = $('#defaultGroup').val();
|
||||
if (value.length > 0) {
|
||||
|
|
@ -253,7 +214,8 @@ options.initConnectedDatabases = function() {
|
|||
|
||||
const trClone = $('#tab-connected-databases table tr.clone:first').clone(true);
|
||||
trClone.removeClass('clone');
|
||||
for (const hash in options.keyRing) {
|
||||
|
||||
const addHashToTable = function(hash) {
|
||||
const tr = trClone.clone(true);
|
||||
tr.data('hash', hash);
|
||||
tr.attr('id', 'tr-cd-' + hash);
|
||||
|
|
@ -269,16 +231,28 @@ options.initConnectedDatabases = function() {
|
|||
$('#tab-connected-databases table tbody:first').append(tr);
|
||||
}
|
||||
|
||||
const hashList = options.keyRing;
|
||||
for (const hash in hashList) {
|
||||
addHashToTable(hash);
|
||||
}
|
||||
|
||||
if ($('#tab-connected-databases table tbody:first tr').length > 2) {
|
||||
$('#tab-connected-databases table tbody:first tr.empty:first').hide();
|
||||
} else {
|
||||
$('#tab-connected-databases table tbody:first tr.empty:first').show();
|
||||
}
|
||||
|
||||
$('#connect-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'associate'
|
||||
});
|
||||
$('#connect-button').click(async function() {
|
||||
const result = await browser.runtime.sendMessage({ action: 'associate' });
|
||||
|
||||
if (result === AssociatedAction.NEW_ASSOCIATION) {
|
||||
// Update the connection list with the added hash
|
||||
options.keyRing = await browser.runtime.sendMessage({ action: 'load_keyring' });
|
||||
for (const hash in hashList) {
|
||||
const newHash = Object.keys(options.keyRing).find(h => h !== hash);
|
||||
addHashToTable(newHash);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -485,8 +459,8 @@ options.createWarning = function(elem, text) {
|
|||
const banner = document.createElement('div');
|
||||
banner.classList.add('alert', 'alert-dismissible', 'alert-danger', 'fade', 'in');
|
||||
banner.style.position = 'absolute';
|
||||
banner.style.left = String(elem.offsetLeft) + 'px';
|
||||
banner.style.top = String(elem.offsetTop + elem.offsetHeight) + 'px';
|
||||
banner.style.left = Pixels(elem.offsetLeft);
|
||||
banner.style.top = Pixels(elem.offsetTop + elem.offsetHeight);
|
||||
banner.style.padding = '0px';
|
||||
banner.style.width = '300px';
|
||||
banner.textContent = text;
|
||||
|
|
|
|||
|
|
@ -69,25 +69,10 @@ body {
|
|||
height: 30px;
|
||||
}
|
||||
#login-filter {
|
||||
outline:none;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 5px;
|
||||
padding: 2px 10px;
|
||||
width: 100%;
|
||||
}
|
||||
.credentials .groups {
|
||||
display: none;
|
||||
border-top: 1px solid #333;
|
||||
}
|
||||
.connected-database {
|
||||
display: none;
|
||||
font-size: 85%;
|
||||
color: #787878;
|
||||
}
|
||||
.credentials .username-new {
|
||||
display: none;
|
||||
}
|
||||
.credentials .username-exists {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,14 @@
|
|||
<button id="reopen-database-button" class="btn btn-sm btn-primary"><span class="glyphicon glyphicon-lock"></span> <span data-i18n="popupReopenButton"/></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="username-field-detected" style="display: none">
|
||||
<hr />
|
||||
<p data-i18n="popupUsernameFieldDetected"></p>
|
||||
<div style="text-align: right">
|
||||
<button id="username-only-button" class="btn btn-sm btn-primary"><span class="glyphicon glyphicon-plus"></span> <span data-i18n="popupUsernameButton"/></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -30,57 +30,80 @@ function statusResponse(r) {
|
|||
$('#configured-and-associated').show();
|
||||
$('#associated-identifier').html(r.identifier);
|
||||
$('#lock-database-button').show();
|
||||
|
||||
if (r.usernameFieldDetected) {
|
||||
$('#username-field-detected').show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$('#connect-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
const sendMessageToTab = async function(message) {
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (tabs.length === 0) {
|
||||
return false; // Only the background devtools or a popup are opened
|
||||
}
|
||||
|
||||
await browser.tabs.sendMessage(tabs[0].id, {
|
||||
action: message
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$(async () => {
|
||||
$('#connect-button').click(async () => {
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'associate'
|
||||
});
|
||||
close();
|
||||
});
|
||||
|
||||
$('#reconnect-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
$('#reconnect-button').click(async () => {
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'associate'
|
||||
});
|
||||
close();
|
||||
});
|
||||
|
||||
$('#reload-status-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
$('#reload-status-button').click(async () => {
|
||||
statusResponse(await browser.runtime.sendMessage({
|
||||
action: 'reconnect'
|
||||
}).then(statusResponse);
|
||||
}));
|
||||
});
|
||||
|
||||
$('#reopen-database-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
$('#reopen-database-button').click(async () => {
|
||||
statusResponse(await browser.runtime.sendMessage({
|
||||
action: 'get_status',
|
||||
args: [ false, true ] // Set forcePopup to true
|
||||
}).then(statusResponse);
|
||||
}));
|
||||
});
|
||||
|
||||
$('#redetect-fields-button').click(function() {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then(function(tabs) {
|
||||
if (tabs.length === 0) {
|
||||
return; // For example: only the background devtools or a popup are opened
|
||||
}
|
||||
const tab = tabs[0];
|
||||
$('#redetect-fields-button').click(async () => {
|
||||
const res = await sendMessageToTab('redetect_fields');
|
||||
if (!res) {
|
||||
return;
|
||||
}
|
||||
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'redetect_fields'
|
||||
});
|
||||
});
|
||||
statusResponse(await browser.runtime.sendMessage({
|
||||
action: 'get_status'
|
||||
}));
|
||||
});
|
||||
|
||||
$('#lock-database-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
$('#lock-database-button').click(async () => {
|
||||
statusResponse(await browser.runtime.sendMessage({
|
||||
action: 'lock-database'
|
||||
}).then(statusResponse);
|
||||
}));
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
$('#username-only-button').click(async () => {
|
||||
await sendMessageToTab('add_username_only_option');
|
||||
await sendMessageToTab('redetect_fields');
|
||||
$('#username-field-detected').hide();
|
||||
});
|
||||
|
||||
statusResponse(await browser.runtime.sendMessage({
|
||||
action: 'get_status'
|
||||
}).then(statusResponse);
|
||||
}).catch((err) => {
|
||||
console.log('Error: Could not get status: ' + err);
|
||||
}));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,31 +8,28 @@ function updateAvailableResponse(available) {
|
|||
}
|
||||
}
|
||||
|
||||
function initSettings() {
|
||||
$('#settings #btn-options').click(function() {
|
||||
async function initSettings() {
|
||||
$('#settings #btn-options').click(() => {
|
||||
browser.runtime.openOptionsPage().then(close());
|
||||
});
|
||||
|
||||
$('#settings #btn-choose-credential-fields').click(function() {
|
||||
browser.windows.getCurrent().then((win) => {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
|
||||
const tab = tabs[0];
|
||||
browser.runtime.getBackgroundPage().then((global) => {
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'choose_credential_fields'
|
||||
});
|
||||
close();
|
||||
});
|
||||
});
|
||||
$('#settings #btn-choose-credential-fields').click(async () => {
|
||||
await browser.windows.getCurrent();
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
const tab = tabs[0];
|
||||
await browser.runtime.getBackgroundPage();
|
||||
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'choose_credential_fields'
|
||||
});
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$(function() {
|
||||
initSettings();
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
$(async () => {
|
||||
await initSettings();
|
||||
updateAvailableResponse(await browser.runtime.sendMessage({
|
||||
action: 'update_available_keepassxc'
|
||||
}).then(updateAvailableResponse);
|
||||
}));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,38 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
const getLoginData = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
browser.runtime.getBackgroundPage().then((global) => {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
|
||||
resolve(global.page.tabs[tabs[0].id].loginList);
|
||||
});
|
||||
});
|
||||
});
|
||||
const getLoginData = async () => {
|
||||
const global = await browser.runtime.getBackgroundPage();
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
return global.page.tabs[tabs[0].id].loginList;
|
||||
};
|
||||
|
||||
$(function() {
|
||||
getLoginData().then((data) => {
|
||||
const ll = document.getElementById('login-list');
|
||||
for (let i = 0; i < data.logins.length; ++i) {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('class', 'list-group-item');
|
||||
a.textContent = data.logins[i].login + ' (' + data.logins[i].name + ')';
|
||||
$(a).data('creds', data.logins[i]);
|
||||
$(a).click(function() {
|
||||
if (data.resolve) {
|
||||
const creds = $(this).data('creds');
|
||||
data.resolve({
|
||||
authCredentials: {
|
||||
username: creds.login,
|
||||
password: creds.password
|
||||
}
|
||||
});
|
||||
}
|
||||
close();
|
||||
});
|
||||
ll.appendChild(a);
|
||||
}
|
||||
});
|
||||
$(async () => {
|
||||
const data = await getLoginData();
|
||||
const ll = document.getElementById('login-list');
|
||||
for (let i = 0; i < data.logins.length; ++i) {
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('class', 'list-group-item');
|
||||
a.textContent = data.logins[i].login + ' (' + data.logins[i].name + ')';
|
||||
$(a).data('creds', data.logins[i]);
|
||||
$(a).click(function() {
|
||||
if (data.resolve) {
|
||||
const creds = $(this).data('creds');
|
||||
data.resolve({
|
||||
authCredentials: {
|
||||
username: creds.login,
|
||||
password: creds.password
|
||||
}
|
||||
});
|
||||
}
|
||||
close();
|
||||
});
|
||||
ll.appendChild(a);
|
||||
}
|
||||
|
||||
$('#lock-database-button').click(function() {
|
||||
browser.runtime.sendMessage({
|
||||
|
|
@ -40,18 +35,17 @@ $(function() {
|
|||
}).then(statusResponse);
|
||||
});
|
||||
|
||||
$('#btn-dismiss').click(function() {
|
||||
getLoginData().then((data) => {
|
||||
// Using reject won't work with every browser. So return empty credentials instead.
|
||||
if (data.resolve) {
|
||||
data.resolve({
|
||||
authCredentials: {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
close();
|
||||
});
|
||||
$('#btn-dismiss').click(async () => {
|
||||
const loginData = await getLoginData();
|
||||
// Using reject won't work with every browser. So return empty credentials instead.
|
||||
if (loginData.resolve) {
|
||||
loginData.resolve({
|
||||
authCredentials: {
|
||||
username: '',
|
||||
password: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,51 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
$(function() {
|
||||
browser.runtime.getBackgroundPage().then((global) => {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
|
||||
if (tabs.length === 0) {
|
||||
return; // For example: only the background devtools or a popup are opened
|
||||
}
|
||||
const tab = tabs[0];
|
||||
$(async () => {
|
||||
const global = await browser.runtime.getBackgroundPage();
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (tabs.length === 0) {
|
||||
return; // For example: only the background devtools or a popup are opened
|
||||
}
|
||||
const tab = tabs[0];
|
||||
|
||||
const logins = global.page.tabs[tab.id].loginList;
|
||||
const ll = document.getElementById('login-list');
|
||||
for (let i = 0; i < logins.length; i++) {
|
||||
const a = document.createElement('a');
|
||||
a.textContent = logins[i];
|
||||
a.setAttribute('class', 'list-group-item');
|
||||
a.setAttribute('id', '' + i);
|
||||
a.addEventListener('click', (e) => {
|
||||
const id = e.target.id;
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'fill_user_pass_with_specific_login',
|
||||
id: Number(id)
|
||||
});
|
||||
close();
|
||||
});
|
||||
ll.appendChild(a);
|
||||
const logins = global.page.tabs[tab.id].loginList;
|
||||
const ll = document.getElementById('login-list');
|
||||
for (let i = 0; i < logins.length; i++) {
|
||||
const a = document.createElement('a');
|
||||
a.textContent = logins[i];
|
||||
a.setAttribute('class', 'list-group-item');
|
||||
a.setAttribute('id', '' + i);
|
||||
a.addEventListener('click', (e) => {
|
||||
if (!e.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (logins.length > 1) {
|
||||
document.getElementById('filter-block').style = '';
|
||||
const filter = document.getElementById('login-filter');
|
||||
filter.addEventListener('keyup', (e) => {
|
||||
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();
|
||||
const id = e.target.id;
|
||||
browser.tabs.sendMessage(tab.id, {
|
||||
action: 'fill_user_pass_with_specific_login',
|
||||
id: Number(id)
|
||||
});
|
||||
close();
|
||||
});
|
||||
ll.appendChild(a);
|
||||
}
|
||||
|
||||
if (logins.length > 1) {
|
||||
document.getElementById('filter-block').style = '';
|
||||
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').click(function() {
|
||||
$('#lock-database-button').click((e) => {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'lock-database'
|
||||
});
|
||||
|
|
@ -54,7 +60,7 @@ $(function() {
|
|||
$('#database-error-message').html(tr('errorMessageDatabaseNotOpened'));
|
||||
});
|
||||
|
||||
$('#reopen-database-button').click(function() {
|
||||
$('#reopen-database-button').click((e) => {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'get_status',
|
||||
args: [ false, true ] // Set forcePopup to true
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title data-i18n="popupTitle"></title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<link rel="stylesheet" href="../options/bootstrap.min.css" />
|
||||
<script src="../browser-polyfill.min.js"></script>
|
||||
<script src="../global.js"></script>
|
||||
<script src="../jquery-3.3.1.min.js"></script>
|
||||
<script src="../options/bootstrap.min.js"></script>
|
||||
<script src="popup_functions.js"></script>
|
||||
<script src="popup_remember.js"></script>
|
||||
<script defer src="../translate.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="buttons">
|
||||
<p>
|
||||
<span data-i18n="popupRememberInfoText"></span>
|
||||
<br />
|
||||
<span class="small" data-i18n="popupURL"></span><span class="small info information-url"></span>
|
||||
<br />
|
||||
<span class="small" data-i18n="popupUsername"></span><span class="small info information-username"></span>
|
||||
</p>
|
||||
<p>
|
||||
<button id="btn-new" class="btn btn-sm btn-success"><span class="glyphicon glyphicon-pencil"></span> <span data-i18n="popupButtonNew"></span></button>
|
||||
<button id="btn-update" class="btn btn-sm btn-warning"><span class="glyphicon glyphicon-refresh"></span> <span data-i18n="popupButtonUpdate"></span></button>
|
||||
<button id="btn-dismiss" class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-remove"></span> <span data-i18n="popupButtonDismiss"></span></button>
|
||||
<button id="btn-ignore" class="btn btn-sm btn-default"><span class="glyphicon glyphicon-remove"></span> <span data-i18n="popupButtonIgnore"></span></button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="connected-database">
|
||||
<p data-i18n="popupRememberSaving" i18n-placeholder="<span></span><em></em>"></p>
|
||||
</div>
|
||||
|
||||
<div class="credentials" style="display: none;">
|
||||
<p class="username-new"><span data-i18n="popupRememberNewUsername" i18n-placeholder="<strong></strong>"></span></p>
|
||||
<p class="username-exists"><span data-i18n="popupRememberUsernameExists" i18n-placeholder="<strong></strong>"></span></p>
|
||||
<p data-i18n="popupRememberChooseCredentials"></p>
|
||||
<ul id="list" class="list-group"></ul>
|
||||
</div>
|
||||
|
||||
<div class="groups" style="display: none;">
|
||||
<p data-i18n="popupRememberChooseGroup"></p>
|
||||
<ul id="list" class="list-group"></ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,290 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const DEFAULT_BROWSER_GROUP = 'KeePassXC-Browser Passwords';
|
||||
|
||||
var _tab;
|
||||
|
||||
function _initialize(tab) {
|
||||
_tab = tab;
|
||||
|
||||
// No credentials set or credentials already cleared
|
||||
if (!_tab.credentials.username && !_tab.credentials.password) {
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
|
||||
// No existing credentials to update --> disable Update button
|
||||
if (_tab.credentials.list.length === 0) {
|
||||
$('#btn-update').attr('disabled', true).removeClass('btn-warning');
|
||||
}
|
||||
|
||||
// No username available. This might be because of trigger from context menu --> disable New button
|
||||
if (!_tab.credentials.username && _tab.credentials.password) {
|
||||
$('#btn-new').attr('disabled', true).removeClass('btn-success');
|
||||
}
|
||||
|
||||
let url = _tab.credentials.url;
|
||||
url = (url.length > 50) ? url.substring(0, 50) + '...' : url;
|
||||
$('.information-url:first').text(url);
|
||||
$('.information-username:first').text(_tab.credentials.username);
|
||||
|
||||
$('#btn-new').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('.credentials').hide();
|
||||
$('ul#list').empty();
|
||||
|
||||
// Get group listing from KeePassXC
|
||||
browser.runtime.sendMessage({
|
||||
action: 'get_database_groups'
|
||||
}).then((result) => {
|
||||
// Only the Root group and no KeePassXC-Browser passwords -> save to default
|
||||
// Or when default group is not set and defaultGroupAskAlways is disabled -> save to default
|
||||
if ((result.groups === undefined || (result.groups.length > 0 && result.groups[0].children.length === 0)) ||
|
||||
(!result.defaultGroupAlwaysAsk && (result.defaultGroup === '' || result.defaultGroup === DEFAULT_BROWSER_GROUP))) {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url ]
|
||||
}).then(_verifyResult);
|
||||
return;
|
||||
} else if (!result.defaultGroupAlwaysAsk && (result.defaultGroup !== '' || result.defaultGroup !== DEFAULT_BROWSER_GROUP)) {
|
||||
// Another group name has been specified
|
||||
const [ gname, guuid ] = getDefaultGroup(result.groups[0].children, result.defaultGroup);
|
||||
if (gname === '' && guuid === '') {
|
||||
showNotification(tr('popupRememberInfoDefaultGroupNotFound'));
|
||||
|
||||
// Create a new group
|
||||
browser.runtime.sendMessage({
|
||||
action: 'create_new_group',
|
||||
args: [ result.defaultGroup ]
|
||||
}).then((newGroup) => {
|
||||
if (newGroup.name && newGroup.uuid) {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url, newGroup.name, newGroup.uuid ]
|
||||
}).then(_verifyResult);
|
||||
} else {
|
||||
showNotification(tr('popupRememberErrorCreatingNewGroup'));
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url, gname, guuid ]
|
||||
}).then(_verifyResult);
|
||||
return;
|
||||
}
|
||||
|
||||
const addChildren = function(group, parentElement, depth) {
|
||||
++depth;
|
||||
const padding = depth * 20;
|
||||
|
||||
for (const child of group.children) {
|
||||
const a = createLink(child.name, child.uuid, child.children.length > 0);
|
||||
a.attr('id', 'child');
|
||||
a.css('cssText', 'padding-left: ' + String(padding) + 'px !important;');
|
||||
|
||||
if (parentElement.attr('id') === 'root') {
|
||||
a.attr('id', 'root-child');
|
||||
}
|
||||
|
||||
$('ul#list').append(a);
|
||||
addChildren(child, a, depth);
|
||||
}
|
||||
};
|
||||
|
||||
const createLink = function(group, groupUuid, hasChildren) {
|
||||
const a = $('<a>')
|
||||
.attr('href', '#')
|
||||
.attr('class', 'list-group-item')
|
||||
.text(group)
|
||||
.click(function(ev) {
|
||||
ev.preventDefault();
|
||||
browser.runtime.sendMessage({
|
||||
action: 'add_credentials',
|
||||
args: [ _tab.credentials.username, _tab.credentials.password, _tab.credentials.url, group, groupUuid ]
|
||||
}).then(_verifyResult);
|
||||
});
|
||||
|
||||
if (hasChildren) {
|
||||
a.text('\u25BE ' + group);
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
// Create the link list for group selection
|
||||
let depth = 0;
|
||||
for (const g of result.groups) {
|
||||
const a = createLink(g.name, g.uuid, g.children.length > 0);
|
||||
a.attr('id', 'root');
|
||||
|
||||
$('ul#list').append(a);
|
||||
addChildren(g, a, depth);
|
||||
}
|
||||
|
||||
$('.groups').show();
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn-update').click(function(e) {
|
||||
e.preventDefault();
|
||||
$('.groups').hide();
|
||||
$('ul#list').empty();
|
||||
|
||||
// Only one entry which could be updated
|
||||
if (_tab.credentials.list.length === 1) {
|
||||
// Use the current username if it's empty
|
||||
if (!_tab.credentials.username) {
|
||||
_tab.credentials.username = _tab.credentials.list[0].login;
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'update_credentials',
|
||||
args: [ _tab.credentials.list[0].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url ]
|
||||
}).then(_verifyResult);
|
||||
} else {
|
||||
$('.credentials:first .username-new:first strong:first').text(_tab.credentials.username);
|
||||
$('.credentials:first .username-exists:first strong:first').text(_tab.credentials.username);
|
||||
|
||||
if (_tab.credentials.usernameExists) {
|
||||
$('.credentials:first .username-new:first').hide();
|
||||
$('.credentials:first .username-exists:first').show();
|
||||
} else {
|
||||
$('.credentials:first .username-new:first').show();
|
||||
$('.credentials:first .username-exists:first').hide();
|
||||
}
|
||||
|
||||
for (let i = 0; i < _tab.credentials.list.length; i++) {
|
||||
const $a = $('<a>')
|
||||
.attr('href', '#')
|
||||
.attr('class', 'list-group-item')
|
||||
.text(_tab.credentials.list[i].login + ' (' + _tab.credentials.list[i].name + ')')
|
||||
.data('entryId', i)
|
||||
.click(function(ev) {
|
||||
ev.preventDefault();
|
||||
const entryId = $(this).data('entryId');
|
||||
|
||||
// Use the current username if it's empty
|
||||
if (!_tab.credentials.username) {
|
||||
_tab.credentials.username = _tab.credentials.list[entryId].login;
|
||||
}
|
||||
|
||||
// Check if the password has changed for the updated credentials
|
||||
browser.runtime.sendMessage({
|
||||
action: 'retrieve_credentials',
|
||||
args: [ url, '', false, true ]
|
||||
}).then((credentials) => {
|
||||
if (!credentials || credentials.length !== _tab.credentials.list.length) {
|
||||
_verifyResult('error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show a notification if the user tries to update credentials using the old password
|
||||
if (credentials[entryId].password === _tab.credentials.password) {
|
||||
showNotification(tr('popupRememberErrorPasswordNotChanged'));
|
||||
_close();
|
||||
return;
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'update_credentials',
|
||||
args: [ _tab.credentials.list[entryId].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url ]
|
||||
}).then(_verifyResult);
|
||||
});
|
||||
});
|
||||
|
||||
if (_tab.credentials.usernameExists && _tab.credentials.username === _tab.credentials.list[i].login) {
|
||||
$a.css('font-weight', 'bold');
|
||||
}
|
||||
|
||||
$('ul#list').append($a);
|
||||
}
|
||||
|
||||
$('.credentials').show();
|
||||
}
|
||||
});
|
||||
|
||||
$('#btn-dismiss').click(function(e) {
|
||||
e.preventDefault();
|
||||
_close();
|
||||
});
|
||||
|
||||
$('#btn-ignore').click(function(e) {
|
||||
browser.windows.getCurrent().then((win) => {
|
||||
browser.tabs.query({ 'active': true, 'currentWindow': true }).then((tabs) => {
|
||||
const currentTab = tabs[0];
|
||||
browser.runtime.getBackgroundPage().then((global) => {
|
||||
browser.tabs.sendMessage(currentTab.id, {
|
||||
action: 'ignore_site',
|
||||
args: [ _tab.credentials.url ]
|
||||
});
|
||||
_close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function _connectedDatabase(db) {
|
||||
if (db.count > 1 && db.identifier) {
|
||||
$('.connected-database:first em:first').text(db.identifier);
|
||||
$('.connected-database:first').show();
|
||||
} else {
|
||||
$('.connected-database:first').hide();
|
||||
}
|
||||
}
|
||||
|
||||
function _verifyResult(code) {
|
||||
if (code === 'error') {
|
||||
showNotification(tr('popupRememberErrorCannotSaveCredentials'));
|
||||
}
|
||||
_close();
|
||||
}
|
||||
|
||||
function _close() {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'remove_credentials_from_tab_information'
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'pop_stack'
|
||||
});
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
// Traverse the groups and ensure all paths are found
|
||||
const getDefaultGroup = function(groups, defaultGroup) {
|
||||
const getGroup = function(group, splitted, depth) {
|
||||
++depth;
|
||||
for (const g of group) {
|
||||
if (g.name === splitted[depth]) {
|
||||
if (splitted.length === (depth + 1)) {
|
||||
return [ g.name, g.uuid ];
|
||||
}
|
||||
return getGroup(g.children, splitted, depth);
|
||||
}
|
||||
}
|
||||
return [ '', '' ];
|
||||
};
|
||||
|
||||
let depth = -1;
|
||||
const splitted = defaultGroup.split('/');
|
||||
return getGroup(groups, splitted, depth);
|
||||
};
|
||||
|
||||
$(function() {
|
||||
browser.runtime.sendMessage({
|
||||
action: 'stack_add',
|
||||
args: [ 'icon_remember_red_background.png', 'popup_remember.html', 10, true, 0 ]
|
||||
});
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'get_tab_information'
|
||||
}).then(_initialize);
|
||||
|
||||
browser.runtime.sendMessage({
|
||||
action: 'get_connected_database'
|
||||
}).then(_connectedDatabase);
|
||||
});
|
||||
Loading…
Reference in a new issue