Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc-browser.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvarjolintu <sami.vanttinen@protonmail.com>2020-06-28 11:47:29 +0300
committervarjolintu <sami.vanttinen@protonmail.com>2020-07-06 16:13:27 +0300
commit8f8dd522f8384b301d11550ec04dac6ae82a4db3 (patch)
tree37d7376516655fb3b2313c39a3b2a18b646ceb91 /keepassxc-browser
parentd518662696a7965477039f161a2bb4e10137b0e9 (diff)
Fix high CPU usage
Diffstat (limited to 'keepassxc-browser')
-rwxr-xr-xkeepassxc-browser/content/keepassxc-browser.js128
-rw-r--r--keepassxc-browser/content/pwgen.js26
-rw-r--r--keepassxc-browser/content/totp-field.js21
-rw-r--r--keepassxc-browser/content/ui.js18
-rw-r--r--keepassxc-browser/content/username-field.js19
5 files changed, 154 insertions, 58 deletions
diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js
index 91b6d8a..b6685f2 100755
--- a/keepassxc-browser/content/keepassxc-browser.js
+++ b/keepassxc-browser/content/keepassxc-browser.js
@@ -44,6 +44,11 @@ let _documentURL = document.location.href;
// These are executed in each frame
browser.runtime.onMessage.addListener(async function(req, sender) {
if ('action' in req) {
+ // Don't allow any actions if the site is ignored
+ if (kpxc.siteIgnored()) {
+ return Promise.resolve();
+ }
+
if (req.action === 'fill_user_pass_with_specific_login') {
kpxc.fillWithSpecificLogin(req.id);
} else if (req.action === 'fill_username_password') {
@@ -275,6 +280,7 @@ kpxcFields.traverseParents = function(element, predicate, resultFn = () => true,
return resultFn(f);
}
}
+
return defaultValFn();
};
@@ -318,20 +324,26 @@ kpxcFields.isSearchField = function(target) {
};
kpxcFields.isVisible = function(field) {
- // Check for parent opacity
- if (kpxcFields.traverseParents(field, f => f.style.opacity === '0')) {
+ // Check element position and size
+ const rect = field.getBoundingClientRect();
+ if (rect.x < 0
+ || rect.y < 0
+ || rect.width < 8
+ || rect.x > document.body.offsetWidth
+ || rect.y > document.body.offsetHeight
+ || rect.height < 8) {
return false;
}
// Check CSS visibility
const fieldStyle = getComputedStyle(field);
- if (fieldStyle.visibility && (fieldStyle.visibility === 'hidden' || fieldStyle.visibility === 'collapse')) {
+ if (fieldStyle.visibility && (fieldStyle.visibility === 'hidden' || fieldStyle.visibility === 'collapse')
+ || fieldStyle.opacity === '0') {
return false;
}
- // Check element position and size
- const rect = field.getBoundingClientRect();
- if (rect.x < 0 || rect.y < 0 || rect.width < 8 || rect.height < 8) {
+ // Check for parent opacity
+ if (kpxcFields.traverseParents(field, f => f.style.opacity === '0')) {
return false;
}
@@ -595,6 +607,16 @@ kpxcFields.getPasswordField = function(usernameId, checkDisabled) {
};
kpxcFields.prepareCombinations = async function(combinations) {
+ if (combinations.length === 0) {
+ return;
+ }
+
+ // Only request this once if there are combinations available
+ let passwordFilled;
+ if (combinations.length > 0) {
+ passwordFilled = await kpxc.passwordFilled();
+ }
+
for (const c of combinations) {
const pwField = _f(c.password);
// Needed for auto-complete: don't overwrite manually filled-in password field
@@ -611,7 +633,7 @@ kpxcFields.prepareCombinations = async function(combinations) {
// If no username field is found, handle the single password field as such
const usernameField = c.username ? _f(c.username) : field;
- if (kpxc.settings.showLoginFormIcon && await kpxc.passwordFilled() === false) {
+ if (kpxc.settings.showLoginFormIcon && passwordFilled === false) {
kpxcUsernameIcons.newIcon(usernameField, _databaseState);
}
@@ -683,6 +705,7 @@ kpxcObserverHelper.inputTypes = [
];
// Ignores all nodes that doesn't contain elements
+// Also ignore few Youtube-specific custom nodeNames
kpxcObserverHelper.ignoredNode = function(target) {
if (target.nodeType === Node.ATTRIBUTE_NODE
|| target.nodeType === Node.TEXT_NODE
@@ -690,13 +713,20 @@ kpxcObserverHelper.ignoredNode = function(target) {
|| target.nodeType === Node.PROCESSING_INSTRUCTION_NODE
|| target.nodeType === Node.COMMENT_NODE
|| target.nodeType === Node.DOCUMENT_TYPE_NODE
- || target.nodeType === Node.NOTATION_NODE) {
+ || target.nodeType === Node.NOTATION_NODE
+ || target.nodeName === 'HTML'
+ || target.nodeName === 'LINK'
+ || target.nodeName === 'HEAD'
+ || target.nodeName === 'VIDEO'
+ || target.nodeName.startsWith('YTMUSIC')
+ || target.nodeName.startsWith('YT-')) {
return true;
}
+
return false;
};
-kpxcObserverHelper.getInputs = function(target) {
+kpxcObserverHelper.getInputs = function(target, ignoreVisibility = false) {
// Ignores target element if it's not an element node
if (kpxcObserverHelper.ignoredNode(target)) {
return [];
@@ -705,7 +735,9 @@ kpxcObserverHelper.getInputs = function(target) {
// Filter out any input fields with type 'hidden' right away
const inputFields = [];
Array.from(target.getElementsByTagName('input')).forEach(e => {
- if (e.type !== 'hidden') {
+ if (e.type !== 'hidden'
+ && !e.disabled
+ && !kpxcObserverHelper.hasKpxcClass(e)) {
inputFields.push(e);
}
});
@@ -724,9 +756,15 @@ kpxcObserverHelper.getInputs = function(target) {
for (const field of inputFields) {
const type = field.getLowerCaseAttribute('type');
- if (kpxcObserverHelper.inputTypes.includes(type) && kpxcFields.isVisible(field)) {
- kpxcFields.setUniqueId(field);
- inputs.push(field);
+ if (ignoreVisibility) {
+ if (kpxcObserverHelper.inputTypes.includes(type)) {
+ inputs.push(field);
+ }
+ } else {
+ if (kpxcObserverHelper.inputTypes.includes(type) && kpxcFields.isVisible(field)) {
+ kpxcFields.setUniqueId(field);
+ inputs.push(field);
+ }
}
}
return inputs;
@@ -745,15 +783,27 @@ kpxcObserverHelper.getId = function(target) {
return `kpxc${target.clientTop}${target.clientLeft}${target.clientWidth}${target.clientHeight}`;
};
+kpxcObserverHelper.hasKpxcClass = function(target) {
+ if (!target.className
+ || !target.className.includes('kpxc')) {
+ return false;
+ }
+
+ return target.className.includes('kpxc');
+};
+
kpxcObserverHelper.ignoredElement = function(target) {
+ if (kpxcObserverHelper.ignoredNode(target)) {
+ return true;
+ }
+
// Ignore elements that do not have a className (including SVG)
if (typeof target.className !== 'string') {
return true;
}
// Ignore KeePassXC-Browser classes
- if (target.className && target.className !== undefined
- && target.className.includes('kpxc')) {
+ if (kpxcObserverHelper.hasKpxcClass(target)) {
return true;
}
@@ -780,7 +830,7 @@ kpxcObserverHelper.handleObserverAdd = function(target) {
if (Object.keys(kpxc.settings).length === 0) {
kpxc.init();
} else {
- kpxc.initCredentialFields(true, inputs);
+ kpxc.handleCredentialFields(inputs);
}
}
};
@@ -790,11 +840,13 @@ kpxcObserverHelper.handleObserverRemove = function(target) {
return;
}
- const inputs = kpxcObserverHelper.getInputs(target);
+ const inputs = kpxcObserverHelper.getInputs(target, true);
if (inputs.length === 0) {
return;
}
+ kpxc.deleteHiddenIcons();
+
// Remove target element id from the list
const id = kpxcObserverHelper.getId(target);
if (_observerIds.includes(id)) {
@@ -824,7 +876,7 @@ kpxc.url = null;
kpxc.submitUrl = null;
kpxc.credentials = [];
-const initcb = async function() {
+const initContentScript = async function() {
try {
const settings = await browser.runtime.sendMessage({
action: 'load_settings'
@@ -873,13 +925,13 @@ const initcb = async function() {
};
if (document.readyState === 'complete' || (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
- initcb();
+ initContentScript();
} else {
- document.addEventListener('DOMContentLoaded', initcb);
+ document.addEventListener('DOMContentLoaded', initContentScript);
}
kpxc.init = function() {
- initcb();
+ initContentScript();
};
// Detects DOM changes in the document
@@ -896,10 +948,7 @@ kpxc.initObserver = function() {
for (const mut of mutations) {
// Skip text nodes and base HTML element
- if (mut.target.nodeType === Node.TEXT_NODE
- || mut.target.nodeName === 'HTML'
- || mut.target.nodeName === 'LINK'
- || mut.target.nodeName === 'HEAD') {
+ if (kpxcObserverHelper.ignoredNode(mut.target)) {
continue;
}
@@ -911,7 +960,7 @@ kpxc.initObserver = function() {
// Check if some class is changed that holds a form or input field(s). Ignore large forms.
const formInput = mut.target.querySelector('form input');
if (mut.attributeName === 'class' && formInput !== null && formInput.form.length < 20) {
- kpxc.initCredentialFields(true);
+ kpxc.handleCredentialFields(Array.from(formInput.form.getElementsByTagName('input')));
continue;
}
@@ -1013,7 +1062,8 @@ kpxc.siteIgnored = function(condition) {
return false;
};
-kpxc.initCredentialFields = async function(forceCall, inputs) {
+// Initializes all input fields from the whole page
+kpxc.initCredentialFields = async function(forceCall) {
if (_called.initCredentialFields && !forceCall) {
return;
}
@@ -1030,11 +1080,13 @@ kpxc.initCredentialFields = async function(forceCall, inputs) {
return;
}
- // If target input fields are not defined, get inputs from the whole document
- if (inputs === undefined) {
- inputs = kpxcFields.getAllFields();
- }
+ // Get inputs from the whole document
+ const inputs = kpxcFields.getAllFields();
+ kpxc.handleCredentialFields(inputs);
+};
+// Handles the input fields from the whole page or from dynamically added content
+kpxc.handleCredentialFields = async function(inputs) {
if (inputs.length === 0) {
return;
}
@@ -1092,10 +1144,6 @@ kpxc.initPasswordGenerator = function(inputs) {
kpxc.initOTPFields = function(inputs) {
for (const i of inputs) {
- if (!kpxcFields.isVisible(i)) {
- continue;
- }
-
const id = i.getLowerCaseAttribute('id');
const name = i.getLowerCaseAttribute('name');
const autocomplete = i.getLowerCaseAttribute('autocomplete');
@@ -1247,6 +1295,10 @@ kpxc.preparePageForMultipleCredentials = function(credentials) {
// Returns the form that includes the inputField
kpxc.getForm = function(inputField) {
+ if (inputField.form) {
+ return inputField.form;
+ }
+
for (const f of document.forms) {
for (const e of f.elements) {
if (e === inputField) {
@@ -1922,6 +1974,12 @@ kpxc.switchIcons = function() {
kpxcTOTPIcons.switchIcon(_databaseState);
};
+kpxc.deleteHiddenIcons = function() {
+ kpxcUsernameIcons.deleteHiddenIcons();
+ kpxcPasswordIcons.deleteHiddenIcons();
+ kpxcTOTPIcons.deleteHiddenIcons();
+};
+
kpxc.setPasswordFilled = function(state) {
browser.runtime.sendMessage({
action: 'password_set_filled',
diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js
index d6fc1d8..2fc8267 100644
--- a/keepassxc-browser/content/pwgen.js
+++ b/keepassxc-browser/content/pwgen.js
@@ -11,6 +11,10 @@ kpxcPasswordIcons.switchIcon = function(state) {
kpxcPasswordIcons.icons.forEach(u => u.switchIcon(state));
};
+kpxcPasswordIcons.deleteHiddenIcons = function() {
+ kpxcUI.deleteHiddenIcons(kpxcPasswordIcons.icons, 'kpxc-password-field');
+};
+
class PasswordIcon extends Icon {
constructor(useIcons, field, inputs, pos, databaseState) {
@@ -18,22 +22,25 @@ class PasswordIcon extends Icon {
this.useIcons = useIcons;
this.databaseState = databaseState;
- this.initField(field, inputs, pos);
- kpxcUI.monitorIconPosition(this);
+ if (this.initField(field, inputs, pos)) {
+ kpxcUI.monitorIconPosition(this);
+ }
}
}
PasswordIcon.prototype.initField = function(field, inputs, pos) {
- if (!field || field.readOnly) {
- return;
+ if (!field
+ || field.readOnly
+ || field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH) {
+ return false;
}
- if (field.getAttribute('kpxc-password-generator')
+ if (field.getAttribute('kpxc-password-field')
|| (field.hasAttribute('kpxc-defined') && field.getAttribute('kpxc-defined') !== 'password')) {
- return;
+ return false;
}
- field.setAttribute('kpxc-password-generator', true);
+ field.setAttribute('kpxc-password-field', true);
if (this.useIcons) {
// Observer the visibility
@@ -58,6 +65,7 @@ PasswordIcon.prototype.initField = function(field, inputs, pos) {
}
field.setAttribute('kpxc-pwgen-next-field-exists', found);
+ return true;
};
PasswordIcon.prototype.createIcon = function(field) {
@@ -117,7 +125,7 @@ kpxcPasswordDialog.dialog = null;
kpxcPasswordDialog.titleBar = null;
kpxcPasswordDialog.removeIcon = function(field) {
- if (field.getAttribute('kpxc-password-generator')) {
+ if (field.getAttribute('kpxc-password-field')) {
const pwgenIcons = document.querySelectorAll('.kpxc-pwgen-icon');
for (const i of pwgenIcons) {
if (i.getAttribute('kpxc-pwgen-field-id') === field.getAttribute('data-kpxc-id')) {
@@ -233,7 +241,7 @@ kpxcPasswordDialog.trigger = function() {
kpxcPasswordDialog.showDialog = function(field, icon) {
if (!kpxcFields.isVisible(field)) {
document.body.removeChild(icon);
- field.removeAttribute('kpxc-password-generator');
+ field.removeAttribute('kpxc-password-field');
return;
}
diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js
index f715448..e09595c 100644
--- a/keepassxc-browser/content/totp-field.js
+++ b/keepassxc-browser/content/totp-field.js
@@ -1,6 +1,5 @@
'use strict';
-const MINIMUM_SIZE = 60;
const ignoreRegex = /(zip|postal).*code/i;
const ignoredTypes = [ 'email', 'password', 'username' ];
@@ -15,6 +14,10 @@ kpxcTOTPIcons.switchIcon = function(state) {
kpxcTOTPIcons.icons.forEach(u => u.switchIcon(state));
};
+kpxcTOTPIcons.deleteHiddenIcons = function() {
+ kpxcUI.deleteHiddenIcons(kpxcTOTPIcons.icons, 'kpxc-totp-field');
+};
+
class TOTPFieldIcon extends Icon {
constructor(field, databaseState = DatabaseState.DISCONNECTED, forced = false) {
@@ -23,8 +26,9 @@ class TOTPFieldIcon extends Icon {
this.inputField = null;
this.databaseState = databaseState;
- this.initField(field, forced);
- kpxcUI.monitorIconPosition(this);
+ if (this.initField(field, forced)) {
+ kpxcUI.monitorIconPosition(this);
+ }
}
}
@@ -35,20 +39,20 @@ TOTPFieldIcon.prototype.initField = function(field, forced) {
if (!forced) {
if (ignoredTypes.some(t => t === field.type)
+ || field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH
+ || field.size < 2
+ || (field.maxLength > 0 && (field.maxLength < 6 || field.maxLength > 8))
|| ignoredTypes.some(t => t === field.autocomplete)
|| field.getAttribute('kpxc-totp-field') === 'true'
|| (field.hasAttribute('kpxc-defined') && field.getAttribute('kpxc-defined') !== 'totp')
- || field.offsetWidth < MINIMUM_SIZE
- || field.size < 2
- || (field.maxLength > 0 && (field.maxLength < 6 || field.maxLength > 8))
|| field.id.match(ignoreRegex)
|| field.name.match(ignoreRegex)
|| field.readOnly) {
- return;
+ return false;
}
} else {
if (field.getAttribute('kpxc-totp-field') === 'true') {
- return;
+ return false;
}
}
@@ -61,6 +65,7 @@ TOTPFieldIcon.prototype.initField = function(field, forced) {
this.createIcon(field);
this.inputField = field;
+ return false;
};
TOTPFieldIcon.prototype.createIcon = function(field) {
diff --git a/keepassxc-browser/content/ui.js b/keepassxc-browser/content/ui.js
index 923e7e1..a7e7c3a 100644
--- a/keepassxc-browser/content/ui.js
+++ b/keepassxc-browser/content/ui.js
@@ -1,5 +1,7 @@
'use strict';
+const MINIMUM_INPUT_FIELD_WIDTH = 60;
+
// jQuery style wrapper for querySelector()
const $ = function(elem) {
return document.querySelector(elem);
@@ -33,6 +35,12 @@ class Icon {
this.icon.style.filter = 'saturate(0%)';
}
}
+
+ removeIcon(attr) {
+ this.inputField.removeAttribute(attr);
+ this.shadowRoot.removeChild(this.icon);
+ document.body.removeChild(this.shadowRoot.host);
+ }
}
const kpxcUI = {};
@@ -101,6 +109,16 @@ kpxcUI.setIconPosition = function(icon, field) {
}
};
+kpxcUI.deleteHiddenIcons = function(iconList, attr) {
+ for (const icon of iconList) {
+ if (icon.inputField && !kpxcFields.isVisible(icon.inputField)) {
+ const index = iconList.indexOf(icon);
+ icon.removeIcon(attr);
+ iconList.splice(index, 1);
+ }
+ }
+};
+
/**
* Detects if the input field appears or disappears -> show/hide the icon
* - boundingClientRect with slightly (< -10) negative values -> hidden
diff --git a/keepassxc-browser/content/username-field.js b/keepassxc-browser/content/username-field.js
index 738276d..f7964d9 100644
--- a/keepassxc-browser/content/username-field.js
+++ b/keepassxc-browser/content/username-field.js
@@ -11,6 +11,10 @@ kpxcUsernameIcons.switchIcon = function(state) {
kpxcUsernameIcons.icons.forEach(u => u.switchIcon(state));
};
+kpxcUsernameIcons.deleteHiddenIcons = function() {
+ kpxcUI.deleteHiddenIcons(kpxcUsernameIcons.icons, 'kpxc-username-field');
+};
+
class UsernameFieldIcon extends Icon {
constructor(field, databaseState = DatabaseState.DISCONNECTED) {
@@ -19,8 +23,9 @@ class UsernameFieldIcon extends Icon {
this.icon = null;
this.inputField = null;
- this.initField(field);
- kpxcUI.monitorIconPosition(this);
+ if (this.initField(field)) {
+ kpxcUI.monitorIconPosition(this);
+ }
}
switchIcon(state) {
@@ -36,12 +41,13 @@ class UsernameFieldIcon extends Icon {
UsernameFieldIcon.prototype.initField = function(field) {
if (!field
+ || field.offsetWidth < MINIMUM_INPUT_FIELD_WIDTH
+ || field.readOnly
|| field.getAttribute('kpxc-username-field') === 'true'
|| field.getAttribute('kpxc-totp-field') === 'true'
|| (field.hasAttribute('kpxc-defined') && field.getAttribute('kpxc-defined') !== 'username')
- || !kpxcFields.isVisible(field)
- || field.readOnly) {
- return;
+ || !kpxcFields.isVisible(field)) {
+ return false;
}
field.setAttribute('kpxc-username-field', 'true');
@@ -53,11 +59,12 @@ UsernameFieldIcon.prototype.initField = function(field) {
this.createIcon(field);
this.inputField = field;
+ return true;
};
UsernameFieldIcon.prototype.createIcon = function(target) {
// Remove any existing password generator icons from the input field
- if (target.getAttribute('kpxc-password-generator')) {
+ if (target.getAttribute('kpxc-password-field')) {
kpxcPasswordDialog.removeIcon(target);
}