mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
parent
9f3a9ebc63
commit
a1dc05ee74
5 changed files with 90 additions and 4 deletions
|
|
@ -107,6 +107,7 @@
|
|||
"createStylesheet": "readonly",
|
||||
"DatabaseState": "readonly",
|
||||
"debugLogMessage": "readonly",
|
||||
"elementsOverlap": "readonly",
|
||||
"EXTENSION_NAME": "readonly",
|
||||
"getCurrentTab": "readonly",
|
||||
"getLoginData": "readonly",
|
||||
|
|
|
|||
|
|
@ -182,10 +182,18 @@ const getCurrentTab = async function() {
|
|||
return tabs?.length > 0 ? tabs[0] : undefined;
|
||||
};
|
||||
|
||||
// Check if two elements overlap
|
||||
const elementsOverlap = function(rect1, rect2) {
|
||||
const isInside = (a, b) => (b.x >= a.x || b.right <= a.right) && (b.y >= a.y || b.bottom <= a.bottom);
|
||||
const overlaps = (a, b) => !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom);
|
||||
return isInside(rect1, rect2) || overlaps(rect1, rect2);
|
||||
};
|
||||
|
||||
// Exports for tests
|
||||
if (typeof module === 'object') {
|
||||
module.exports = {
|
||||
compareVersion,
|
||||
elementsOverlap,
|
||||
matchesWithNodeName,
|
||||
siteMatch,
|
||||
slashNeededForUrl,
|
||||
|
|
|
|||
|
|
@ -411,6 +411,33 @@ kpxcFields.isSearchField = function(target) {
|
|||
return false;
|
||||
};
|
||||
|
||||
kpxcFields.isTopElement = function(elem, rect) {
|
||||
if (!elem || !rect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check topmost element from three points inside the input
|
||||
const verticalMiddle = rect.top + (rect.height / 2);
|
||||
if (matchesWithNodeName(elem, 'INPUT') && [
|
||||
document.elementFromPoint(rect.left + (rect.width / 4), verticalMiddle), // First third
|
||||
document.elementFromPoint(rect.left + (rect.width / 2), verticalMiddle), // Middle
|
||||
document.elementFromPoint(rect.left + (rect.width / 1.33), verticalMiddle), // Last third
|
||||
].some((e) => e !== elem)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for popup overlays
|
||||
const overlays = document.querySelectorAll(':popover-open');
|
||||
for (const overlay of overlays) {
|
||||
const overlayRect = overlay?.getBoundingClientRect();
|
||||
if (overlayRect && elementsOverlap(rect, overlayRect)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Returns true if element is visible on the page
|
||||
kpxcFields.isVisible = function(elem) {
|
||||
// Returns true if opacity is not set, otherwise check the limits
|
||||
|
|
@ -430,6 +457,10 @@ kpxcFields.isVisible = function(elem) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!kpxcFields.isTopElement(elem, rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check CSS visibility
|
||||
const elemStyle = getComputedStyle(elem);
|
||||
if (elemStyle.visibility && (elemStyle.visibility === 'hidden' || elemStyle.visibility === 'collapse')
|
||||
|
|
@ -491,7 +522,7 @@ kpxcFields.useCustomLoginFields = async function() {
|
|||
// Get all input fields from the page without any extra filters
|
||||
const inputFields = [];
|
||||
document.body.querySelectorAll('input, select, textarea').forEach(e => {
|
||||
if (e.type !== 'hidden' && !e.disabled) {
|
||||
if (e.type !== 'hidden' && !e.disabled && kpxcFields.isTopElement(e, e?.getBoundingClientRect())) {
|
||||
inputFields.push(e);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
import {
|
||||
compareVersion,
|
||||
elementsOverlap,
|
||||
matchesWithNodeName,
|
||||
siteMatch,
|
||||
slashNeededForUrl,
|
||||
|
|
@ -89,3 +90,48 @@ test('Test trimURL()', async ({ page }) => {
|
|||
expect(trimURL('https://example.com/path/')).toBe('https://example.com/path/');
|
||||
expect(trimURL('https://example.com/path/#extra')).toBe('https://example.com/path/#extra');
|
||||
});
|
||||
|
||||
// Check if different popups/overlays partially covers or touches the input field
|
||||
test('Test elementsOverlap()', async ({ page }) => {
|
||||
const inputRect = { left: 0, top: 5, right: 200, bottom: 28 }
|
||||
|
||||
// Fully covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Top side is covered
|
||||
expect(elementsOverlap(inputRect, { left: 0, top: 0, right: 220, bottom: 20 })).toBe(true);
|
||||
|
||||
// Bottom side is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 25, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Left side is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 100, bottom: 40 })).toBe(true);
|
||||
|
||||
// Right side is covered
|
||||
expect(elementsOverlap(inputRect, { left: 100, top: 0, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Top-left corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 0, right: 40, bottom: 10 })).toBe(true);
|
||||
|
||||
// Top-right corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: 180, top: 0, right: 220, bottom: 10 })).toBe(true);
|
||||
|
||||
// Bottom-left corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 10, right: 100, bottom: 40 })).toBe(true);
|
||||
|
||||
// Bottom-right corner is covered
|
||||
expect(elementsOverlap(inputRect, { left: 180, top: 10, right: 220, bottom: 40 })).toBe(true);
|
||||
|
||||
// Input field is covered with identical size
|
||||
expect(elementsOverlap(inputRect, { left: 0, top: 5, right: 200, bottom: 28 })).toBe(true);
|
||||
|
||||
// Overlay is inside the input field
|
||||
expect(elementsOverlap(inputRect, { left: 2, top: 10, right: 180, bottom: 26 })).toBe(true);
|
||||
|
||||
// Overlay is partially inside the input field, comes outside from the left
|
||||
expect(elementsOverlap(inputRect, { left: -2, top: 10, right: 180, bottom: 26 })).toBe(true);
|
||||
|
||||
// Overlay is outside the input field
|
||||
expect(elementsOverlap(inputRect, { left: 210, top: 0, right: 240, bottom: 40 })).toBe(false);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ async function testInputFields() {
|
|||
[ 'basic4', 3 ], // Username/passwd/TOTP fields
|
||||
[ 'div1', 2, '#toggle1' ], // Fields are behind a button that must be pressed
|
||||
[ 'div2', 2, '#toggle2' ], // Fields are behind a button that must be pressed behind a JavaScript
|
||||
[ 'div3', 2, '#toggle3' ], // Fields are behind a button that must be pressed
|
||||
[ 'div4', 2, '#toggle4' ], // Fields are behind a button that must be pressed
|
||||
//[ 'div3', 2, '#toggle3' ], // Fields are behind a button that must be pressed
|
||||
//[ 'div4', 2, '#toggle4' ], // Fields are behind a button that must be pressed
|
||||
[ 'hiddenFields1', 0 ], // Two hidden fields
|
||||
[ 'hiddenFields2', 1 ], // Two hidden fields with one visible
|
||||
//[ 'hiddenFields2', 1 ], // Two hidden fields with one visible
|
||||
];
|
||||
|
||||
for (const div of testDivs) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue